ドメイン駆動設計(DDD)で設計をしていると、次のような疑問に必ずぶつかります。
- このエンティティは同じ集約に入れるべきか?
- それとも別の集約として切り離すべきか?
集約の境界は、DDDにおいて非常に重要な設計判断です。 この記事では、実務でよく使われる判断基準を整理します。
1. 不変条件を同時に守る必要があるか(最重要)
最も重要な基準は 不変条件(Invariant) です。
「このルールは常に同時に成立している必要があるか?」
- YESなら 同じ集約
- NOなら 別集約
例
注文と注文明細
Order
└ OrderItem
不変条件
- 注文の合計金額 = 各注文金額の合計
- 注文明細の数量 ≥ 1
数量が変われば、注文全体の合計金額は変わります。
つまり、これらは 常に同時に成立している必要があります。
そのため Order と OrderItem は 同じ集約になります。
別集約になる例
Order
Inventory (在庫)
ルール
注文が確定したら在庫を減らす
この処理は
- 注文確定
- 在庫更新
の2つの処理に分かれます。
これらは 同時トランザクションでなくても問題ないため
Order集約
Inventory集約
と分けます。
2. ライフサイクルが一緒か
次に考えるのは ライフサイクルです。
「一緒に生まれて一緒に消えるか?」
- YES → 同じ集約
- NO → 別集約
例
Order
└ OrderItem
OrderItem は
Orderが作られるときに作られるOrderが削除されるときに削除される
つまり 独立した存在ではないため、同じ集約に含まれます。
別集約の例
User
Order
Order は
Userが削除されても履歴として残る
このように ライフサイクルが独立しているため、別集約になります。
3. 集約は他の集約をIDで参照する
DDDでは次のルールがよく使われます。
集約は他の集約をIDで参照する
OK
Order
└ userId
NG
Order
└ Userオブジェクト
集約間はオブジェクト参照ではなく ID 参照にします。 これは「IDで参照できるなら別集約」という意味ではなく、 「別集約である場合は ID で参照する」というルールです。
このルールを守ることで、集約間の結合度を下げることができます。
具体例は別の記事 (DDDで「集約はIDで参照する」とはどういう意味か) を参照してください。
4. 同時に更新されるか
もう一つの判断基準は 更新タイミングです。
「この2つはいつも一緒に更新されるか?」
- YES → 同じ集約の可能性
- NO → 別集約
例
Order
Payment
多くのシステムでは
- 注文作成
- 決済
は別タイミングで行われます。
そのため
Order集約
Payment集約
のように分けることが多いです。
5. 集約は小さく保つ
DDDではよく次の原則が言われます。
集約は小さく保つ
巨大な集約を作ると
- トランザクションが重くなる
- ロック競合が増える
- スケールしにくくなる
といった問題が起きます。
そのため、迷った場合は 小さく分ける方向で設計することが多いです。
まとめ
集約を設計する際は、次の質問を自分に投げかけると整理しやすくなります。
- この不変条件は同時に守る必要があるか?
- 一緒に生まれて一緒に消えるか?
- 片方だけ更新されるケースはあるか?
- トランザクションを分けても問題ないか?
特に重要なのは次の考え方です。
「どの不変条件をトランザクションで守るか」
これが、そのまま 集約の境界になります。
集約設計はDDDの中でも難しい部分ですが、この視点を持つと設計の判断がしやすくなります。