かとじゅんの技術日誌

技術の話をするところ

コードで学ぶドメイン駆動設計入門 〜アグリゲート編〜

コードで学ぶドメイン駆動設計入門 〜エンティティとバリューオブジェクト編〜 - じゅんいち☆かとうの技術日誌
コードで学ぶドメイン駆動設計入門 〜振る舞いとサービス編〜 - じゅんいち☆かとうの技術日誌
コードで学ぶドメイン駆動設計入門 〜ファクトリ編〜 - じゅんいち☆かとうの技術日誌
コードで学ぶドメイン駆動設計入門 〜リポジトリ編〜 - じゅんいち☆かとうの技術日誌
引き続き連投エントリ。次はアグリゲート。

実は最近まで「アグリゲートってなんだろう、、ライフサイクルの話題なのか」なって誤認識してたのですが、もう一度原書を読みなおして、やっと理解。まぁ、このパターンはすでに実施していたので、改めてこれはアグリゲートという名前かと知ったという次第。

つまるところ、アグリゲートとは、簡単にいうとライフサイクルを取り扱う境界のことですね。そもそも、Aggregateとは集約という意味があって、DDDではエンティティやバリューオブジェクトを集約し、それらの境界を定義するのがアグリゲートです。
なぜアグリゲートという概念が重要かというと、原書ではこのような下りで説明されています。

関連を最小限に抑える設計は、関連したオブジェクトの参照を辿ることを単純にし、相互関係の爆発をいくらか制限する。
しかし、ほとんどのビジネスドメインは、相互接続しており、我々は相変わらず、オブジェクト参照で長く深い経路を辿ることになる。
ある意味、この混乱ははっきりとした境界を与えることのない現実世界を反映している。それがソフトウェア設計の問題である。

つまり、ライフサイクルを扱うといっても、参照の相互接続によって依存関係が爆発しないように工夫しなければ現実的ではないということだと思います。そこでアグリゲートが登場。

アグリゲートは、内部のエンティティやバリューオブジェクトを集約している境界で、その境界はエンティティであり、ルートエンティティと呼ばれる。

  • ルートエンティティは、内部のオブジェクトへのすべてのアクセスを制御する。

内部のオブジェクトの不変条件をチェックするために、ルートエンティティが責任を持つということだと思います。

  • アグリゲートの外部のオブジェクトは、ルートエンティティだけを参照として保持できる。内部のオブジェクトは参照を保持できない。

これは外部のオブジェクトが必要としない属性は不必要に公開しないということ。

  • 内部のオブジェクトの一時的な参照を外部のオブジェクトに渡すことができる。

この場合は受け取った参照を処理が終わっても保持してはならない。この考え方はこれまで紹介してきたようにエンティティであればcloneしたインスタンスを渡した時点でこの要件を満たします。不変なバリューオブジェクトであれば、そのままのインスタンスを外部に渡しても、状態を変更できません。アグリゲートの不変条件に影響を与えないので安全です。
このアグリゲートの単位でファクトリでの生成処理やリポジトリでの永続化処理を行うということです。RDBMSに対応したリポジトリを利用する際は、アグリゲートのルートエンティティ単位でトランザクションを扱うことになるでしょう。

schema-generatorでは、ActionsImplクラスはアグリゲートにおけるルートエンティティです。エンティティは何かしらバリューオブジェクトを保持するので大体はこのパターンに合致するのではないかと思います。ちなみに、ルートエンティティの内部にあるエンティティはローカルエンティティと呼ぶようです。

また、DDDsampleという原書のExampleと連動しているプロジェクトがあって、アグリゲートであるCargo(貨物)のソースも公開されています。興味ある方はこのあたり参照
このコードを見ても分けるように、publicで公開している属性はバリューオブジェクトで短いスコープ内で利用しているようです。Cargoのdevliveryプロパティは外部に公開する必要がないので、カプセル化されています。

モジュールが残っているけど、いいかなw
多分、次はScalaでいきます。