こんにちわ。DDD初学者のものです。
DDDにおける1集約=1トランザクションの制約において、結果整合性をどう実装すれば良いかずっと悩んでおりまして、質問させてください。
前提
仕事集約: id, title, point, userID
口座集約: id, userID, value
があったとして、仕事を依頼する際に、口座の全残高を確認し、仕事完了時にお支払いするお金(仕事集約のpoint)が全残高より少ない場合、仕事依頼を行うことができ、かつ、その時点で口座からpoint分のお金を引くという処理を実装したいと思っています。
また、ドメインイベントを利用して、仕事集約内で非同期でイベントpublish(pointを送信)して口座集約の更新を行うことを考えています。(ESは導入しません。こちらの記事の「イベントの発信と受信の流れ」の一番左のイメージ[https://codezine.jp/article/detail/10392])
この時、DDDにおける1集約=1トランザクションの原則に則って、コードを書く場合、どうすれば良いか悩んでいます。
悩んでいるポイント
仕事集約ではcommitできたが、口座集約ではerrorになった場合、お金を払っていないのに、仕事を依頼することができてしまう。
口座集約ではcommitできたが、仕事集約ではerrorになった場合、お金を払ったのに、仕事を依頼することができない。
検討した解決策1
仕事集約: id, title, point, userID
口座集約: id, userID, value, workID
のように、口座集約にworkIDをもたせる。
そして、口座を参照する際に、口座集約のworkIDが仕事集約に存在するか確認し、仕事集約に存在する場合、有効な口座引き落としと判断する。そのようなデータのみ返却する。(仕事集約を参照する際もしかり。口座集約にworkIDが存在するか確かめて返却する)
こうすれば、仕事集約がerrorになり、口座集約がcommitできた場合でも、そういったデータは参照することができないようになります。
結果として口座集約に不要なゴミデータが残りますが、readにおいて整合性は保たれた状態となります。(readで整合性を担保する)
検討した解決策2
仕事集約: id, title, point, userID, key
口座集約: id, userID, value, key
のように各集約にkeyをもたせる。
keyには同一の値が格納されるものとする。
この時、1分おきに仕事集約と口座集約でkeyのペアが存在しているか確認するワーカーを起動させておき、keyのペアが存在しないデータは削除する。
この仕組を実装すれば、仕事集約でcommitできたが、口座集約ではerrorとなった場合、一時的に不整合な状態となるが、ワーカーが起動すれば仕事集約のデータは削除されるので、整合性が保たれた状態になります。
質問
今回の仕様において、DDDにおいて整合性を保つために「検討した解決策1,2」のどちらを採用するべきでしょうか。
検討した解決策1は簡単に実装できますが、ゴミデータが残るし、参照時に必ず2つの集約を参照して取得する必要があるという、暗黙の了解が生まれる気がして、別の開発者が触った場合、バグになる可能性があるのではないかという懸念があります。
また、解決策2の場合、keyという「実装都合によるデータ」を集約に持たせる必要があり、DDDをやる上でそういったことはしたくないなと思ったりします。(解決策1のworkIDの場合、集約を口座代帳という概念にし、workIDは摘要と考えれば説明つくなと考えています。)
その上解決策2はワーカーを実装する必要があり、実装も解決策1に比べて難易度があがる気がしています。
みなさんならこういった仕様の場合どう実装されますでしょうか?解決策1、2どちらをとりますか?
それともマイクロサービスでいうsagaパターンや、2フェーズコミット等を取り入れますでしょうか?
そもそも、前提が間違っているのかもしれませんが、その場合も教えていただけると幸いです。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/04/06 12:18 編集