web-dev-qa-db-ja.com

DDD:トランザクションごとに複数の集合ルートを更新するのはなぜ悪い習慣なのですか?

Vaughn Vernonからの引用:

2つ以上のアグリゲートが少なくとも更新に依存している場合は、結果整合性を使用します。

enter image description here

彼はさらに、Domain Eventsを使用して、更新が必要な他のAggregate Rootにアクションを公開できることを示唆しています。

彼はさらに最終的な一貫性が必要な悪かもしれないことを説明します。

1つのリクエスト/トランザクションで複数の集合ルートを更新することが悪い習慣になるのはなぜですか?

7
Nik Kyriakides

私がこれまでに読んだすべてのことから、理由は、1つのトランザクションで複数の集計を変更すると、それらが同じデータベースHostに格納されるという要件が作成されるためと思われます。 (2フェーズコミットについても考慮しません。)

これはトレードオフを導入し、これを経験則ではなく考察にします。

問題の境界コンテキストが複数のデータベースホストに分割されることはありますか?複数ではないデータベース-問題ありません。データベースhosts

信じられないほどのボリュームを処理する場合、またはテーブルインデックスの選択が得意でない場合を除き、ほとんどのコンテキストは、単一のデータベースホストのみを使用します。これにより、複数の集約変更(1つの境界コンテキスト内)に関連するデータベーストランザクションを使用できます。これにより、一貫性の保証が非常に簡単になります。

すべてを考慮して、私はこのような意思決定プロセスを好む:

  1. 集計が最終的に一貫するのに十分ですか?その場合、結果整合性を使用します。
  2. アグリゲートが常に単一のデータベースホスト上に存在することを合理的に期待できますか?その場合、同じデータベーストランザクションを共有できるようにします。 (集約がdifferent境界コンテキストにある場合、ここでの答えはおそらく「いいえ」です。)
  3. 複数のホストと保証された一貫性が必要です。私たちの要件は重いので、今だけ私たちはフープを飛び越える必要があります。設計を通じて問題を解決します。

#3の例を示すには:

  • Balanceコンテキストは、各テナントのバランスを追跡します。
  • テナントの残高がマイナスであってはなりません。
  • 支払いコンテキストは、テナントの残高の一部を使いたいと考えています。控除はすぐに一貫している必要があります(前のルールを保証するため)。支払いが失敗した場合、残高は最終的に戻る必要があります。
  • Balanceコンテキストは、そのAPIで新しい予約を返すメソッドを公開し、要求された量だけ残高を減らすか、その量が利用できない場合は失敗を返します。
  • Balanceコンテキストは、Paymentコンテキストからのイベントを消費します。
  • 特定のイベントにより、テナントのバランスが向上します。
  • その他のイベントは、テナントの残高の減少に関連し、常に以前の予約にリンクされています。彼らはその予約を確認します。
  • 予約は短時間有効です。 5分。その時間内に確認されなかった予約は取り消され、補償するために残高が増加します。

この例では、すべてのイベントが処理されるという保証が必要であることに注意してくださいexactly once。特に、イベントをスキップしてはいけません。それは実行可能ですが、正しくすることは困難です。ほとんどのツールはこの点で気密ではありません。最も簡単に特定できる障害点は、データベースを更新した後、イベントを発行する前にアプリケーションサーバーがクラッシュした場合です。正確に1回のイベントの配信を保証すること自体は、価値のある議論です。

5
Timo

1つのリクエスト/トランザクションで複数の集約ルートを更新することが、このような悪い習慣になるのはなぜですか?

問題はその逆です。単一のトランザクションで複数の集計を変更しようとすることは、集計の境界を正しくモデル化していないことを示しています。

別の言い方をすると、同じトランザクションで2つの異なる集計を変更すると、それらのストレージに制約が導入され(集計は同じデータベースに格納される必要があります)、その制約はmodelには反映されません。それは事実上暗黙的です。

12
VoiceOfUnreason