関数型ドメインモデリングを 非関数型のプログラミング言語で やってみた
by
Tomohisa Takaoka
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
関数型ドメインモデリングを ⾮関数型のプログラミング⾔語で やってみた
Slide 2
Slide 2 text
Opening
Slide 3
Slide 3 text
株式会社ジェイテックジャパンの紹介 ● 創業50年を超えた総合IT企業、株式会社 ジャパンテクニカルソフトウェア (JTS) のグループ企業 ● New York 所在 J-Tech Creations, Inc.と協 業 ● B2C / B2B アプリケーションを 開発‧運⽤する ● .NET‧Azure 等 Microsoft の 技術スタックを主に使⽤
Slide 4
Slide 4 text
セッションの内容 1. 書籍「関数型ドメインモデリング」 2. C# と関数型パラダイム 3. 鉄道指向プログラミング (ROP) 4. コマンドで始まりイベントで終わるワークフローと、 CQRS‧イベントソーシング
Slide 5
Slide 5 text
関数型ドメインモデリング
Slide 6
Slide 6 text
参考⽂献 ● ⽇本語タイトル 「関数型ドメインモデリング ドメイン駆動設計とF#で ソフトウェアの複雑さに⽴ち向かおう」 ● Scott Wlaschin ⽒著、猪股健太郎⽒訳 ● オリジナルは2018年発表 ⽇本語版は2024年出版 ● 株式会社ドワンゴ (アスキードワンゴ) が翻訳‧ 出版、達⼈出版会が電⼦版を販売 ※本セッションの引⽤はすべてこの書籍からのもの
Slide 7
Slide 7 text
関数型ドメインモデリングの内容 ● 「本書を書いた⽬的は、ソフトウェア設計全般、 特にドメイン駆動設計が関数型プログラミングの ⼿法からどのように恩恵を受けられるかを⽰すた め」[⽇本語版へ寄せて] ● 「本書は、(ソフトウェアの) 複雑性に対処するため に、ドメイン駆動設計と関数型プログラミングが うまく機能することを⽰す本の1つ」 [訳者まえがき] ● 「本書の⽬的は、ドメインモデリングの⼿段とし て関数型プログラミングが実際に優れており、明確 かつ簡潔な設計を⽣み出せる、と⽰すこと」 [p.1]
Slide 8
Slide 8 text
なぜ関数型ドメインモデリングに注⽬したか? ⼿続き的‧暗黙的 データの状態 (処理の結果) 宣⾔的‧明⽰的 データの遷移 (処理の流れ)
Slide 9
Slide 9 text
注⽬したポイント 「イミュータブルなデータ とイミュータブルな関数を 組み合わせた『型』により ドメインの状態遷移を 明⽰的に表現する」 [訳者まえがき] 型がドキュメントの代わりとなり、ソース コードがドキュメントとしても機能する [第5, 7章] 不正な状態は表現できず、ルールが維持さ れていることをコンパイラがチェックする [第6, 10章] データ構造ではなく、ビジネスイベントや ワークフローに焦点を当てる [第1, 7章]
Slide 10
Slide 10 text
C# と関数型パラダイム
Slide 11
Slide 11 text
C# について ● Microsoft が開発したプログラミング⾔語 ● .NET ランタイム上で動作する ● .NET は OSS & クロスプラットフォーム 最も活発なオープンソースプロジェクトの⼀つ ● 基本はオブジェクト指向⾔語 マルチパラダイムでもある ● 現在の⾔語バージョンは 12.0 (13.0 はプレビュー版) ● 2017年 (ver 7.0) 以降は毎年バージョンアップ
Slide 12
Slide 12 text
C# がサポートする関数型パラダイムの要素 ● ラムダ式 ● 第⼀級関数 ○ ⾼階関数 ○ 関数合成 ○ 部分適⽤ ○ カリー化 ● パターンマッチング ● イミュータビリティ 関数型⾔語と⽐較すると 完全ではない部分もある
Slide 13
Slide 13 text
C# がサポートしない関数型パラダイムの要素 関数合成演算⼦‧パイプライン演算⼦ ● 拡張メソッドとメソッドチェーンで代⽤ Option 型 ● 組み込みの型はないが、⾃分で実装は可能 ● LanguageExt などのライブラリも利⽤可能 直和型 / タグ付きUnion(判別共⽤体) ● 関数型っぽく書くための最⼤の障壁かもしれない ● 提案はされている(実装はまだ開始されていない)
Slide 14
Slide 14 text
C# で関数型っぽく書くために ● データを record 型で定義し、イミュータブルにする ○ データに振る舞いを持たせない ○ データに状態を持たせない ● 関数を静的 (static) メソッドで定義する ○ トップレベルに関数を直接書けないので ● メソッドチェーンでパイプライン処理を書く ● Unit 型を定義し、void の代わりにメソッドの戻り値として使う ○ デリゲートを全て Func 型に統⼀できる ● Option 型を定義し、Nullable な型に代えて使う ● Result 型を定義し、例外をスローせずに返す
Slide 15
Slide 15 text
鉄道指向プログラミング Railway Oriented Programming
Slide 16
Slide 16 text
鉄道指向プログラミング (ROP) とは ● 著者の Scott Wlaschin ⽒が提唱 ● エラーハンドリングの⼿法 ● プログラムの処理を鉄道のレールに⾒⽴てる ● 処理の流れを線路、エラーを分岐点と捉える ● 正常な処理が続く場合は⼀本のレールを進み、 エラーが発⽣した場合は別のレールに分岐するという形で表現 ● ⼀旦分岐した線路は、元の線路に戻ることがない > パイプラインのどこかで例外が返されたら、残りのステップを回避する
Slide 17
Slide 17 text
鉄道指向プログラミングの特徴 1. 関数の戻り値を Result 型を⽤いて表現する ○ 成功時は成功の値を、失敗時はエラー情報を返す 成功か失敗のどちらか⽚⽅を戻す (直和型 / タグ付きユニオンを使った実装が理想) ○ ⼿続き型⾔語で⼀般的な例外を try - throw - catch で扱う形と⽐較して、 「エラーをドメインエラーとして扱い、ドメイン駆動設計の他の部分と同じように 注意を払う」扱い⽅ [p.188] 2. Result 型を戻す複数の関数をパイプラインで接続する ○ 成功処理とエラー処理に処理を分離する ○ 処理の流れが決まっているため、エラー処理の記述がシンプルになる ○ 「エレガントにエラーを補⾜するテクニック」[p.188]
Slide 18
Slide 18 text
関数の戻り値を Result 型を⽤いて表現する エラー なし 値型2 エラー あり 値型2 Result<値型2, Error> 値が⼊っている Result<値型2, Error> 例外となった場合 値型1 関数 f1 OR Result<値型2, Error> result = f1 (値型1) return return
Slide 19
Slide 19 text
try / throw / catch と Result 型でのエラーハンドリングの違い エラー 値型2 値型1 関数 f1 Result<値型2, Error> result = f1 (値型1) エラー 値型2 値型1 関数 f2 値型2 value2 = f2 (値型1) throw return return return
Slide 20
Slide 20 text
Result型を戻す複数の関数をパイプラインで接続する エラー 値型2 値型1 関数 f1 return return エラー 値型3 関数 f2 return return Bind エラー 値型4 関数 f3 return return Bind
Slide 21
Slide 21 text
鉄道指向プログラミングの動作の図解 パターン1:全て成功して、最終的に 値型4 を返す
Slide 22
Slide 22 text
鉄道指向プログラミングの動作の図解 パターン2:最初の関数 f1 で エラー1 となる場合
Slide 23
Slide 23 text
鉄道指向プログラミングの動作の図解 パターン3:2つ⽬の関数 f2 で エラー2 となる場合
Slide 24
Slide 24 text
鉄道指向プログラミングの動作の図解 パターン4:3つ⽬の関数 f3 で エラー3 となる場合
Slide 25
Slide 25 text
ROP でパイプライン処理をシンプルに表現できる
Slide 26
Slide 26 text
ROP でパイプライン処理をシンプルに表現できる
Slide 27
Slide 27 text
C#で鉄道指向プログラミングを⾏うためには ● C# の⾔語的な問題を克服する必要がある ○ 直和型 / タグ付きユニオンがないので、型として Result をシンプルに表しにくい ○ 同期 / ⾮同期処理 (Task) が混ざった時にシンプルな書き⽅で表現しにくい ○ Exception を書く部分との結合が冗⻑になりがち ● 既存の鉄道指向プログラミングを含むライブラリも使えるが... ○ LanguageExt, dotNext, ErrorOr など ○ それぞれ便利だが、欲しい機能のうちあるものはこのライブラリにあるが、 別のものはあのライブラリに、という感じで、どれを使ったらいいか悩む.... ○ 鉄道指向プログラミングがメインではなく、他の多くの拡張機能も含まれている > ⾃作で鉄道指向プログラミングライブラリを書いてみよう!
Slide 28
Slide 28 text
ResultBox:鉄道指向プログラミングライブラリ C# で鉄道指向プログラミングを 書きやすくする ResultBox というオープンソースライブラ リを作成しました! 鉄道より規模を少し⼩さくして、 箱の移動のメタファーを使⽤ ● Conveyor ● UnWrapBox ● Combine ● Scan etc... https://github.com/J-Tech-Japan/ResultBoxes
Slide 29
Slide 29 text
ResultBox の特徴 1. C# の⾔語的な問題への対応 ○ パターンマッチングしやすくするために .IsSuccess (bool) で値の有無を表現している ○ async / await 処理後の Task> に対してもメソッドチェーンを使⽤出来 るようにたくさんの拡張メソッドをライブラリ内に定義している ○ Exception を捕獲して Result に変換する、および最終的にエラーだったら それを throw する機能も含めることにより、既存のコードと混ぜやすくしている ○ F# ⾵の関数をたくさん定義する⽅式ではなく、C# にあった Lambda と メソッドチェーンを使⽤する⽅法に合わせた書き⽅を使⽤している 2. 鉄道指向プログラミングに必要な機能が揃っており、 且つ他の機能はないため、シンプルに使える
Slide 30
Slide 30 text
ResultBox で書いたプログラム (例1) ※ Conveyor : ResultBox における bind
Slide 31
Slide 31 text
ResultBox で書いたプログラム (例2)
Slide 32
Slide 32 text
C# で鉄道指向プログラミングを⾏った感想 ○ ● 各処理 (lambda) にフォーカスすることにより、認知負荷が減った ● エラー処理を含め、短いプログラムで効率的に記述できる ● 関数型プログラミングが苦⼿な⼈には読みにくいと感じる部分もあるかも ● ResultBox これからの課題 ○ 特定の Exception 型のみを catch する ○ 関数のシグネチャでエラーをドキュメントとして扱うような機構 ○ バリデーションなどで使⽤できるアプリカブルなアプローチ
Slide 33
Slide 33 text
コマンドで始まりイベントで終わる ワークフローと、 CQRS‧イベントソーシング
Slide 34
Slide 34 text
ビジネスイベントやワークフローに焦点を当てる 「データ構造ではなく、ビジネスイベントやワークフ ローに焦点を当てる」 [第1章] なぜ? ● 「ビジネスは単にデータを持っているだけではな く、何らかの⽅法でデータを変換する」 ● 「典型的なビジネスプロセスは、データやドキュ メントの変換の連続、ビジネスの価値はこの変換 の過程で⽣み出される」 ● 「使われずに放置されている静的なデータは、役 に⽴たない」
Slide 35
Slide 35 text
ビジネスプロセスとワークフロー [関数型ドメインモデリング p.15]
Slide 36
Slide 36 text
ワークフローは、コマンドで始まり、イベントで終わる [関数型ドメインモデリング p.15] ● 「ワークフローは1つの関数にマッピングされる」 [p.48] ● 「ワークフローへの⼊⼒は、常にコマンドに関連するデータ」 [p.48-49] ● 「ワークフローの出⼒は、ワークフローが⽣成するイベント」 [p.48-49] > 関数型プログラミングの仕組みと⾒事にマッチする
Slide 37
Slide 37 text
CQRS‧イベントソーシングにもマッチする Client Command Handler (Function) Event Store Projection Query Events Workflow CQRS Event Sourcing CQRS (コマンドクエリ責務分離) ● アプリケーション (コード&モデル) を更新系 (コマンド) と参照系 (クエリ) に分離する ● 「クエリとコマンドはドメインモデリングの 観点から⾒るとほとんどの場合違う」 (p.248) イベントソーシング ● 「状態に変化があるたびに、その変化を表す イベントが永続化される」 (p.249) ● ワークフローが出⼒するイベントをそのまま データストアに永続化する Materialized View
Slide 38
Slide 38 text
Sekiban の紹介 ● アプリケーション開発⽤フレームワーク ● C# で書かれているが、F# でも利⽤可 ● CQRS&イベントソーシングパターンに沿った開発 ○ コマンドの実⾏とイベントの⽣成 ○ コマンド‧イベントの永続化 (Azure Cosmos DB / AWS Dynamo DB / PostgreSQL) ○ イベントの再⽣ ○ クエリの実⾏ ● コマンド‧クエリの⾃動テストを Given-When-Then パターンで書ける テスト⽤フレームワーク ● Web API エンドポイント⾃動作成
Slide 39
Slide 39 text
コード例
Slide 40
Slide 40 text
コード例
Slide 41
Slide 41 text
コード例
Slide 42
Slide 42 text
Closing
Slide 43
Slide 43 text
関数型ドメインモデリングを実践した感想 ● ビジネスアプリケーション開発には有⽤ ○ ドメイン駆動設計と関数型プログラミングの親和性の⾼さが実感できる ○ 関数型プログラミングに詳しくなくても参考になる ● 関数型ではない⾔語の場合、適⽤には⼯夫が求められる ○ ⾃作 or 外部のライブラリで効率よく書けるようにできる部分もある ○ ⾔語によっては、ライブラリを使ってもすべてを実践できない ● オブジェクト指向から関数型への思考の転換は慣れも必要 ○ 社内でもなかなか浸透していない ○ 最初はマルチパラダイムなオブジェクト指向⾔語で関数型っぽく書くことにも メリットがあるかも
Slide 44
Slide 44 text
もっと詳しく 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