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

fast-checkとneverthrowのPBT+Result型で 堅牢なビジネスロジックを...

Avatar for Globe-ing Globe-ing
May 22, 2025
1k

fast-checkとneverthrowのPBT+Result型で 堅牢なビジネスロジックを実現する

2025/5/23 TS Kaigi 2025登壇資料

Avatar for Globe-ing

Globe-ing

May 22, 2025
Tweet

Transcript

  1. 本日のアジェンダ ©Globe-ing Inc. I 自己紹介&会社紹X I ビジネスロジックを表現するとはQ I Result型とnevertho6 I

    プロパティベーステストとはQ I fast-checkによるPBTの実# I Result型+PBTの展望
  2. © G l o b e - i n g

    I n c . 自己紹介 上田慶祐(うえだけいすけ) ƒ 所属・役´ ² グロービング株式会社クラウドプロダクト事業部 CT ƒ 経¯ ² 中学生くらいからずっと個人開発’ ² 新卒入社でコンサルティングに従事した後現職に転職してからは職業 エンジニ¡ ƒ やってるこŠ ² 自社のプロダクトおよび共同開発プロダクトの技術責任’ ² アーキテクト・設計者としての動きが多いが自分で実装するのも好G ² バックエンドからフロントエンド、インフラまで全部見¢ ² 好きな言語はもちろんTS、あとはPythonも慣れているのでよく使い ます(社内の言語はTS or Python)
  3. 社名 グロービング株式会社 / Globe-ing Inc. (証券コード277A) 代表者​ 代表パートナー 輪島総介​ 代表取締役社長 田中耕平​ 資本金 1,195,288,500円(2024年11月29日現在)

    所在地​ 本社オフィス Southオフィス 東京都港区南青山3丁目1番34 ​3rd MINAMI AOYAMA 11F​ 東京都港区南青山2丁目24-11 フォーラムビルディング10F​ 設立​ 2017年1月​(本格的な事業開始は2021年3月) 主要子会社 株式会社 アバランチ X-AI.Labo 株式会社 会社概要 私たちについて ©Globe-ing Inc.
  4. 顧客の事業や改革をハンズオンで実行/推進 X 当社コンサルタントが顧客の立場で、事業責任者として事業を推進) X 人材/ノウハウを拠出することで事業パートナー​として共に成果を創出) X 伴走型支援で、成果創出と自律を実現 CxOクラスの伴走者として戦略/DXを支援 X 経営戦略/新規事業立ち上げ/M&A戦略、​DX/デジタル事業戦略の構想策定等の支援)

    X CSO/CDOの元で「参謀」役として​“プロジェクトをかじ取り” コンサルティングノウハウを型化した​クラウドプロダクトを開発・提供 X ノウハウをプロダクト化し、幅広いクライアントに競争力の高い価格で提供 JI型​
 (Joint Initiative)​
 コンサルティング​ クラウドプロダクト​ 従来型​
 コンサルティング​​ © G l o b e - in g In c . 事業概要 私たちについて
  5. © G l o b e - i n g

    I n c . 全世界のコンサルタントAIエージェント化 経 営 層 管 理 層 現 場 層 全 世 界 2 3 0 万 人 の コ ン サ ル タ ン ト を A I コ ン サ ル タ ン ト で の 代 替 を 狙 う Joint Initiative ク ラ イ ア ン ト の 経 営 層 の 一 部 と な り 、 ハ ン ズ オ ン で 実 行 推 進 A I や ク ラ ウ ド 最 大 活 用 し 、 経 営 層 の 意 思 決 定 ス ピ ー ド を 2 倍 に AI Consultant 戦 略 / 企 画 立 案 時 に ア イ デ ア 出 し / 仮 説 出 し を サ ポ ー ト 議 事 録 / 市 場 調 査 な ど の 作 業 を 全 て お ま か せ 、 5 0 % 以 上 の 工 数 削 減 に Cloud Product 必 要 な デ ー タ は 即 抽 出 、 デ ー タ ド リ ブ ン な 業 務 推 進 の 基 盤 に 誰 も が デ ー タ を 利 用 可 能 と な り 、 デ ー タ 利 活 用 の 民 主 化 を 実 現 デ ー タ 基 盤 ク ラ イ ア ン ト J I コ ン サ ル タ ン ト A I コ ン サ ル タ ン ト
  6. © G l o b e - i n g

    I n c . 全世界のコンサルタントAIエージェント化 グロービング の独自性 言 語 の 壁 も 越 え 、 世 界 に 約 2 3 0 万 人 い る 旧 来 型 の コ ン サ ル タ ン ト を A I エ ー ジ ェ ン ト で 完 全 代 替 01 2025年6月 リリース AI議事コン A I 議 事 録 作 成 ツ ー ル 02 2025年夏予定 AIリサーチ(仮) A I 記 事 調 査 ツ ー ル 03 coming soon AIパワポ作成(仮) A I パ ワ ポ 自 動 作 成 ツ ー ル 04 coming soon グロービングくん(仮) A I コ ン サ ル タ ン ト A I コ ン サ ル タ ン ト が 日 々 の 業 務 を サ ポ ー ト クライアント
  7. © G l o b e - i n g

    I n c . ミッション クラウドプロダクト コンサルティングノウハウのプロダクト化 コ ン サ ル タ ン ト が 培 っ た “ ノ ウ ハ ウ の 工 業 化 ” を 行 い 、 幅 広 い ク ラ イ ア ン ト に “ 勝 た せ る S a a S ア プ リ ケ ー シ ョ ン ” と し て 提 供 す る 経 営 イ ン パ ク ト の 大 き い 領 域 を 対 象 に テ ク ノ ロ ジ ー ( C l o u d × B i g D AT A × A I ) を 組 み わ せ た プ ロ ダ ク ト を 開 発 知見 分析力 方法論 設計・開発 最適化 コスト削減 支出最適化 経営の 見える化 営業効率化 SCM最適化 持続可能性 HR オクタゴン 8つの領域
  8. © G l o b e - i n g

    I n c . プロダクト クラウドプロダクト セールススイート 顧客体験を最適化し
 事業成長を加速させる 経営の 見える化 営業効率化 持続可能性
  9. © G l o b e - i n g

    I n c . プロダクト クラウドプロダクト スペンドインテリジェンススイート 外部支出を最適化し
 業務効率・ガバナンス向上させる 設計・開発 最適化 コスト削減 支出最適化
  10. そもそもビジネスロジックとは? ビジネスロジックを表現する ビジネスロジックとはs  業務において満たすべき制約条—  特に本セッションではEnterprise Business Ruleと呼ばれるアプリケーションがなくとも(=紙で業務をして いても)必要となるルールを指してい„

     「商品単価は0円以上でなければならない」のようなシンプルなもz  「要相見積もり品の場合は見積もり先が3社以上でなければならない」のような複合的な条—  「支払い完了時のみ出荷待ちに変更できる」のような状態遷移に関わるもの など クリーンアーキテクチャの図でいうところのEntityで表現される要素が ビジネスロジック(ドメイン駆動開発でいうところのドメイン層) The Clean Code Blog The Clean Architecture 13 August 2012 より引用 ©Globe-ing Inc.
  11. ビジネスロジックの表現方法 ビジネスロジックを表現する ビジネスロジックの表現方法といった場合には2つの構成要素が存在する ビジネスロジックを記述する方法 ビジネスロジックをどこにどうやって記述する„ h 手続き型(トランザクショナルスクリプト¤  データに対する処理の流れを手続き的に記述す ®

    h Active Recordパター™  データベースの構造、アクセス方法を提供する クラスにビジネスロジックを実装す® h オブジェクト指向によるドメインオブジェク  データ構造とデータに対する操作をクラスで表 現す® h 関数型によるドメインオブジェク  データ構造を型として表現して型に対する演算 (関数)として表現する ビジネスロジック違反を表す方法 ビジネスロジックの違反(金額が負で入力された)を どのように呼び出し元(アプリケーション層・ユース ケース)に伝える „ h 例外によるアプロー  違反時に例外を送出す ®  Zodやclass-validatorなどのバリデーションラ イブラリを使いライブラリから例外を送出す ® h 返り値によるアプロー  Nullによる表現(違反時はNullを返す  型によるアプローチ(判別共用体やResult型を 使う)など ©Globe-ing Inc. 本日はこちら にフォーカ ス
  12. 返り値によるアプローチ   判別共用体の活用 ビジネスロジックを表現する 判別共用体 (Discriminated Union) を活用するとエラーの型を詳細に表現することが可能となる ©Globe-ing Inc.

    判別共用体? ƒ TypescriptのUnionType(|)の利用方法の1† ƒ 判別子 Discriminatorを要素の1つとすることで 型の絞り込み (Narrowing) をうまく活用できz ƒ 例えば下記の右の型のkino ƒ kind===’success’のチェックをすれば型安全に newBalanceにアクセスできz ƒ またkind===’error’で絞り込めば今度はtypeを 判別子としてエラーの種類に応じた処理を型安 全に書くこともできる
  13. 返り値によるアプローチ ② 判別共用体の活用 ビジネスロジックを表現する ドメインオブジェクトのメソッドが判別共用体を返すことで型安全にエラーハンドリングを行うことができる (エラーハンドリングの強制はできない) ©Globe-ing Inc. « result.kind===’error’で絞り込んだことでエラーの種類

    (type)に安全にアクセスできt « またerrorTypeの型は"invalidAmount" | "insufficientFunds"に推論されるのswitch文とnever型を 使ったエラー網羅性チェックも可– « caseを網羅していればdefaultではerrorTypeはneverに 推論される
  14. Result型とNeverthow Result型自体はシンプルな型なのでとくにライブラリを使わなくても問題ないが、付随する色々な操作を実装す るのは少し骨が折れるので既存ライブラリを活用するのが無難 ©Globe-ing Inc. ០supermarco/Neverthow https://github.com/supermacro/neverthroØ Ë Result型に特化したライブラ¯

    Ë Github Stars 5.5œ Ë Result型を取り扱う分にはこのライブラリでこまることはな’ Ë 類似ライブラリにvultix/ts-resultなどがあるがstarsなどの状況からこちらが無v tŸ gcanti/fp-ts https://github.com/gcanti/fp-t} Ë Result型を含む関数型プログラミング(fp)で活用できるライブラ¯ Ë 単にResult型を使うだけであれば不要な部分が多いので、関数型ドメインモデリングを行うなど関 数型を全面に押し出して開発する場合はこちら
  15. map, andThenによる処理の連鎖 Result型とNeverthow ©Globe-ing Inc. mapやandThen関数を用いるとResult型の結果に対して処理を行い別のResult型に対応させる処理フローをメ ソッドチェーンで記述できる ma– ‰ (T)=>U型の関数を引数としてResult<T,E>をResult<U,E>にマッピングする関™

    ‰ Resultの後に失敗しない(Result型でない)処理を行う andThej ‰ (T)=>Result<U, F>型の関数を引数としてResult<T,E>をResult<U,E|F>にマッピングする関™ ‰ Resultの後にさらに失敗するかもしれない処理(Result型を返す)処理を行う
  16. eslintによるエラー処理のチェック Result型とNeverthow eslint-plugin-neverthrowを使うことでエラー処理をしていないResult型を検出してlintでのエラー処理を強制で きる ©Globe-ing Inc. eslint-plugin-nerverthowのmust-use-resultルーÆ m Result型を返す関数を呼び出してその結果を使っているかどうかをチェックするルーÀ m

    このルールに違反している場合は適切なエラーハンドリングを行っていないことを意味す– m 公式で紹介されているmdbetancourt/eslint-plugin-neverthrowはeslintのflatConfig非対応なの で最新のeslintを使う場合はフォーク版の@bufferings/eslint-plugin-neverthrowを利用する
  17. ここまでのまとめ Result型とNeverthow Neverthowを使うことで「エラーの情報を表現する」「複数のエラーに対応する」「エラー処理を強制する」と いう3つの目標を達成しつつ、「メソッドチェーンによるシンプルな表現」を行える ©Globe-ing Inc. Äz エラーの情報を表現す§ Ï Result<T,

    E>型の型引数でエラーの情報を型として表現でき© Ï エラー情報を判別共用体とすることでエラーの情報を持たせやす— ¿z 複数のエラーに対応す§ Ï combinedWithAllErrorsによりエラーの情報を配列としてまとめることができ© ’z エラー処理を強制す§ ƒ eslintのプラグインによりエラー処理をしていないResult型をチェックでき§ Øz メソッドチェーンによる表Ë Ï mapやandThenによりif文の分岐なくシンプルな形で処理の流れを表現できる
  18. プロパティベーステストとは プロパティベーステストとは 単体テスト手法の1つで特定の入力値に対する期待値をテストするのではなく、あるべき性質(プロパティ)に 基づいてテストする手法のこと ©Globe-ing Inc. äý テスト対象の性質を定義す« ö 例えば「ゼロでない値」「1文字以上7文字以下の文字列」のようなプリミティブ型に対する性°

    ö 「回答期限の切れた見積依頼書」のようなドメインオブジェクトに対する性質 なÁ ý 入力対象の性質を満たすジェネレータを定義す« ö ジェネレータとは性質を満たすランダムな値・オブジェクトを生成する関Ž ö ライブラリ側で事前定義されたジェネレータを使う場合もあるし、自前で特定の性質を満たすドメインオ ブジェクトを生成するジェネレータを実装することもあª iý 多数のランダムケースで検“ ö ジェネレータにより多数のランダムなテストケースが生成され、自動化テスト (jest, vitestなど) で実行さ れª ‚ý 最小の例を探索す« ö 失敗した場合は失敗する最小の例(=エッジケース)をジェネレータの生成する範囲を狭めて(シュリン ク)発見する ➡️このプロセスを支援するのがプロパティベーステストフレームワーク
  19. 具体例 Fizzbuzzのテスト プロパティベーステストとは 非正の値を渡すと例外が発生するFizzbuzzを通常のユニットテストでテストすることを考える ©Globe-ing Inc. 具体的な値は一切登場しな• ~ fc.propertyとfc.integerがキr ~

    fc.propertyはジェネレータ(fc.integer)とテスト ケースを受け取ってテストケースを実行してい” ~ 例えばfizzBuzz関数の実装が間違ってゼロの場合に 例外を投げてない場合は以下のような出力が得られ てCounterexpmaple(反例)を発見できる
  20. fast-checkとは fast-checkによるPBTの実装 プロパティベーステストをJS/TSで実現するためのライブラリ ©Globe-ing Inc. プロパティベーステストの構成要素を網Ø Ü 値を生成するジェネレータを組み込みで提供(fast-checkで はArbitrartyと呼ばれるœ Ü

    数字などのプリミティブ型からuuidだったりbase64文字 列のような特定ユースケースで使えるものまで存y Ü 独自のジェネレータも容易に定義できÐ Ü 失敗したテストを最小限のテストケースにして反例を見つけ る縮小(Shrink)に対応 テストフレームワーク中s Ü あくまでプロパティに基づいたテストケースを生成すること に特‘ Ü アサーションを行う部分は他のテストフレームワークを使う のでJestでもVitestでもなんでも使用可能 豊富な実 Ü react、jestやjs-yamlなどライブラリ開発で活用されバグの発 見に貢献している
  21. fast-checkが提供するArbitraryと独自ジェネレータ fast-checkによるPBTの実装 プリミティブ型から配列、オブジェクトなど多様なArbitraryが用意されているため様々なテストに対応でき、こ れを組み合わせることで独自のジェネレータを定義できる ©Globe-ing Inc. プリミティブº ¹ fc.integer({ min:

    -99, max: 99 }¢ ¹ fc.string({ minLength: 4, maxLength: 6 — ¹ fc.date({max: new Date(xxxx)}) データ構˜ ¹ fc.array(fc.integer()¢ ‰ 整数値の配„ ¹ fc.dictionary(fc.string(), fc.string()¢ ‰ 文字列をキーとして文字列が値の辞“ ¹ fc.record(¢ ‰ 指定した型のオブジェクト 独自ジェネレータの定義 組み込みのArbitrary ‰ fc.recordでまとめて必要な値を生4 ‰ mapでドメインオブジェクトにマッピン6 ‰ このようなGenerator関数を定義しておくこと で生成ロジックを再利用してテストが楽にな る 特定のユースケー— ¹ fc.stringMatching(regex¢ ‰ 正規表現を満たす文字„ ¹ fc.uuid(¢ ‰ UUIg ¹ fc.emailAdress(¢ ‰ メールアドレス など
  22. ここまでのまとめ Result型とNeverthow fast-checkを使うことで網羅的なテストを行うことが簡単になり、Result型と合わせて開発時により高速にバグ を見つけつつテストでコードを駆動できる ©Globe-ing Inc. ¢ÿ テストカバレッジ向ì è 今回のサンプルコードレベルではわかりづらいが複雑な帳票を表すクラスをテストする場合にはラ

    ンダムな生成は有¬ ·ÿ 生成ロジックの再利© è 通常のテストでもファクトリ関数を使うことはあるがライブラリレベルで生成の仕組みがあるのは 便” è プロパティごとに生成ロジックを定義して様々な状態に対するテストを簡単に書けÆ †ÿ Result型との相} è PBTそれ自体はテスト対象やテストフレームワークから独立しているためResult型と自然に調和すÆ è _unsafeUnwrap(Err)などのテスト用メソッドを用いることで可読性と検証の網羅性を保ってテスト できる
  23. ここまでで紹介した手法を用いてバックエンドをPoCとして一連実装して良かった点は「ビジネスロジックが見 やすくなる」「エラー処理忘れによる500エラーの撲滅」など Result型+PBTを検証してよかったところ Result型とPBTの展望 ©Globe-ing Inc. ¼û ビジネスロジックが見やすくなÏ æ Result<T,

    E>のEの型を最初に定義するのでどのようなビジネスロジックの逸脱があるのかを宣言的 に把握できÐ æ PBTと合わさることでどのような前提条件(プロパティ)であればどのようなエラーとなるのかをテ ストファイルから把握しやす àû エラー処理忘れがなくなÏ æ エラー処理を忘れるとlintで弾かれるのでかなり強‚ æ ただし後述するように外部ライブラリとの接合部分はResult.fromThrowable や自分でのTry-catchで 例外をResult型に変換する必要がある
  24. © G l o b e - i n g

    I n c . が、いいところばっかりでもない(技術選定は常にトレードオフ) ここから述べるような懸念事項は考慮する必要がある
  25. 検討ポイント③ 非標準な記法・実装 Result型とPBTの展望 Result型の利用は広まりつつあるものも一般的に使われている記法ではない。またfp-tsかneverthowなど複数の ライブラリがある中で言語標準の書き方でないため将来的な言語仕様の変更により影響を受ける可能性もある ©Globe-ing Inc. 一般的な書き方ではな€ ƒ どうしても学習コストが高•

    ƒ 実際社内メンバーでも関数型の経験がない限り理解は難しそうだっW ƒ 慣れるまでは生産性が低下する可能性があP ƒ チーム全体での知識共有と教育が必6 ƒ AI との相k ƒ AI Coding Agent, Vibe Coding全盛の中でAIが生成しにくいコードとな る可能k ƒ .clinerulesで覚え込ませるとある程度対応できそうだっW ƒ コードの可読性の問 ƒ メソッドチェーンが長くなると読みづらくなることがあP ƒ 新規参画メンバーの学習障壁となる可能性 言語仕様にある書き方ではない・ライブラリ依 ƒ ライブラリ選t ƒ 今回はNeverthowを選定したがfp-tsなど複数の代替が存‘ ƒ 今後の趨勢ではライブラリの乗り換えを強いられる可能性もあP ƒ 今後のJSの仕様拡¨ ƒ なぜResult型のライブラリを使うのか?の理由の1つにmapなどのメソッド チェーン記法の導入があP ƒ メソッドチェーンのような関数型らしい記法はTC39で議論されていP ƒ パイプライン演算子(Stage29 ƒ 拡張メソッド(Stage19 ƒ do記法(Stage19 ƒ パターンマッチング (Stage1r ƒ Stage的に仕様入りは相当先だが将来大きく変わる可能性あž ƒ サードパーティライブラリの依存リス ƒ ドメイン層というコアで依存するため、ライブラリの問題が全体に影響す P ƒ バージョンアップによるAPIの変更や非互換性の発生リスクがある
  26. Result型とPBTの適用射程 Result型とPBTの展望 シンプルなビジネスロジックの場合はResult型の高いエラー表現力やPBTによる網羅的テストの効果が薄い。複 雑なビジネスロジックで長期の開発を行う場合に、領域を絞って活用を始めると効果が大きい ©Globe-ing Inc. ドメインロジック中心に適用す… y プレゼンテーション層(コントローラ)まで持ち回るのは高コストにな りやすˆ

    y 一番質を保ちたいDomain層のみをResult型にしてアプリケーションサー ビス層(ユースケース)でResult型を例外に変えてしまB y 例外とHTTPレスポンスの対応はフレームワークに任せ„ y インフラ層(ORM)の例外をResultにするのも辛 y 重要なビジネスロジックが集中するドメイン層で、エラー状態を明示的 に表現することで品質向上につながる 規模の大きなアプリケーションに適用す… y 小規模なアプリケーションではビジネスロジックも限られており、Result 型とPBTによる堅牢性向上よりも、実装工数の増加による負の面が大きˆ y 大規模アプリケーションでは、複雑な状態遷移や例外的なケースが増え るため、Result型の利点が活き„ y チーム開発において、エラー処理の一貫性とコード品質の標準化に役立 つのでチームを啓蒙しながら適用していく PBTにこだわりすぎなä y 事例ベースにテストで十分な場合も多いは§ y 様々なパターンを網羅的に検証すると大変みたいなものからPBTの適用を 始めるべき (B2Bの帳票はこの特徴を満たす£ y どうしても独自のジェネレータは慣れるまで工数増につながる上、テス トオラクル問題を産‘ y 入力値に対して期待値をどうやって作るかの問’ y PBTでは入力値がランダムなのでともすればテストコードで実装と同 じコードを書くような無意味なテストを生みかねない 全てのドメインに適用しなä y 理想的には全てのドメインに適用した方が統一的かつ品質的にも望まし いがとくに重要で複雑なビジネスロジックを持つドメインから適用を始 めて効果を検証するのが現実 y CRUDベースの単純なドメインよりも、複雑な状態管理や計算ロジックを 持つドメインを優先する