関数型DDDの実践本として話題の『Domain Modeling Made Functional』を読んで個人的にメモったDDDのエッセンス
誰向けの本か?
- 型と関数のみを使用してドメインをモデル化および実装する方法を知りたいと考えている人
- ドメイン駆動設計について簡単に説明し、オブジェクト指向設計やデータベースファースト設計との違いを知りたいと考えている人
- あなたは経験豊富なドメイン駆動設計の実践者で、DDD が関数型プログラミングに最適である理由を知りたいと考えている人
- 関数型プログラミングについて学びたいと思っているが、理論や抽象化が多すぎて先延ばしになっている人で、関数型プログラミングを現実世界のドメインにどのように適用できるかを知りたいと考えている人
内容
この本は 3 つの部分に分かれている
DDDの目的
ソフトウェアで扱うモデルをビジネスドメインに合わせること
こうなることで
- 市場投入までの時間が短縮
- 顧客の満足度を高め、軌道から外れる可能性を減らす
- 誤解ややり直しによる時間が減る
- メンテナンスと進化が容易
本番環境にリリースされるのは、ドメインの専門家の理解ではなく、開発者の理解によるソースコードである
DDDコミュニティの4つのガイドラインについて
- データ構造ではなく、ビジネスイベントとワークフローに焦点を当てる
→ビジネスはデータを持っているだけではなく、それを何らかの方法で変換する
→ビジネスの価値はこの変換のプロセスで生み出されるため理解が大事 - 問題のドメインをより小さなサブドメインに分割する
→ドメインとは「ドメインの専門家が専門とするものである」と定義
→ドメインの「問題空間」と「解決空間」を区別し、別のものとして扱う
→問題空間のサブドメインが解決空間のサブシステム(境界付きコンテキスト)にマッピングされる
→コンテキストを見つけるのは難しい、コンテキストマップなどを活用 - ソリューション内の各サブドメインのモデルを作成する
- プロジェクトに関係する全員の間で共有され、コード内のあらゆる場所で使用される共通言語 (「ユビキタス言語」として知られる) を開発する
データベース駆動設計、クラス駆動設計をしたいという衝動と闘う
- データベース駆動設計ではドメインを正確にモデル化することは難しい、データベースの観点から設計を歪めてしまう
→DDDでは永続性を無視して考える(永続性無視) - クラス駆動設計では現実世界には存在しないクラス〇〇BaseClassなどが出現し、ドメインを歪めてしまう
ドメインのドキュメント化はどうする?
まず以下のような感じでワークフローを文章化すると良い
- 境界付きコンテキスト:注文の受注
- ワークフロー:注文する
- トリガー:「注文フォーム受信」イベント(見積チェックなしの場合)
- 主なインプット:注文フォーム
- 他のインプット:製品カタログ
- アウトプットイベント:「注文完了」イベント
- 副作用:注文したものと一緒に確認通知が顧客に送信される
そして、ワークフローに関連付けられたデータを次のように文章化する
- 境界付きコンテキスト:注文の受注
- データ:注文 =
顧客情報
AND 配送先住所
AND 請求先住所
AND 注文明細 のリスト
AND 請求額- データ:注文明細 =
製品コード
AND 数量
AND 価格- データ:顧客情報 = ??? // まだわからない
- データ:請求先住所 = ??? // まだわからない
ただ、これは簡単すぎる
注文にはライフサイクルがあり、「未検証の注文」と「検証済みの注文」が少なくとも存在するとすると
- 境界付きコンテキスト:注文の受注
- データ:未検証明細 =
未検証の顧客情報
AND 未検証の配送先住所
AND 未検証の請求先住所
AND 未検証の注文明細 のリスト- データ:未検証の注文明細 =
未検証の製品コード
AND 未検証の数量- データ:検証済み明細 =
検証済み顧客情報
AND 検証済み配送先住所
AND 検証済み請求先住所
AND 検証済み注文明細 のリスト- データ:検証済み注文明細 =
検証済み製品コード
AND 検証済み数量
さらに、価格計算をした後の「価格が付いた注文」が存在するので以下の文章化もする
- 境界付きコンテキスト:注文の受注
- データ:検証済み明細 =
検証済み顧客情報
AND 検証済み配送先住所
AND 検証済み請求先住所
AND 検証済み注文明細 のリスト
AND 請求額- データ:価格付き注文明細 =
検証済み製品コード
AND 検証済み数量
AND 明細価格
最後はこの価格付き注文を承認する
- 境界付きコンテキスト:注文の受注
- データ:受注承認 =
価格付き注文
AND 承認書
このようにドメインを少し構造化した方法で捉える
ドメインの専門家に見せて一緒に作業ができるかどうか
境界付きコンテキスト間の通信
コンテキスト間の通信はイベントを使用する
イベントを送信するためのメカニズムは、選択したアーキテクチャによって異なる
マイクロサービスまたはエージェントを使用したアーキテクチャの場合は非同期キューイングを使用することになり、モノリシックシステムでは同じキューイングアプローチを内部で使用してもいいし、関数呼び出しを介して上流コンポーネントと下流コンポーネントの間を直接リンクしてもいい
このときデータ転送も必要であるので、DTOやシリアライズ、デシリアライズについても検討する必要がある
境界のあるコンテキスト間の契約
コンテキスト間の通信を成功させるには、2つのコンテキストが共通の契約形式に同意する必要がある
関係を表す一般的な用語
- 共有カーネル関係:通信契約を共同で所有する(DTOなどの定義変更の影響を受ける)
- コンシューマ/サプライヤー関係またはConsumer Driven Contract:下流コンテキストが上流コンテキストに供してほしい契約を定義する
- 適合主義的な関係:下流コンテキストは、上流コンテキストによって提供されたコントラクトを受け入れる
全くドメインが一致しない外部サービスとの通信には「腐敗防止層(ACL)」を設けて内部のドメインモデルが破損するのを防ぐ
これらのコンテキスト間の契約関係をコンテキストマップに図示しておくと良い