web-dev-qa-db-ja.com

イベントソーシングを使用してショッピングシナリオをモデル化する方法

イベントソーシングを使用して簡単なショッピングシナリオをプログラムしようとしています。

---------------     ---------------       ----------------
| OrderService |   | ProductService |    | PaymentService |
----------------   ------------------    -----------------
        |________________|_____________________|
                         |
                --------------------
                |       KAFKA      |
                --------------------
                         |
                --------------------
                |      PROCESSOR   |
                --------------------
                         |
                --------------------
                |     DATABASE     |
                --------------------

3つのサービスがあります。

  • 注文サービス:顧客が作成した製品の注文を受け取るAPI
  • 製品サービス:製品の在庫が含まれています
  • 支払いサービス:注文の支払いを行います

Kafka:さまざまなイベントのイベントストア。

Processor:現在の状態のビューを生成するCQRSプロセッサ。

データベース:現在の状態のマテリアライズドビュー。

私の主な疑問は、次のようなフローにどのように対処するかです。

  1. Order Serviceが呼び出され、イベントが作成されます:OrderPlaced
  2. ProductServiceは商品の在庫を確認し、それに基づいてProductReservedまたはOrderCancelledNoStockイベントを作成します。
  3. PaymentServiceはProductReservedイベントの場合に支払いを処理し、新しいイベントOrderCompletedを生成しました。

純粋なイベントソーシングアプローチであるため、ProductServiceにはデータベースがありません。

  • 在庫に関するシステムのスルーのソースとしてDATABASEのマテリアライズドビューを検討することは良い習慣ですか?私の見解では、真実の唯一の情報源はイベントであるべきだと思いませんか?
  • DATABASE CQRSプロセスのレイテンシにより不整合が見つかる可能性が非常に高いと思います。そのため、データベースをスルースのソースとして使用することは本当に危険です。ただし、具体化されたビューを持つことは、製品とプロセスの現在の在庫を知る、または製品の注文を知る唯一の方法のようです。

私の頭に浮かぶ問題のあるケース:

  • 同じ製品の2つの同時OrderPlacedがProductServiceに到着します。これはDATABASEを呼び出して、製品の在庫があるかどうかを確認します(残り1製品)。どちらも問題ないので、同じ製品に対してProductReservedイベントを作成し、PaymenServiceがそれを処理します。
  • 1つのOrderPlaced注文がProductServiceに到着します。 1個の商品が残っているため、ProductReservedを公開しています。 DATABASEがCQRSプロセスによって更新される前に、同じ製品の別の注文が到着しました。そのため、十分な在庫がないにもかかわらず、別のProductReservedを公開します。

このようなシナリオを処理するための推奨される方法は何ですか?

2
codependent

ここにあるのは、複数のサービスを生成するプロセスです。 [〜#〜] ddd [〜#〜] では、これらのサービスは可能な最大のトランザクション境界である集約です。 DDDとの関連で答えます。

これは、注文プロセスをorder placedからorder completedイベントに調整する Saga/Process managerOrderProcess)でモデル化できます。

ProductServiceは商品の在庫を確認し、それに基づいてProductReservedまたはOrderCancelledNoStockイベントを作成します。

実際、ProductServiceProductReservedイベントを発生させますが、OrderCancelledNoStockイベントは発生させません。このサービスは注文について知る必要はなく、product reserving-懸念の分離についてのみ知っている必要があります。代わりに、ProductOutOfStock例外をスローする必要があります。

ProductReservedイベントが発生した場合、OrderProcessはそれを受け取り、PaymentServiceにコマンドを送信して支払いを行います(これは実際の支払い手順によって異なります)。

ProductOutOfStock例外がスローされた場合、OrderProcessはそれをキャッチしてCancelOrderOrderServiceに送信します。

同じ製品の2つの同時OrderPlacedがProductServiceに到着します。これはDATABASEを呼び出して、製品の在庫があるかどうかを確認します(残り1製品)。どちらも問題ないので、同じ製品に対してProductReservedイベントを作成し、PaymenServiceがそれを処理します。

OrderServiceは、イベントソースの集約として実装されている場合、すべての製品が予約された後にProductOutOfStock例外をスローすることで許可されません。在庫の検証は、予測ではなく、以前に生成されたイベントのストリーム全体に対して行われます。同時コマンド実行の場合、イベントを永続化しようとする最後のコマンドはそれらを永続化することに失敗し、その後、イベントはallを再ロードすることによって再試行されます、winingコマンドによって生成されたものを含め、ProductOutOfStock例外で失敗します。

0

一般的な規則として、コマンドの検証は常にプリンシパルストレージ(イベントストリーム)に対して行われる必要があります。そうでない場合、最終的に一貫した予測に対して検証すると、正しく指摘したように、一貫性が失われる可能性があります。

場合によっては、2つのステップで検証することが理にかなっていることがあります。最初は予測に対して、次に主ストレージに対してです。たとえば、UIはデータベースにクエリを実行して、在庫がゼロの場合に「在庫切れ」メッセージをユーザーに表示します。しかし、ユーザーが注文すると、バックエンドはイベントストリームに対してそれを検証します。

0
Fyodor Soikin