考える場所

ココロとカラダ、思考する全部

DDDにおけるオブジェクトの関連

単方向にする

DDD には一方向の関係性が適しています。Eric Evans は、「可能な限り関係性を制限することが重要です」、そして「ドメインを理解することで自然な方向性が明らかになります」と助言しています。
- ドメイン駆動設計のコーディング: データを重視する開発者のためのヒント (第 3 部)

DDDにおいては関連を極力排除するのが正しいようだ。たとえば「伝票」と「明細」からなるaggregateについて。まずaggregate rootである「伝票」がグローバルなIDをもつ。関連は「伝票」から「明細」の向きにある。つまり「伝票」オブジェクトが「明細」オブジェクト(のコレクション)を参照する。逆はしない。「伝票」は「伝票リポジトリ」にある(リポジトリから取得する)。

class Slip {
  UUID id;
  List<Detail> details;
}
class Detail {
}
slipReposiotry.get(id);

集約はリポジトリにある

そもそも集約というのは、内部に複数のオブジェクトを抱えていても、それ自体で1個のオブジェクトとみなす考え方なので、外部の集約を含めるのは現実的ではない
- 『java - ドメインモデルの完全な状態を保つ方法 - スタック・オーバーフロー』から(Junichi Katoさん)

たとえば「伝票」に関連する「顧客」について。「顧客」は「伝票」のトランザクションには当然ないので、「伝票」をrootにするaggregateには含まれない。 「顧客」はグローバルなIDをもつまたひとつのaggregateだ。このとき、オブジェクトの関連として「伝票」に「顧客」の参照をもたせたくなるが、これをしない。「顧客」はグローバルなIDをもち「顧客リポジトリ」にある。従って「伝票」は「顧客」を識別できるIDをもつまででよい。

class Slip {
  UUID id;
  UUID customerId;
}
class Customer {
  UUID id;
}
slip = slipRepository.get(id);
customerReposiotry.get(slip.customerId);

関連を制限する

念のためではなく、特定のコンテキストで必要になることがわかっている場合にのみ、リポジトリにメソッドを配置します。
- ドメイン駆動設計のコーディング: データを重視する開発者のためのヒント (第 3 部)

「顧客」に関連する「伝票」という向きに辿りたい場合の関連を考える。「伝票」に「顧客」のIDをもつ方法がある。しかしそうはしない。疎結合(互いにIDをもつまで)ではあるが相互参照になるからだ。「伝票」は「伝票リポジトリ」にある。従って「伝票リポジトリ」に「顧客」から辿るためのメソッドを設ける方法を採る。

slipRepository.findByCustomerId(customer.id);

どちらの向きにIDをもつのか、あるいはもたないのかはユースケースに依るのだろう。

疑問

外部の集約をリポジトリから取得する役割はどこが担うのだろうか。ドメインオブジェクトがリポジトリを利用するのだろうか。リポジトリは実装上の都合であってドメインの本質ではない。従ってドメインオブジェクトがリポジトリを利用するのはよくなさそうだ。ならばドメインオブジェクトのコラボレーションをするユースケースクラスなどサービスレイヤ上のクラスが担うのだろうか。

追記(2016-05-25)

はてなブックマークにてかとじゅんさんからご意見を頂戴しておりました。

Ericさんの理論では、疑問の答えは後者であってます。ドメインモデルはユビタス言語の表現に集中する&集約は内部のオブジェクト(外部集約へのID参照は自集約の所有なので内部となる)に関心があるということなので。
- DDDに関するj5ik2oのはてなブックマーク

嬉しくて手が震えています。

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計