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

関数型ドメインモデリングを 非関数型のプログラミング言語で やってみた

Tomohisa Takaoka
September 09, 2024
3.5k

関数型ドメインモデリングを 非関数型のプログラミング言語で やってみた

Scott Wlaschin 氏の著作「関数型ドメインモデリング」の日本語訳が最近出版されました。本書は、ドメイン駆動設計(DDD)と関数型のプログラミングスタイルによってソフトウェアの複雑性にどう対処できるか、その手法を解説しています。関数型プログラミングは難しいという印象を持たれがちです。

しかし現代の主流プログラミング言語の多くは実際にはマルチパラダイムであり、関数型言語が持つ特徴を多かれ少なかれ取り入れることで、より柔軟で強力なプログラミングができることを目指しています。それで、関数型とは見なされないプログラミング言語を使う場合でも、工夫をすることで本書で紹介されている設計・実装のテクニックの多くを適用できます。

このセミナーでは、「鉄道指向プログラミング(ROP)」を始めとした本書の幾つかのポイントを C# でどのように実践したかをご紹介します。

Tomohisa Takaoka

September 09, 2024
Tweet

More Decks by Tomohisa Takaoka

Transcript

  1. 株式会社ジェイテックジャパンの紹介 • 創業50年を超えた総合IT企業、株式会社 ジャパンテクニカルソフトウェア (JTS) のグループ企業 • New York 所在

    J-Tech Creations, Inc.と協 業 • B2C / B2B アプリケーションを 開発‧運⽤する • .NET‧Azure 等 Microsoft の 技術スタックを主に使⽤
  2. セッションの内容 1. 書籍「関数型ドメインモデリング」 2. C# と関数型パラダイム 3. 鉄道指向プログラミング (ROP) 4.

    コマンドで始まりイベントで終わるワークフローと、 CQRS‧イベントソーシング
  3. 参考⽂献 • ⽇本語タイトル 「関数型ドメインモデリング ドメイン駆動設計とF#で ソフトウェアの複雑さに⽴ち向かおう」 • Scott Wlaschin ⽒著、猪股健太郎⽒訳

    • オリジナルは2018年発表 ⽇本語版は2024年出版 • 株式会社ドワンゴ (アスキードワンゴ) が翻訳‧ 出版、達⼈出版会が電⼦版を販売 ※本セッションの引⽤はすべてこの書籍からのもの
  4. 関数型ドメインモデリングの内容 • 「本書を書いた⽬的は、ソフトウェア設計全般、 特にドメイン駆動設計が関数型プログラミングの ⼿法からどのように恩恵を受けられるかを⽰すた め」[⽇本語版へ寄せて] • 「本書は、(ソフトウェアの) 複雑性に対処するため に、ドメイン駆動設計と関数型プログラミングが

    うまく機能することを⽰す本の1つ」 [訳者まえがき] • 「本書の⽬的は、ドメインモデリングの⼿段とし て関数型プログラミングが実際に優れており、明確 かつ簡潔な設計を⽣み出せる、と⽰すこと」 [p.1]
  5. 注⽬したポイント 「イミュータブルなデータ とイミュータブルな関数を 組み合わせた『型』により ドメインの状態遷移を 明⽰的に表現する」 [訳者まえがき] 型がドキュメントの代わりとなり、ソース コードがドキュメントとしても機能する [第5,

    7章] 不正な状態は表現できず、ルールが維持さ れていることをコンパイラがチェックする [第6, 10章] データ構造ではなく、ビジネスイベントや ワークフローに焦点を当てる [第1, 7章]
  6. C# について • Microsoft が開発したプログラミング⾔語 • .NET ランタイム上で動作する • .NET

    は OSS & クロスプラットフォーム 最も活発なオープンソースプロジェクトの⼀つ • 基本はオブジェクト指向⾔語 マルチパラダイムでもある • 現在の⾔語バージョンは 12.0 (13.0 はプレビュー版) • 2017年 (ver 7.0) 以降は毎年バージョンアップ
  7. C# がサポートする関数型パラダイムの要素 • ラムダ式 • 第⼀級関数 ◦ ⾼階関数 ◦ 関数合成

    ◦ 部分適⽤ ◦ カリー化 • パターンマッチング • イミュータビリティ 関数型⾔語と⽐較すると 完全ではない部分もある
  8. C# がサポートしない関数型パラダイムの要素 関数合成演算⼦‧パイプライン演算⼦ • 拡張メソッドとメソッドチェーンで代⽤ Option 型 • 組み込みの型はないが、⾃分で実装は可能 •

    LanguageExt などのライブラリも利⽤可能 直和型 / タグ付きUnion(判別共⽤体) • 関数型っぽく書くための最⼤の障壁かもしれない • 提案はされている(実装はまだ開始されていない)
  9. C# で関数型っぽく書くために • データを record 型で定義し、イミュータブルにする ◦ データに振る舞いを持たせない ◦ データに状態を持たせない

    • 関数を静的 (static) メソッドで定義する ◦ トップレベルに関数を直接書けないので • メソッドチェーンでパイプライン処理を書く • Unit 型を定義し、void の代わりにメソッドの戻り値として使う ◦ デリゲートを全て Func 型に統⼀できる • Option 型を定義し、Nullable な型に代えて使う • Result 型を定義し、例外をスローせずに返す
  10. 鉄道指向プログラミング (ROP) とは • 著者の Scott Wlaschin ⽒が提唱 • エラーハンドリングの⼿法

    • プログラムの処理を鉄道のレールに⾒⽴てる • 処理の流れを線路、エラーを分岐点と捉える • 正常な処理が続く場合は⼀本のレールを進み、 エラーが発⽣した場合は別のレールに分岐するという形で表現 • ⼀旦分岐した線路は、元の線路に戻ることがない > パイプラインのどこかで例外が返されたら、残りのステップを回避する
  11. 鉄道指向プログラミングの特徴 1. 関数の戻り値を Result 型を⽤いて表現する ◦ 成功時は成功の値を、失敗時はエラー情報を返す 成功か失敗のどちらか⽚⽅を戻す (直和型 /

    タグ付きユニオンを使った実装が理想) ◦ ⼿続き型⾔語で⼀般的な例外を try - throw - catch で扱う形と⽐較して、 「エラーをドメインエラーとして扱い、ドメイン駆動設計の他の部分と同じように 注意を払う」扱い⽅ [p.188] 2. Result 型を戻す複数の関数をパイプラインで接続する ◦ 成功処理とエラー処理に処理を分離する ◦ 処理の流れが決まっているため、エラー処理の記述がシンプルになる ◦ 「エレガントにエラーを補⾜するテクニック」[p.188]
  12. 関数の戻り値を Result 型を⽤いて表現する エラー なし 値型2 エラー あり 値型2 Result<値型2,

    Error> 値が⼊っている Result<値型2, Error> 例外となった場合 値型1 関数 f1 OR Result<値型2, Error> result = f1 (値型1) return return
  13. try / throw / catch と Result 型でのエラーハンドリングの違い エラー 値型2

    値型1 関数 f1 Result<値型2, Error> result = f1 (値型1) エラー 値型2 値型1 関数 f2 値型2 value2 = f2 (値型1) throw return return return
  14. C#で鉄道指向プログラミングを⾏うためには • C# の⾔語的な問題を克服する必要がある ◦ 直和型 / タグ付きユニオンがないので、型として Result をシンプルに表しにくい

    ◦ 同期 / ⾮同期処理 (Task) が混ざった時にシンプルな書き⽅で表現しにくい ◦ Exception を書く部分との結合が冗⻑になりがち • 既存の鉄道指向プログラミングを含むライブラリも使えるが... ◦ LanguageExt, dotNext, ErrorOr など ◦ それぞれ便利だが、欲しい機能のうちあるものはこのライブラリにあるが、 別のものはあのライブラリに、という感じで、どれを使ったらいいか悩む.... ◦ 鉄道指向プログラミングがメインではなく、他の多くの拡張機能も含まれている > ⾃作で鉄道指向プログラミングライブラリを書いてみよう!
  15. ResultBox の特徴 1. C# の⾔語的な問題への対応 ◦ パターンマッチングしやすくするために .IsSuccess (bool) で値の有無を表現している

    ◦ async / await 処理後の Task<ResultBox<T>> に対してもメソッドチェーンを使⽤出来 るようにたくさんの拡張メソッドをライブラリ内に定義している ◦ Exception を捕獲して Result に変換する、および最終的にエラーだったら それを throw する機能も含めることにより、既存のコードと混ぜやすくしている ◦ F# ⾵の関数をたくさん定義する⽅式ではなく、C# にあった Lambda と メソッドチェーンを使⽤する⽅法に合わせた書き⽅を使⽤している 2. 鉄道指向プログラミングに必要な機能が揃っており、 且つ他の機能はないため、シンプルに使える
  16. C# で鉄道指向プログラミングを⾏った感想 ◦ • 各処理 (lambda) にフォーカスすることにより、認知負荷が減った • エラー処理を含め、短いプログラムで効率的に記述できる •

    関数型プログラミングが苦⼿な⼈には読みにくいと感じる部分もあるかも • ResultBox これからの課題 ◦ 特定の Exception 型のみを catch する ◦ 関数のシグネチャでエラーをドキュメントとして扱うような機構 ◦ バリデーションなどで使⽤できるアプリカブルなアプローチ
  17. CQRS‧イベントソーシングにもマッチする Client Command Handler (Function) Event Store Projection Query Events

    Workflow CQRS Event Sourcing CQRS (コマンドクエリ責務分離) • アプリケーション (コード&モデル) を更新系 (コマンド) と参照系 (クエリ) に分離する • 「クエリとコマンドはドメインモデリングの 観点から⾒るとほとんどの場合違う」 (p.248) イベントソーシング • 「状態に変化があるたびに、その変化を表す イベントが永続化される」 (p.249) • ワークフローが出⼒するイベントをそのまま データストアに永続化する Materialized View
  18. Sekiban の紹介 • アプリケーション開発⽤フレームワーク • C# で書かれているが、F# でも利⽤可 • CQRS&イベントソーシングパターンに沿った開発

    ◦ コマンドの実⾏とイベントの⽣成 ◦ コマンド‧イベントの永続化 (Azure Cosmos DB / AWS Dynamo DB / PostgreSQL) ◦ イベントの再⽣ ◦ クエリの実⾏ • コマンド‧クエリの⾃動テストを Given-When-Then パターンで書ける テスト⽤フレームワーク • Web API エンドポイント⾃動作成
  19. 関数型ドメインモデリングを実践した感想 • ビジネスアプリケーション開発には有⽤ ◦ ドメイン駆動設計と関数型プログラミングの親和性の⾼さが実感できる ◦ 関数型プログラミングに詳しくなくても参考になる • 関数型ではない⾔語の場合、適⽤には⼯夫が求められる ◦

    ⾃作 or 外部のライブラリで効率よく書けるようにできる部分もある ◦ ⾔語によっては、ライブラリを使ってもすべてを実践できない • オブジェクト指向から関数型への思考の転換は慣れも必要 ◦ 社内でもなかなか浸透していない ◦ 最初はマルチパラダイムなオブジェクト指向⾔語で関数型っぽく書くことにも メリットがあるかも
  20. もっと詳しく Sekiban C#/F# ⽤アプリケーション開発フレームワーク GitHub https://github.com/J-Tech-Japan/Sekiban Landing Page https://www.sekiban.dev/jp X

    account (DM可) @sekibandev ResultBoxes https://github.com/J-Tech-Japan/ResultBoxes J-Tech Japan Tech Blog https://zenn.dev/p/jtechjapan_pub C# Result 型ライブラリ GitHub