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

TypeScriptプロジェクトにスキーマ駆動開発を持ち込み、より型安全な世界へ

SansanTech
PRO
September 12, 2023

 TypeScriptプロジェクトにスキーマ駆動開発を持ち込み、より型安全な世界へ

イベント
TypeScriptを活用した型安全なチーム開発
https://sansan.connpass.com/event/292695/

■登壇概要
タイトル:TypeScriptプロジェクトにスキーマ駆動開発を持ち込み、より型安全な世界へ
登壇者:技術本部 Digitization部 Bill One Entry グループ 秋山 雅之

■Digitization部 エンジニア 採用情報
https://media.sansan-engineering.com/digitization

SansanTech
PRO

September 12, 2023
Tweet

More Decks by SansanTech

Other Decks in Technology

Transcript

  1. TypeScriptプロジェクトに
    スキーマ駆動開発を持ち込み、
    より型安全な世界へ
    Sansan株式会社
    Digitization部 Bill One Entryグループ 秋⼭ 雅之 / @aki202

    View Slide

  2. 今⽇の内容
    https://buildersbox.corp-
    sansan.com/entry/2023/08/
    14/182118
    2

    View Slide

  3. 2021年中途⼊社。
    請求書のデータ化システムを⽇々改善 & 運⽤。
    {
    "📛aliases": ["@aki202", "リーダブル秋⼭"],
    "❤likes": ["プログラミングパラダイム", "アーキテクチャ"],
    "🧑💻roles": ["バックエンドエンジニア", "フロントエンドエンジニア"]
    }
    秋⼭ 雅之
    Sansan株式会社
    Digitization部 Bill One Entryグループ
    3

    View Slide

  4. 1. はじめに
    1. スキーマ駆動開発とは
    2. Testing Trophyとの関係性
    2. 構成
    1. 最初の構成と課題
    2. 最終的な構成
    3. バックエンド
    1. スキーマから型ファイルを作る
    2. APIハンドラに型を与える
    3. バリデーターを追加する
    4. huskyでスキーマ変更を検知する
    4. フロントエンド
    1. スキーマからAPIクライアントを作る
    5. パターンマッチングを持ち込む
    6. おわりに
    ⽬次
    4
    © Sansan, Inc.

    View Slide

  5. 1. はじめに

    View Slide

  6. “事前に定義した単⼀のインタフェースを元に、
    プロダクトを構成する各サービスを開発する⼿法”
    スキーマ駆動開発とは
    図:ヒトクセ『開発効率UPを実現する「スキーマ駆動開発」』より 6

    View Slide

  7. (1) ⾃動化:スキーマからドキュメント・実装コード・テストを部分的に
    ⾃動⽣成できる。
    (2) 依存制約の解消:例えば従来はバックエンドチームが担当するWebAPIの
    完成を待たなければ、フロントエンドチームが開発を始められないという
    制約があった。スキーマさえ定義されていれば、この制約を取り払える
    ようになる。
    (3) 堅牢性の担保:スキーマを各サービスが共有することで、データ構造や
    プロトコルの⼀貫性が⾼まり、サービスの堅牢性を担保できます。
    何が嬉しいのか
    7

    View Slide

  8. 従来のTesting Pyramidに代わ
    り提唱されているモダンフロ
    ントエンドに
    おけるテストガイドライン。
    右図の上から順番に、
    - End to End:E2Eテスト
    - Integration:結合テスト
    - Unit:単体テスト
    - Static:Lintや型チェック
    下のレイヤーほど低コスト。
    Testing Trophyとは?
    図:“Testing Javascript” より
    図:Google Cloud
    “Continuous testing” より
    8

    View Slide

  9. スキーマ駆動開発が寄与するのは
    ⼀番下のStaticレイヤー。
    最もコストの⼩さなレイヤーなので、
    ここのStaticレイヤーをどれだけ厚く
    できるかどうかが良いテストの指標の
    ⼀つ。
    Testing Trophyとの関係性
    9
    図:“Testing Javascript” より

    View Slide

  10. 2. 構成

    View Slide

  11. 最初の構成
    Frontend
    - APIクライアントのパラメータは
    openapi.yamlを⽬視で⾒て⼿動設定
    Backend
    - スキーマはOpenAPI 3.0で記述
    - APIハンドラはany型でデータを受信
    - データのバリデーションはAPIハン
    ドラやその先の処理で個別に実⾏
    11

    View Slide

  12. 課題1: 信頼境界の不在
    クライアント
    APIハンドラ
    アプリケーション
    サービス
    ドメイン
    サービス
    ⼀連のリクエスト・レスポンスのフロ
    ーは直列の関数実⾏と⾒做せる。
    “ここ以降の処理に渡るデータは安全”
    と⾔えるボーダーが信頼境界。
    信頼境界がない場合、各関数で引数が
    予期されたデータかチェックする必要
    がある。→ 重複やヌケモレのリスク
    があるアンチパターン
    エンティティ
    ルーティング
    引数チェック
    引数チェック
    引数チェック
    引数チェック
    信頼境界が無い状態
    12

    View Slide

  13. スキーマ定義からTypeScriptの型を作っておらず、次の状態だった。
    Backend
    - 受信するデータがany型
    - レスポンスデータがany型
    Frontend
    - クライアントから送信するデータがany型
    - クライアントが受け取るデータがany型
    何が問題か
    - 例えばAPIリクエストやレスポンスのenumの列挙⼦を⼀つ変更した場合、
    影響範囲を全て⽬視チェックする必要がある
    課題2: 静的解析の範囲が⼩さい
    13

    View Slide

  14. 最終的な構成
    Frontend
    - スキーマから型の付いたクライアントを
    作成
    Backend
    - スキーマから型定義ファイルを作成
    - APIハンドラの引数とレスポンスに型制
    約を追加
    - バリデーターを挟み信頼境界を設定
    14

    View Slide

  15. 3. バックエンド

    View Slide

  16. スキーマから型ファイルを作る
    - swagger-typescript-api を利⽤
    - 豊富なオプション
    - 明朗なアウトプット
    swagger-typescript-api の変換例(acacode/swagger-typescript-api より)
    16

    View Slide

  17. APIハンドラに型を与える
    - リクエストとレスポンスに型を付与
    - ただしこの時点で引数はany型で⼊ってくる
    ↑ ExpressのAPIハンドラに型を与える
    ← スキーマから⽣成した型ファイル
    17

    View Slide

  18. - ルーティングにはbeforeの構成から引き続き
    swagger-routes-express を採⽤しているが、
    リクエストに対するバリデーションは⾏ってくれ
    ず、リクエストパラメータはany型でAPIハンド
    ラに渡される。
    - スキーマを元にバリデーションを⾏ってくれる
    express-openapi-validator を新たに導⼊した。
    - これによりOpenAPI 3.0で記述できる範囲での
    信頼境界を設定できた。
    - つまりリクエストがスキーマ通りのリクエ
    ストであるかどうかを、APIハンドラは
    気にしなくて良い状態
    バリデーターを追加する
    18

    View Slide

  19. - 次の状態になった。
    - (1) リクエストに対してバリデーションを早い段階で掛けることで、
    信頼境界を設定できた。
    - (2) APIハンドラを型安全な関数に変えることができた。
    - これらは型ファイルが最新の状態であることを前提にしている。
    ➛ 型ファイルか現在のスキーマと⼀致しているかどうかを検知する必要がある。
    - huskyを⽤いて、“現在の型ファイル” と “スキーマから⼀時的に作った型ファイ
    ル” のハッシュが⼀致しているかをgit push前に検証する。
    huskyでスキーマ変更を検知する
    19

    View Slide

  20. 4. フロントエンド

    View Slide

  21. スキーマからAPIクライアントを作る
    - APIクライアントの⽣成にも、
    バックエンドでスキーマから型定義の⽣成に使ったswagger-typescript-api利⽤
    - 右図のようにリクエストを発⾏できる。
    - リクエストデータは型安全。
    - レスポンスデータも型安全。
    21

    View Slide

  22. スキーマのパス
    - beforeの構成と同様、スキーマはbackendレポジトリに置いている。
    - APIクライアントの⽣成時、frontendレポジトリから参照するスキーマの
    パスは、
    backendレポジトリにあるスキーマを相対パスで指定している。
    - これは⼀⾒雑な⽅法で、開発者PCのレポジトリの配置⽅法に依存するこ
    とになるが、シンボリックリンクを使うという⽅法もあるので、特に問
    題にはなっていない。
    - featureブランチをmergeする際に衝突が発⽣するときもあるが、これは
    どのような⽅法を取っても同様に抱える⼩さな問題。
    22

    View Slide

  23. 5. パターンマッチングを持ち込む

    View Slide

  24. - 当初の⽬的だったスキーマ駆動開発の導⼊は終わった。
    - バックエンドとフロントエンドが単⼀のスキーマを参照し、型制約の恩
    恵が受けられている状態。
    ここまでで
    本当に型制約の恩恵は⼗分か?
    24

    View Slide

  25. 分岐処理の例
    - 次のようなケースは?
    25

    View Slide

  26. 分岐処理のヌケモレ
    - 型定義にも当然 “guest” が追加されるが、
    コードベースの分岐処理がそれに対応できているかは⽬視による確認が
    必要。
    26

    View Slide

  27. switch⽂も同様
    - LintルールやExhaustiveness
    checkingというテクニックで網
    羅チェックが可能
    - でもそれぞれ、表現⼒が弱い、
    冗⻑なコードが必要という弱点
    がある
    27

    View Slide

  28. ts-patternによる網羅的分岐処理
    1. 簡潔なコードで網羅的分岐処理が可能
    2. guestが追加されることを静的に検知できる
    3. stringのunionだけではなく、複数のunionの組み合わせの分岐処理、条件
    式から値をキャプチャするなどといった複雑な要件にも対応できる。
    28

    View Slide

  29. ts-patternあれこれ
    ↑クラスの種類で分岐処理
    ↑2つにUnionの組み合わせで分岐処理
    ←詳しくは https://zenn.dev/aki202/articles/5d725c080640f9
    29

    View Slide

  30. 6. おわりに

    View Slide

  31. - 次の施策を通して、プロジェクト全体に型制約を与え、より安全な開発をで
    きるようになりました。
    - APIリクエストとレスポンスに型制約を与えた
    - リクエストに対し早期にバリデーションをかけ信頼境界を作った
    - パターンマッチで網羅的な分岐処理を静的に保証できるようになった
    - システムは⽣まれた瞬間から複雑化します。複雑化の中で堅牢性を担保する
    のはこの仕事の最も⾯⽩い所の⼀つだと思いますし、⾃分もやっていて楽し
    いです。
    Digitization部はSansanの主要なプロダクトのプラットフォームに位置する
    重要部署です。是⾮カジュアル⾯談しましょう。気軽にご連絡ください。
    まとめ
    31

    View Slide

  32. View Slide