8

私はまだCQRSスタイルのアーキテクチャに関連する基本的な(そして解決された)問題に苦労しています:

集約ルートのセットに依存するビジネスルールをどのように実装しますか?

例として、予約アプリケーションを取り上げます。コンサートのチケット、映画の座席、レストランのテーブルを予約できる場合があります。いずれの場合も、販売される「アイテム」の数は限られています。

イベントや場所がとても人気があると想像してみましょう。新しいイベントや時間帯の販売が開始されると、予約は非常に迅速に到着し始めます。おそらく1秒あたりの数が多くなります。

クエリ側では、大規模な拡張が可能であり、予約はキューに入れられ、自律コンポーネントによって非同期的に処理されます。最初は、予約コマンドをキューから削除するときに受け入れますが、ある時点で、残りのコマンドを拒否し始める必要があります。

限界に達したとき、どうやって知ることができますか?

予約コマンドごとに、ある種の店舗に問い合わせて、リクエストに対応できるかどうかを判断する必要があります。これは、その時点ですでに受け取った予約の数を知る必要があることを意味します。

ただし、ドメインストアがWindows Azureテーブルストレージなどの非リレーショナルデータストアである場合、SELECT COUNT(*) FROM ...

1つのオプションは、次のように、現在のカウントを追跡するだけの個別の集約ルートを保持することです。

  • AR:予約(誰?何人?)
  • AR:イベント/タイムスロット/日付(集計数)

2番目の集約ルートは最初の集約ルートの非正規化された集約ですが、基盤となるデータストアがトランザクションをサポートしていない場合、大量のシナリオでこれらが同期しなくなる可能性が非常に高くなります(これが私たちが試みていることです)そもそもアドレス)。

考えられる解決策の1つは、予約コマンドの処理をシリアル化して、一度に1つだけが処理されるようにすることですが、これはスケーラビリティ(および冗長性)の目標に反します。

このようなシナリオは、標準の「在庫切れ」シナリオを思い出させますが、違いは、予約をバックオーダーにうまく入れることができないことです。イベントが完売すると完売するため、補償措置がどうなるかわかりません。

そのようなシナリオをどのように処理しますか?

4

4 に答える 4

4

これについてしばらく考えた後、根本的な問題は、異種のRESTサービスの非トランザクション的な性質よりもCQRSに関連していないことにようやく気づきました。

本当にそれはこの問題に要約されます:いくつかのリソースを更新する必要がある場合、2番目の書き込み操作が失敗した場合にどのように一貫性を確保しますか?

リソースAとリソースBに順番に更新を書き込みたいと想像してみましょう。

  1. リソースAが正常に更新されました
  2. リソースBを更新しようとして失敗します

最初の書き込み操作は、例外が発生した場合に簡単にロールバックできないので、何ができるでしょうか。リソースAに対して補償アクションを実行するために例外をキャッチして抑制することは、実行可能なオプションではありません。まず、実装は複雑ですが、次に安全ではありません。ネットワーク接続の障害が原因で最初の例外が発生した場合はどうなりますか?そのシナリオでは、リソースAに対する補償アクションを作成することもできません。

重要なのは、明示的なべき等性です。Windows Azureキューは、正確に1回のセマンティクスを保証するわけではありませんが、少なくとも1回のセマンティクスを保証します。これは、断続的な例外が発生した場合に、メッセージが後で再生されることを意味します。

前のシナリオでは、次のようになります。

  1. リソースAの更新が試行されます。ただし、リプレイは検出されるため、Aの状態は影響を受けません。ただし、「書き込み」操作は成功します。
  2. リソースBが正常に更新されました。

すべての書き込み操作がべき等である場合、メッセージリプレイで結果整合性を実現できます。

于 2010-12-23T14:18:07.090 に答える
2

興味深い質問です。これを使用すると、CQRSの問題点の1つに釘付けになります。

Amazonがこれを処理する方法は、要求されたアイテムが売り切れた場合にビジネスシナリオでエラー状態に対処することです。エラー状態は、要求されたアイテムが現在在庫がなく、出荷予定日であることを電子メールで顧客に通知するだけです。

ただし、これはあなたの質問に完全には答えません。

チケットを販売するシナリオを考えると、顧客からのリクエストは予約リクエストであることを必ず伝えます。予約リクエストはできるだけ早く処理され、最終的な回答は後でメールで復活します。これを許可することにより、一部の顧客は、要求を拒否した電子メールを受け取る可能性があります。

今。この拒絶反応の痛みを和らげることができますか?そうです。在庫のあるアイテムのパーセンテージまたは量を含むキーを分散キャッシュに挿入し、アイテムが販売されるたびにこのカウンターをデクリメントします。このようにして、予約リクエストが出される前にユーザーに警告することができます。たとえば、最初のアイテム数の10%しか残っていない場合、顧客は問題のアイテムを入手できない可能性があります。カウンターがゼロの場合、それ以上の予約リクエストの受け付けを拒否します。

私のポイントは:

1)ユーザーが行っているリクエストであり、これが拒否される可能性があることをユーザーに通知します。2)問題のアイテムを取得するための成功の可能性が低いことをユーザーに通知します。

あなたの質問に対する正確な答えではありませんが、これがCQRSを扱うときにこのようなシナリオを処理する方法です。

于 2010-12-04T22:37:39.087 に答える
1

eTagを使用すると、トランザクションロックの代わりに使用できる楽観的同時実行制御を使用して、ドキュメントを更新し、潜在的な競合状態を安全に処理できます。詳細については、 http://msdn.microsoft.com/en-us/library/dd179427.aspxの備考を参照してください。

ストーリーは次のようになります。ユーザーAは最大チケット2でイベントEを作成し、eTagは123です。需要が高いため、3人のユーザーがほぼ同時にチケットを購入しようとします。ユーザーBが予約リクエストBを作成します。ユーザーCが予約リクエストCを作成します。ユーザーDが予約リクエストDを作成します。

システムSは予約リクエストBを受信し、eTag 123でイベントを読み取り、残りのチケットが1つになるようにイベントを変更します。Sは、元のeTagと一致するeTag 123を含む更新を送信して、更新が成功するようにします。eTagは456になりました。予約リクエストが承認され、ユーザーに成功が通知されます。

別のシステムS2は、システムSが要求Bを処理すると同時に予約要求Cを受信するため、イベントを読み取り、eTag 123を使用してイベントを残りの1つのチケットに変更し、ドキュメントの更新を試みます。ただし、今回はeTag 123が一致しないため、例外を除いて更新は失敗します。システムS2は、eTag 456とカウントが1になっているドキュメントを再読み取りして操作を再試行するため、これを0にデクリメントし、eTag456で再送信します。

ユーザーCユーザーにとって残念なことに、システムSはユーザーBの直後にユーザーDの要求の処理を開始し、eTag 456でドキュメントを読み取りましたが、システムSはシステムS2よりも高速であるため、システムS2の前にeTag 456でイベントを更新できたため、ユーザーDもチケットの予約に成功しました。eTagは789になりました

したがって、System S2は再び失敗し、もう一度試行しますが、今回はeTag 789でイベントを読み取ると、使用可能なチケットがないことがわかり、ユーザーCの予約要求が拒否されます。

予約リクエストが成功したかどうかをユーザーに通知する方法はあなた次第です。数秒ごとにサーバーをポーリングして、予約ステータスが更新されるのを待つことができます。

于 2010-12-04T23:14:20.637 に答える
1

ビジネスの観点を見てみましょう(私は同様のことを扱っています-空きスロットでの予約予約)...

あなたの分析で私がオフになっていると思う最初のことは、予約可能なチケット/座席/テーブルの概念がないということです。これらは予約されているリソースです。

トランザクションの場合は、何らかの形式の一意性を使用して、同じチケット/シート/テーブルで二重予約が発生しないようにすることができます(詳細についてはhttp://seabites.wordpress.com/2010/11/11/を参照してください)。一貫性のあるインデックス-制約)。このシナリオでは、同期(ただし同時)コマンド処理が必要です。

トランザクションではない場合は、イベントストリームをさかのぼって監視し、コマンドを補正できます。システムが確実に(つまり、イベントストリーム分析の後で)コマンドが完了し、補正されたかどうかがわかるまで(つまり、「予約が行われたか?」はい、もしくは、いいえ?")。言い換えれば、補償は確認サイクルの一部にすることができます。

もう少し後退しましょう...

請求も含まれる場合(オンラインチケット販売など)、このシナリオ全体がとにかく物語に発展すると思います(予約チケット+請求チケット)。請求がなくても、体験を信頼できるものにするためのサガ(テーブルの予約+予約の確認)があります。したがって、チケット/テーブル/座席の予約の1つの側面だけにズームインしている場合でも(つまり、まだ利用可能であるかどうか)、「長期」トランザクションは、支払いを行うか、確認するまで完了しません。 。とにかく補償が行われ、何らかの理由でトランザクションを中止すると、チケットが再び解放されます。ここで興味深いのは、ビジネスがこれにどのように対処したいかということです。同じチケットを渡せば、他の顧客がトランザクションを完了した可能性があります。この場合、チケット/座席/テーブルを二重に予約すると、払い戻しがより面白くなる可能性があります。不便を補うために、次の/同様のイベントの割引を提供することもできます。答えは、技術モデルではなく、ビジネスモデルにあります。

于 2010-12-06T17:30:22.480 に答える