集約は「クラスの集まり」ではない

集約(Aggregate)という言葉を聞いたとき、多くの人は次のようなイメージを持ちます。

  • EntityとValue Objectを束ねたもの
  • 1対多の関連をまとめた構造
  • ルートEntityを持つオブジェクトグラフ

これらはすべて、集約の“形”を説明してはいますが、集約の本質を説明してはいません。

集約は、構造の話ではありません。集約とは、判断とルールを守るための単位です。どの範囲までを一つの判断として扱い、どこから先は別の判断として切り離すのか。その線引きこそが、集約を設計するという行為です。


なぜモデルを「まとめる」必要があるのか

第3章では、モデルが判断とルールを守る主体であることを説明しました。しかし、実際のシステムでは、判断は一つのモデルだけで完結しないことがほとんどです。

  • 注文は、顧客と商品と支払いに関係する
  • 予約は、日時と空き状況と制約条件に依存する

これらをすべて単一のモデルに押し込めると、モデルはすぐに破綻します。逆に、完全に分離してしまうと、「同時に守るべきルール」を保証できなくなります。

集約は、このジレンマに対する答えです。同時に守るべき不変条件を、一つの境界に閉じ込める。それが集約の役割です。


不変条件は「同時性」の問題でもある

集約を理解するうえで、重要なのは「同時性」という視点です。

  • この操作とあの操作は、同時に行われてよいのか
  • 状態が途中で食い違っても許されるのか
  • 一貫性はどこまで保証されるべきか

これらは、ビジネスルールであると同時に、トランザクションの問題でもあります。

集約の境界とは、強い一貫性を保証する範囲です。この範囲の中では、不変条件が必ず守られなければなりません。一方で、境界の外側とは、最終的に整合すればよい、という設計も選択できます。


集約ルートが持つ責務

集約には、必ず外部との窓口となる存在があります。それが集約ルートです。

集約ルートの責務は単純です。

  • 集約内部の不変条件を守る
  • 外部からの操作を一元的に受け付ける

重要なのは、「内部のオブジェクトを直接操作させない」ことです。もし外部から内部のEntityやValue Objectを自由に変更できると、不変条件は簡単に破られてしまいます。

集約ルートは、門番のような存在です。正しい手続きを踏んだ操作だけを通し、それ以外はそもそも表現できないようにします。


集約が大きくなりすぎる理由

集約設計でよくある失敗は、「安全そうだから」という理由で、あらゆるものを一つの集約に詰め込んでしまうことです。

確かに、大きな集約は不変条件を守りやすく見えます。しかし、その代償として次の問題が発生します。

  • 更新のたびに多くのデータを読み込む必要がある
  • ロック範囲が広がり、並行性が下がる
  • 変更の影響範囲が大きくなる

集約を小さくするとは、責任を放棄することではありません。どの一貫性を本当に守る必要があるのかを見極めることです。


集約を分けるという判断

集約を分けるときは、次のような判断基準で考えます。

  • 同時に変更される必要があるか
  • 不整合が一瞬でも許されないか
  • 一つの操作として扱うべきか

これらの問いに「はい」と答えられる範囲が、一つの集約になります。

それ以外の関係は、IDによる参照やイベントによる連携で十分な場合がほとんどです。


集約は設計を難しくするための概念ではない

集約は、DDDの中でも特に難解だと感じられがちな概念です。しかし、その理由の多くは、「正解を探そうとする」姿勢にあります。

集約に唯一の正解はありません。重要なのは、その集約が

  • どの不変条件を守っているのか
  • なぜその境界なのか

を説明できることです。


この章のまとめ

集約とは、モデルをまとめるための構造ではありません。集約とは、同時に守るべき判断とルールの単位です。

次章では、この集約をどのように取得し、保存し、システムの外側とつなぐのか。そのための仕組みである「Repository」について掘り下げていきます。