前回の記事
【ミライトデザイン社内勉強会#18】「IDDD本から理解するドメイン駆動設計」輪読会~第9章「モジュール」~ - Qiita
実践DDD本 第10章「集約」~トランザクション整合性を保つ境界~ (1/3):CodeZine(コードジン)
境界内部のエンティティは集約内部で一意となる。
とあるが、EntitiyはIDを持っていてそもそも一意であることが担保されているんじゃないの?
- 集約内部のエンティティもグローバルで一意じゃなくなるわけではない
- その集約の内部のエンティティはそもそのグローバルからはアクセスできない
- グローバルからしてみれば存在自体知らない
- エンティティは一意という前提
- 注文詳細は外部から参照することはないので、集約内で一意になるっていう書き方をしたんだとと思う
- 逆に、集約内部のエンティティをグローバルで一意になるっていうとグローバルからアクセスできるって誤解されそうだから、この書き方をしたんじゃない?
トランザクションの衝突とはどういうこと?
巨大な集約の問題点
複数のユーザーが同時に操作した場合に、トランザクションが衝突する可能性が非常に高いためです。
上記の文章で衝突する可能性があると記しているが、具体的にどういうことか?
- トランザクションの説明
- 集約が整合性の単位なので、大きい集約だと、バックログアイテムの編集中はスプリントも変更できないことになる
- バックログアイテム、スプリントとみたいに集約が分かれるとバックログの修正中でもスプリントを修正できる
- 衝突した場合は処理を待つことになる。
id参照はエンティティの中に他のEntityのidを持ってるってわかるけど直接参照ってどういうこと?
-
直接参照は直接Entityを持ってる
- プログラム的にいえば、インスタンス化されたEntityを持っている
-
小さい集約にした場合
- 同じ集約として整合性を保っていたものを別の集約に分けた場合は、もともとは同じ集約内で依存していたので、別集約への依存を気にすることになってしまう。
- 具体的にはプロダクトエンティティが集約ルートだったので、例えばプロダクトをdeleteした場合はバックログアイテムやリリースエンティティも同じくdeleteされるように整合性を保っている。
しかし、集約を分けたことによって整合性を保つために依存を気にする必要が出てくる。- 小さい集約にした場合には、プロダクトをdeleteした時は、リポジトリでバックログもdeleteする必要がある
- 小さくしすぎると整合性の担保がしづらくなる
- 具体的にはプロダクトエンティティが集約ルートだったので、例えばプロダクトをdeleteした場合はバックログアイテムやリリースエンティティも同じくdeleteされるように整合性を保っている。
- 同じ集約として整合性を保っていたものを別の集約に分けた場合は、もともとは同じ集約内で依存していたので、別集約への依存を気にすることになってしまう。
-
集約の単位を考えるのが難しい
集約は極力、値オブジェクトから構成する
とあるが、それはなんで?値オブジェクトだけにしたほうが集約は小さくなる気はするけど、他には理由があるの?
- 「値オブジェクトだけにしたほうが集約は小さくなる気はするけど、」←値オブジェクトから構成したい理由はこれ、他に大きな理由はなさそう
- 他とのEntityの関連は値オブジェクトでIDを保持して集約が大きくなりすぎないようにしようってことじゃない?
- 集約の中にEntity持ちすぎないように、するように使用っていう話だと思う
ここでは、ルートエンティティである「Product」クラスのコード(C#/Java)を選択しています。
とあるが、BacklogItemの誤字??
- 多分、誤字
- ProductをBacklogItemと読み替えればいい
集約での依存性の注入を避けるとあるがイメージが沸かない。集約での依存性注入ってどういう状態?
-
そもそもDI(依存性の注入)とは?
- 引数として依存関係を渡してくる
class ユーザー登録() { private $userRepository; // 別にコンストラクタだけとは限らないけど public function __construct(UserRepository $repository) { $this->userRepository = $repository } }
-
集約でのDIは集約内部で使うオブジェクトを外から渡して来ている
-
集約でのDIを避けるとは?
- 小さい集約では、IDだけを持っていて必要な時にEntitiyを取得する必要がある
- その方法が、
【方式1】集約の中からリポジトリを呼び出す方式
と【方式2】集約の外(リポジトリまたはドメインサービス)から、事前に呼び出しておく方式
がある - 方法1は集約内部にリポジトリをDIで取得してきて、集約内部でEntitiyを取得する
- 集約の中でリポジトリを呼び出してしまうと、ドメインがインフラ層に依存してしまうのでなるべく避けた方がよい。
- 集約の外(リポジトリとかドメインサービスとか)でエンティティを生成し、それを引数として渡す
ユースケースを実行するユーザーが自分であれば「トランザクション整合性」を利用し、他のユーザーやシステムの役割と思える場合には「結果整合性」を使う
とあるが他のユーザーやシステムの役割とは?
p.2
- 例えば、商品を購入するして商品履歴に反映する。という処理が必要な場合、商品を購入するユーザーが実行するのでトランザクション整合性、商品履歴に反映するのはシステムの役割だから、結果整合性みたいな話だと思う
調べた単語
トランザクション整合性と結果整合性
- トランザクション整合性
- Aさんから10,000円を、Bさんに渡す
- Aから10,000円を引いた瞬間にBさんは10,000円増える
- 時間差はない
- 結果整合性
- 処理の途中で整合性があっていなくても、結果的に整合性があうこと
- Aから10,000円を引いた後にBさんは10,000円増える
- 時間差がある
- 常に整合性が取れているわけではないけど、結果的には整合性がある
- ex. 商品を購入して、すぐに購入履歴を見ても反映されてないけど、数秒後には反映されている
楽観的並行性制御
- テーブルをいじっている時(データ更新)にロックすることを悲観的ロックという。
- それに対して楽観的とは、同時に更新はされないだろうと楽観的に捉えてロックせず、バージョン番号などによって生合成を保つ制御
- 操作ごとにバージョン番号を保持していて、バージョン3の次に処理されるのはバージョン4
- バージョン3の次にバージョン5の操作がきても、バージョン4からでしか受付ない
- 操作ごとにバージョン番号を保持していて、バージョン3の次に処理されるのはバージョン4
次回
【ミライトデザイン社内勉強会#20】「IDDD本から理解するドメイン駆動設計」輪読会~第11章「ファクトリ」~ - Qiita