🐝

Aggregateに関する理解

2023/09/17に公開

コントロールフロー

一言

  • Aggregate は簡単に言うと複数のObjectをまとめるもので、まとめる基準が分かればOK

参照系

  • Aggregate は主にデータ整合性の問題を解決するための作ったものであり、参照処理で特に気にしない。言い換えると、一つのリクエストで複数の Aggregate を跨いでも、一つの Aggregate の一部を取得しても特に問題なし

更新系

  • 同じTransactionで更新しないと整合性問題が起きる場合は同じTransactionにまとめて更新する必要がある
  • そのため、同じTransactionにまとめて更新しないといけないものがAggregate になる
    • 実質、Aggregate の境界はリポジトリの実装で初めて表現できて、リポジトリで更新系の単位となるのは Aggregate になるわけ。
  • Transactionを決めればAggregate も決まる
  • Transactionを決める方法は不変条件を洗い出して確定すること
  • 不変条件を洗い出すのはユビキタス言語(複数のObjectの関係)から抽出する
    • もしAが変わったらBも変わるのような断言
    • キーワード:整合性、正常状態、連動・・・

整合性の強弱

  • 強:Transaction
    • 同じAggregate にまとめる
    • 複数のObjectを同じAggregateにまとめて同じTransactionで処理する
  • 弱:結果整合性
    • 違うAggregateに分ける
    • 違うAggregateが違うTransactionで処理するため一定の時間で整合性が失う可能性があるが、許容できる時間内で整合性が取れる
    • ユーザが許容できるのが決め手になり、分けることでパフォーマンス的にメリットが出る場合がよくある
  • 不要:ユーザがUIで手動実施
    • そもそも整合性を自動的に取る必要がない場合がある。その代わりにユーザがUIで手動実施する
    • 例:Backlogのタスクが全部完了する場合にその親ストーリーも完了にする
      • 強整合性の場合:ストーリーとタスクを同じAggregate(Transaction)で対応する
      • 弱(結果)整合性の場合:ストーリーとタスクを異なるAggregate(Transaction)で対応するが、イベント駆動によって整合性を取る
      • 不要の場合:ストーリーとタスクを異なるAggregate(Transaction)で対応する。ストーリーはタスクが全部完了した場合にユーザがストーリーを完了させる
        • この場合は、ユーザがUIなどを操作することによってシステムを介入できるのが前提
    • Ref:

  • Ref

  • aggregate の例としてよく挙げられたのは上記のリンクに書いた order and its line-items(order line) だ。

    • order と line-items は違う Entity であるが、同じ aggregate にまとめて同じ Transaction で対応しないと、一部だけ保存できて一部できないことが発生する可能性がある。そうなると、実際の order が守るべきこと(不変条件)と異なり、整合性が失ってしまう。

コードの特徴

  • aggregate と 内部 Entity 間の関係:aggregate 内部の Entity は aggregate root のクラスの内部フィールド(Kotlinの場合、Java 等の場合はメンバー変数)として扱う
class AggregetRoot {
    val id: AggregetRootId,
    val entity1: Entity1,
    val entity2: Entity2,
}
  • aggregate の間の関係:ID によって紐づく
class Aggreget1 {
    val id: Aggreget1Id,
    val entity1: Entity1,
}
class Aggreget2 {
    val id: Aggreget2Id,
    val entity2: Entity2,
    val aggreget1Id: Aggreget1Id,
}
松の風

Discussion