集約で境界を正しく表現する意味


集約

DDDでは変更の単位として扱われ、言葉の意味からはオブジェクトの集合の意味が強く感じられますが
その真の意味はもう少し深いところにあります

まずは、定義からみていきます

集約の定義

集約は不変条件を維持する単位として切り出されたエンティティと値オブジェクトの集合であり
集約には境界とルートが存在します

不変条件

ある処理の間、その真理値が真のまま変化しない述語 by Wiki

値オブジェクトの意味する不変のimmutableではなくinvariantが原訳としては正しいです
不変条件というとややこしいですが、「制約条件のような一貫性のルール」でビジネスルールと捉えて良いでしょう

境界

集約に何が含まれるのかを定義するためのまさしく境界です

ルート

集約に含まれる特定のオブジェクトです
原則的に集約の保持するフィールドの操作はこのルートが一任します
ルートのみが変更を行うことで不変条件を保ちます

ルートが操作を一任するという点で「デメテルの法則」に則ったコーディングが求められます

デメテルの法則

デメテルの法則によると、メソッドを呼び出すオブジェクトは次の4つに限定されます

  • オブジェクト自身
  • 引数として渡されたオブジェクト
  • インスタンス変数
  • 直接インスタンス化したオブジェクト

集約の不変条件からも重要なことはもちろん結合度を下げて保守性を上げる効果も期待できます

集約の原則

  • 境界を定めて、変更の単位をまとめる
  • 不変条件を正しく表現する

境界を定めて、変更の単位をまとめる

後述の「集約の単位」でも記載していますが、トランザクションの単位としてまとめることが求められます
この原則からずれるとデータロックの原因になりパフォーマンス劣化になります
また、集約の一部検証がうまく行えないことで処理が失敗する可能性も高くなってしまいます

不変条件を正しく表現する

Serviceクラスなどに処理が点在することで「ドメインモデル貧血症」を引き起こし、のちの変更コストが増大します
正しく不変条件が実装されないことでソフトウェアとしての曖昧さが高くなりバグの温床になります

また、不変条件というのは単語的な表現です
エンティティ・値オブジェクトが持つ情報をそのまま表現するのではなく、ユビキタスな表現を行うことでよりビジネスルールを明確にします
例えば、車であれば右に曲がるとき「ハンドルを右にきる」のような表現をします
(ステアリングが…タイロッドが…といった詳細は集約で隠蔽化されるべきです)

集約の単位

集約の単位はより小さくより扱いやすくなどの観点で決めがちですが、「トランザクションの単位」というのがわかりやすい方針ではないでしょうか
何となく決まるものと言うよりは「ビジネスルールに寄り添った単位」になるのが自然です

集約例

賃貸住宅という集約のルートを考えた時に以下の考え方ができます
「家には複数人住める」 「入居者のなかに代表の契約者がいる」 「退去時にはすべての住人がいなくなる」

    

代表の契約者がいないのに住人がいたり、退去時に子供だけ残すといったことはないはずなのです
また、入居者の身辺状況や名前といった情報は賃貸住宅において直接操作したい内容ではないのです

つまり、入居者エンティティは別にあるが賃貸住宅が抱えるものではなく
賃貸住宅は入居者エンティティのID郡を保持している状態が自然と言えそうですね

【賃貸住宅】
- 住所
- 契約者ID
- 入居者ID

【入居者】
- ID
- 名前
- 生年月日

集約のメリット

ここまで仰々しいことをするメリットは何なのでしょうか?

集約を正しく扱うことでデメテルの法則で掲げているようなソフトウェアとしての保守性向上はもちろん
集約オブジェクトが雄弁にビジネスルールを語りだすようなコードになっていくのが理想です

DDDの本質は「何らかの問題を解決する」ための方法論なので問題解決を早く行うために
ビジネスの実態を表現して継続的な改善に対する不確定要素を最小にすることではないかと思います

参考

  1. 成瀬 允宣. ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本
  2. ボトムアップドメイン駆動設計 後編

その他

レイヤードアーキテクチャの視点
Serviceクラスの意義と勘所
ValueObjectという考え方