Aggregate Root とは
はじめに
DDD(Domain-Driven Design)の文脈で頻繁に登場する Aggregate Root(集約ルート) について解説していきます。
本記事では、
- Aggregate / Aggregate Root とは何か
- なぜこの概念が必要なのか
- 設計時の注意点
を整理します。
Aggregate(集約)とは
Aggregate とは、
- 関連する複数の Entity / Value Object を
- 一つのまとまり(整合性の境界) として扱う
DDD の設計単位です。
重要なのは、
Aggregate は「常に一貫した状態を保つべき単位」
という点です。
Aggregate Root(集約ルート)とは
Aggregate Root とは、
- Aggregate の中で
- 外部から直接参照・操作してよい唯一の Entity
を指します。
Aggregate 内には複数の Entity が存在することがありますが、
- 外部のコードは Aggregate Root 経由でしか操作できない
- 内部の Entity は、Root に守られた存在
という関係になります。
具体例(イメージ)
注文ドメインの例
- Order(注文) ← Aggregate Root
- OrderItem(注文明細)
この場合、
- OrderItem を単体で保存・取得しない
- OrderItem の追加・削除は 必ず Order を通して行う
というルールを設けます。
class Order(
val id: OrderId,
private val items: MutableList<OrderItem>
) {
fun addItem(item: OrderItem) {
// 整合性チェック
items.add(item)
}
}
OrderItem は Order の内部状態であり、
- 勝手に変更されること
- 不正な状態になること
を Aggregate Root が防ぎます。
なぜ Aggregate Root が必要なのか
① 整合性を守るため
もし内部 Entity を直接操作できると、
- 制約チェックが漏れる
- 状態が壊れる
といった事故が起こりやすくなります。
Aggregate Root は、
- 状態変更の入口を一箇所に集約
することで、これを防ぎます。
② トランザクション境界を明確にするため
DDD では、
- 1 トランザクション = 1 Aggregate
が原則です。
本記事でいう「トランザクション」は、
DB の BEGIN / COMMIT を指すものではなく、
ビジネス的に一貫性が保たれるべき操作単位 を意味します。
Aggregate Root を単位にすれば、
- どこまでを一括で保存するか
- どこからを別トランザクションにするか
が自然に決まります。
③ 依存関係をシンプルにするため
外部コードが
- Root だけを知っていればよい
状態になるため、
- 依存が減る
- 設計が読みやすくなる
というメリットがあります。
設計時の注意点
① Aggregate を大きくしすぎない
「関連しているから」という理由だけで
- 何でも一つの Aggregate に詰め込む
のは危険です。
- 更新頻度
- トランザクションの必要性
を基準に分けるのがポイントです。
② 他 Aggregate を直接参照しない
Aggregate 間の参照は、
- ID 参照のみ
に留めるのが原則です。
class Order(
val customerId: CustomerId
)
Customer オブジェクトを直接持たないことで、
- 境界が崩れる
- トランザクションが肥大化する
のを防ぎます。
③ Repository は Aggregate Root 単位
Repository は、
- Aggregate Root の取得・保存のみを責務とする
のが基本です。
interface OrderRepository {
fun findById(id: OrderId): Order?
fun save(order: Order)
}
内部 Entity 用の Repository を作らない点が重要です。
よくある誤解
- Entity = Aggregate Root ではない
- テーブル = Aggregate ではない
- 画面単位で Aggregate を決める
Aggregate は、
ビジネスルールと整合性の単位
から導かれるものです。
まとめ
- Aggregate は「整合性の境界」
- Aggregate Root は「外部との唯一の窓口」
- 状態変更・保存・取得は Root 経由
この概念を意識することで、
- ドメインモデルが壊れにくく
- 変更に強い設計
になります。
今後、UseCase や Repository を設計するときも、
「これはどの Aggregate Root を操作しているか?」
を常に意識していきたいところです。