Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TypeScript+Orvalで実現する型安全かつ堅牢でスケーラブルなマルチチャネル通...

TypeScript+Orvalで実現する型安全かつ堅牢でスケーラブルなマルチチャネル通知基盤 / TSKaigi Night talks ~after conference~

TSKaigi Night talks 〜after conference〜 での登壇資料です。
MOSHではマルチプロダクト戦略のもと、複数チームが共通で使える通知基盤を開発しています。電子メール・LINE Push・プロダクト内通知のマルチチャネルに対応しつつ、「届くこと」を当たり前に保証するために必要な 堅牢性(robustness) と スケーラビリティ をどう実現したかを話しました。

* 誤った入力への対処: OpenAPI + Orval によるスキーマ駆動開発で、Honoのroutes・Zodバリデータを自動生成し、型安全な入出力を低コストで実現
* 実行中エラーへの対処: AWS Step Functions をオーケストレーターに、分散処理・リトライ・Redriveを活用。DynamoDB(+ dynamodb-toolbox)で冪等性を担保し、副作用のない回復性を確保
* スケーラビリティ: SFnの分散処理により、高負荷時の拡張と新機能追加が容易な構成へ

TypeScriptだけで分散処理の信頼性を作り込む大変さと、そこにマネージドサービスを組み合わせるアプローチについて触れています

Avatar for doriven

doriven

June 09, 2026

Other Decks in Programming

Transcript

  1. TypeScript + Orvalで実現する堅牢でスケーラブル なマルチチャネル通知基盤 Yuki Niitsuma @doriven_tech in TSKaigi Night

    talks ~after conference~ PRESENTATION SLIDES [ ver.01 2025.02 ] © MOSH Inc. MOSH develops and operates a platform that supports independent creators in selling their services online.
  2. PRESENTATION SLIDES © MOSH Inc. MOSH株式会社 • 技術部 テクニカルプロダクトチーム 趣味

    • PCゲーム / アニメ 信念 • ゲームは1日最低1時間 @doriven_tech 新妻 佑記
  3. • 実装による不具合 • 外部サービスの障害 ◦ クラウドインフラ ◦ 通知サービス ▪ AWS

    SES, SendGrid ▪ LINE Message API • メールドメインやIPレピュテーションの低下 • etc 到達率を下げる要因 PRESENTATION SLIDES © MOSH Inc.
  4. 堅牢性とは? 計算機科学における、ロバストネス (英: robustness)、ロバスト性 、 堅牢性とは、コンピュータシステムで実行中のエラー[1][2]や、誤った入 力に対処できる能力のこと。 referenced by wikipedia

    ロバストネス(コンピュータ) 以下を対処する能力があれば堅牢性があるといえる • 実行中のエラー • 誤った入力 PRESENTATION SLIDES © MOSH Inc.
  5. 到達率を下げる要因で堅牢性が対処するべきもの① • 実装による不具合 (インターフェイスの不整合 ) < 誤った入力 • 外部サービスの障害 ◦

    クラウドインフラ ◦ 通知サービス ▪ AWS SES, SendGrid ▪ LINE Message API • メールドメインのレピュテーションの低下 • etc PRESENTATION SLIDES © MOSH Inc.
  6. 到達率を下げる要因で堅牢性が対処するべきもの② • 実装による不具合(インターフェイスの不整合) < 誤った入力 • 外部サービスの障害 < 実行中のエラー ◦

    クラウドインフラ ◦ 通知サービス ▪ AWS SES, SendGrid ▪ LINE Message API • メールドメインのレピュテーションの低下 • etc PRESENTATION SLIDES © MOSH Inc.
  7. 考え方は2つ • Fault Avoidance (可用性を極限まで高めてエラー自体をなくす) • Fault Tolerance (障害前提で仕組み作りをする) 前者は外部のPaaSやSaasを使っているため選択が出来ない。

    後者での仕組み作りをし、パフォーマンスを維持しつつ、回復性を持った (リトライ可能な)仕組みを用意する必要がある 実行中のエラーに対処するには? PRESENTATION SLIDES © MOSH Inc.
  8. SFnはパフォーマンス・機能両面で拡張しやすい PRESENTATION SLIDES © MOSH Inc. 1. 複数件の処理を分割・並列実行 が可能で結果統合が容易 ◦

    任意のバッチ・並列数が 指定可能 2. 処理の拡張がしやすい • 分散処理の全体像が分か りやすい • 任意の箇所に処理を差し 挟むことが可能 件数のカウント プロダクト内 通知 ① ② ②
  9. 1. State単位でのリトライが可能 ◦ 分散処理で失敗した部分 だけリトライが可能 2. リトライ超過で失敗した箇所か ら任意のタイミングで再開 (Redrive)可能 ◦

    例: 外部サービスが長期 障害から回復後に再開可 能 StepFunctionsはリトライがしやすい PRESENTATION SLIDES © MOSH Inc.
  10. 堅牢性 PRESENTATION SLIDES まとめ (ご清聴ありがとうございました 🙏) MOSHではマルチプロダクト戦略に合わせ、堅牢でスケーラブルな通知基盤を実現 した。  1.

    入力の誤りに対処 スキーマ駆動(OpenAPI + Orval)により、少ない実装で不 正な入力を未然に防ぎ、事前に 検知可能な仕組みを構築。  2. 実行エラーに対処 SFnとDynamoDBを採用。冪等 性を担保したリトライにより、副 作用のない回復性を実現。  3. スケーラビリティ SFnによる分散処理で、高負荷 時の拡張性や新機能の追加が 容易な構成を達成。 © MOSH Inc.
  11. PRESENTATION SLIDES © MOSH Inc. 1. OpenAPI 定義 (分割YAML) openapi/paths

    openapi/schemas  2. openapi-bundled.yml (結合された単一ファイル )  orval Hono サーバー routes / handler / schema client: "hono" フロントエンド React + SWR クライアント client: "swr" サービス間通信 fetch クライアント 1つの仕様から、サーバー・フロント・サービス間クライアントをまとめて生成 Orvalでhonoのroutesとhandlersの生成の流れ
  12. 巨大な単一ファイルではなく、パス・スキーマを 分割管理し、 bundle で結合する。 • openapi/paths/ … パス定義 • openapi/schemas/

    … スキーマ定義 • $ref で相互参照する分割構成 • redocly bundle で1ファイルに結合 (openapi-bundled.yml) OpenAPI 定義の構成 openapi/ ├── openapi.yml # エントリ ($refで参照) ├── openapi-bundled.yml # bundle結果(生成物) ├── paths/ │└──ここにパス毎の定義を記載した設定ファイル └── schemas/ │└──ここにスキーマを定義 └── ... PRESENTATION SLIDES © MOSH Inc.
  13. orval は OpenAPI から TS コード(クライアン ト/サーバー/Zod)を生成するツール ・client: "hono" …

    Hono ハンドラー雛形を生成 ・mode: "tags-split" … タグ単位でディレクトリ 分割 ・compositeRoute … 全ハンドラーを集約する routes.ts を自動生成 ・validatorOutputPath … 共有 Zod バリデータ ・zod.strict … 余分なプロパティを拒否 ・zod.coerce … URLパラメータの型強制 ・biome: true … 生成後に自動フォーマット orval 設定のポイント( orval.config.ts) example: { input: { target: "../../openapi/openapi-bundled.yml" }, output: { client: "hono", // Honoハンドラー生成 mode: "tags-split", // タグ単位で分割 target: "src/handlers", schemas: "src/handlers/schemas", biome: true, override: { hono: { compositeRoute: "src/routes.ts", validatorOutputPath: "src/handlers/validator.ts", }, zod: { strict: { response: true, body: true, query: true}, coerce: { query: true, param: true }, }, }, }, } PRESENTATION SLIDES © MOSH Inc.
  14. 生成されるファイル タグ単位でディレクトリが作られ、ハンドラー ・Zod・型・集約ルートが生成される。 • {feature}.handlers.ts Hono ハンドラー雛形(ここに実装を書く) • {feature}.zod.ts req/res

    の Zod バリデーション • {feature}.context.ts Hono の Context 型(param/query/body/res) • schemas/*.ts 共有スキーマ(型定義) • routes.ts 全ハンドラー集約(自動生成・編集不要) • validator.ts 共有 Zod バリデータ src/handlers/ ├──{frist-tag-name}/ │ ├── *.handlers.ts # 実装を書く場所 │ ├── *.zod.ts # バリデーション │ └── *.context.ts # Context型 ├── schemas/ # 共有スキーマ │ └── ... └── validator.ts # 共有バリデータ src/routes.ts # 全ハンドラー集約 (自動) PRESENTATION SLIDES © MOSH Inc.
  15. ハンドラ実装パターン バリデータと Context 型は自動生成。 開発者はビジネスロジックだけを書けば よい。 • zValidator(...) は自動生成を import

    する だけ • param / query / response が型安全に検 証される • Context 型で入出力の型が保証される • c.get("currentUserId") で認証ミドルウェ アの値を取得 • 開発者が書くのは中のビジネスロジックの み const factory = createFactory(); export const getAgentChatMessagesHandlers = factory.createHandlers( zValidator("param", getAgentChatMessagesParams), zValidator("query", getAgentChatMessagesQueryParams), zValidator("response", getAgentChatMessagesResponse), async (c: GetAgentChatMessagesContext) => { // ↓ ここだけ自分で実装 }, ); PRESENTATION SLIDES © MOSH Inc.
  16. 型の定義が可能 • Get/Putのオペレーション時に validationの自動化 • validation周りの余計なロジックの定 義が不要 • 実装前にスキーマが分かる +

    その型 が担保されているので理解しやすくな る dynamodb-toolboxで解決② PRESENTATION SLIDES © MOSH Inc.