🐝
Aggregateに関する理解
コントロールフロー
一言
- 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