All Articles

functional domain modeling 1

メモ

データ構造よりビジネスイベントに焦点を当てる

ドメインイベント → あるデータを処理して付加価値をつけ始める起点
ex. 「新しい注文フォームを受け取りました」は、注文受付プロセスを開始するドメインイベントです
ドメインイベントは常に過去形で書かれる —何かが起こった— それは変更できない事実

コマンド → ドメインイベントの発生理由となる命令 コマンドが成功すると、ワークフローが開始され、対応するドメインイベントが作成される。   ex. コマンドが「Xを発生させる」の場合、ワークフローがXを発生させると、対応するドメインイベントは「Xが発生しました」になります。
「顧客ABCに貨物を送る」; ドメインイベント:「出荷が送信されました。」

データベースの観点から常に設計する場合、データベースモデルに合うように設計を歪めることになりがち ドメインから作業し、特定のストレージ実装に関係なくモデル化することを進める DB なしで interface だけ切って開発してみたら面白そう

クラスに設計を駆動させることは、データベースに設計を駆動させることと同じくらい危険な場合がある。繰り返しになるが、要件に耳を傾けていないから。
例えば注文の基底となる OrderBase というものを作ったとすると、それがドメインの専門家がわからない以上、設計がよくない。ドメインの歪みとなる。
オブジェクトの観点からも考えない。あくまでドメインから考える。
逆に永続化層が色んな状態をもって複雑になってしまっていてもアプリケーション側でうまく設計を作ることも全然間に合うということ。

優れたアーキテクチャの目標の1つは、コンテナ、コンポーネント、およびモジュール間のさまざまな境界を定義すること

真に分離されたマイクロサービスアーキテクチャを作成するのは難しい。
マイクロサービスの1つをオフにして他の何かが壊れた場合、実際にはマイクロサービスアーキテクチャではなく、分散モノリスである。
これがマイクロサービスの定義だろうけど、実現するのとても難しいと思う。

どちらかというと、再実行とかでマイクロサービスとしての形をうまく保つための工夫が必要不可欠なんだなと思った。

お互いに依存しないためには、キューイングをしてイベントで動くようにしとかないといけない。
キューに渡すデータは、DTO をドメインから作り、DDD の中では DTO を送信する。つまり送信側は、Domain → DTO のマッピング処理を境界内に持つ。
DTO は json や xml などに serialize されて、キューに運ばれる。

DDDの用語では、永続的なIDを持つオブジェクトはエンティティと呼ばれ、永続的なIDを持たないオブジェクトは値オブジェクトと呼ばれる。
「値オブジェクト」と「エンティティ」の違いは、コンテキストによって異なる 故に、モデルは 値オブジェクト <-> エンティティを行き来する ex.) 携帯電話 製造時に、各電話には一意のシリアル番号(一意のID)が割り当てられるため、そのコンテキストでは、電話はエンティティとしてモデル化される
販売されている場合、シリアル番号は関係ありません。同じ仕様のすべての電話は交換可能であり、値オブジェクトとしてモデル化できる。
特定の電話が特定の顧客に販売されると、IDは再び関連するようになり(商品なので管理するIDがあるだろう)、エンティティとしてモデル化する必要がある

  • 値オブジェクトの例は、名前、住所、場所、お金、日付
  • エンティティの例は、顧客、注文、製品、および請求書

エンティティのコレクションは、アグリゲート アグリゲート をもつ最上位のエンティティは アグリゲートルート と呼ばれる アグリゲートは整合性の境界として機能する。
アグリゲートの一部が更新されると、整合性を確保するために他の部分も更新する必要がある場合がある

例えば、注文に注文明細(一つ一つの行)というアグリゲートがあったときに、注文明細の一部が変更されたら
注文の合計価格は変わる

アグリゲートは永続性の基本単位。
データベースからオブジェクトをロードまたは保存する場合は、集計全体をロードまたは保存する必要がある。
各データベーストランザクションは単一のアグリゲートで機能する必要があり、
複数のアグリゲートを含めたり、アグリゲートの境界を越えたりしないようにする

ドメインのエラーもアグリゲートやドメインタイプで分かれていると扱いやすい

type PlaceOrderError =
| ValidationError
| ...//その他のエラー

信頼できるドメインの2つの側面である整合性と一貫性のモデリング すべてのデータ値が常に有効であると確信できる場合、実装はクリーンな状態を維持でき、防御的なコーディングを行う必要がなくなる。
あちこちでモデルの検証を行わなくて済むようにしたい。

顧客の Email アドレスも未検証、検証という状態をもつなら異なる値オブジェクトで管理する

type CustomerEmail = 
| Unverified of EmailAddress
| Verified of EmailAddress

CustomerEmail に isVerified のようなフラグを持たせない
意図しない操作が行われないようにするため

ワークフロー = ビジネスプロセスのパイプライン
パイプラインは複数の小さなパイプから成る
そのパイプごとに変換が行われ、それらを接着して大きなパイプラインが構成される
このスタイルのプログラミングを変換指向プログラミングと呼ぶ

関数型プログラミングの原則に従って、パイプラインの各ステップがステートレスで副作用がないように設計されていることを確認する。つまり、各ステップを個別にテストして理解することができる。

F#

// B を受けて A の型を返す
type A = A of B