コマンドでの検証後エラーの処理方法(DDD + CQRS)


17

たとえば、登録フォームを送信するときは、有効な状態(例、電子メールアドレスの構文、年齢など)であることをDomain ModelWriteModelin CQRS)にチェックインする必要があります。

次に、を作成しCommand、それをに送信しますCommand Bus

コマンドは何も返すべきではないことを理解しています。

では、エラーをどのように処理しますCommand Busか?(たとえば、1秒前に同じで登録したユーザーusername/email)。

そのコマンドが失敗したことをどのようにして知り、どのようにしてエラーを知っていますか?


2
イベントバスは必要ありません。CQRSを実装するときにイベントバスが必要だと誰もが思うのはなぜか、私にはわかりません。CQRSは、単に書き込みと読み取りを完全に分離し、別々の懸念事項を作成することを意味します。あなたがそうする限り、あなたはCQRSを持っています。コマンドは非同期である必要さえありません。エラー報告付きの同期コマンドが機能する場合は、それを実行します。
アンディ

1
成功し場合、コマンドは何も返しません。このアイデアは、コマンドがエラー時に例外をスローする可能性があるという意味を持つCQSから始まりました。説明しているのは、一方向のコマンドです。それに関連してこの回答を参照してください。
ケイシースピークマン

回答:


3

コマンドは何も返すべきではないことを理解しています。

それは一つの見方ですが、完全に固まっているわけではありません。HTTPでの書き込み(PUT、POST、DELETE)を検討してください-これらのメッセージはすべて、リソースが状態を変更するという要求を伴うメッセージであるという意味でコマンドであり、すべてが応答を返します。

それでは、コマンドバス以外のエラーをどのように処理しますか?(たとえば、同じユーザー名/メールで1秒前に登録したユーザー)。

そのコマンドが失敗したことをどのようにして知り、どのようにしてエラーを知っていますか?

したがって、コマンドハンドラーと直接通信している場合、返されたメッセージは、コマンドが受信され処理されたことを確認するための完全に合理的な方法です。

ターゲットと直接通信できないようにするバスなどのミドルウェアを使用している場合は、非同期メッセージングパターンに注目することをお勧めします。コマンドハンドラを取得してメッセージを呼び出し元?

1つのアイデアは、コマンドの結果をサブスクライブすることです。これは、Hohpeのエンタープライズ統合パターンのアイデアの一部を借用しています。基本的な考え方は、クライアントは送信されたコマンドメッセージに精通しているため、コマンドメッセージの結果として発行された新しいメッセージをサブスクライブする適切な位置にあるということです。コマンドハンドラーは、データを記録帳に保存した後、変更が成功したことを通知するイベントを発行し、クライアントはそれらのイベントにサブスクライブします-メッセージ内のさまざまな識別子の一致を考慮して正しいイベントを認識します相関IDなど)。

代替アプローチはもう少し直接的です。1つは、メッセージにコールバックを含めることです。コールバックは、メッセージが正常に処理された後にコマンドハンドラーによって呼び出すことができます。

非常によく似た代替方法は、コマンドハンドラーが確認応答を書き込むためにコマンドメッセージにスペースを予約することです。クライアントには問題のコマンドメッセージが既にあるため、回線は既に完了しています。「約束」または「完全な未来」を考えてください メッセージは、コマンドハンドラに確認応答を書き込む場所を指示します。これにより、確認応答が利用可能であることをクライアントに通知します(カウントダウンラッチ)。

もちろん、ミドルウェアを削除する追加オプションがありますが、これは単純に正しいことを行うのを妨げているようです。

たとえば、1秒前に同じユーザー名/メールアドレスで登録したユーザー

ユーザー登録をべき等で処理している場合、それは必ずしもエラーではありません。応答が確認されるまでメッセージを繰り返すことが、少なくとも1回の配信を保証する一般的な方法です。


1

たとえば、登録フォームを送信するときは、ドメインモデル(CQRSのWriteModel)が有効な状態(例、電子メールアドレスの構文、年齢など)であることを確認する必要があります。

検証には多くの種類があります。電子メールアドレスの構文と年齢の形式を確認するときの検証は、コマンドが実行できる検証の一種です。これは実際にはドメインの問題ではありません。一部のドメインの専門家がそれらの仕様を教えてくれるので、そのように見えるかもしれませんが、コマンドの作成でこの種の検証を行う必要があります。実際、コマンドが作成されてBUSに送信された後、アクションを実行することはより複雑になるため、一般的な考えは、できるだけ早く検証を行うことです。

それでは、コマンドバス以外のエラーをどのように処理しますか?(たとえば、同じユーザー名/メールで1秒前に登録したユーザー)。

この種の検証は、CQRSの最初からCQRSコミュニティで多く議論されています。どこでそれをしますか?それは非常に議論されています。私は個人的に次のアプローチを使用します。コマンドがBUSに送信される前に、ユーザー名/電子メールを一元管理された方法(つまり、ユーザー名/電子メールの一意のインデックス制約)としてマークします。その後、コマンドを送信します。欠点は、コマンドが失敗した場合、短期間ユーザー名が取得されて使用されないことです。これは私のビジネスにとっては許容範囲であり、おそらくあなたのビジネスにとってはそうでしょう。

そのコマンドが失敗したことをどのようにして知り、どのようにしてエラーを知っていますか?

ドメイン不変のためにAggregateによって非同期コマンドが拒否された場合、何らかのコマンドを発行者に通知する補償コマンドを発行することにより、何らかの対策を講じる必要があります(つまり、コマンドが失敗したという説明メールを送信します)。

重複した電子メールの問題は、その電子メールアドレスが別の人に属しているために電子メールを送信できないことです。そのため、コマンドをバスに送信する前に確認します。


1

検証はデコレータで実行する必要があります。検証が必要なコマンドは、そのように装飾できます。

コマンドでvoidを返す場合、検証は例外を使用して処理できるため、返されたタスクの結果を使用してsyncまたはasync呼び出しで検証を取得できます。

別の可能性は、検証を検証結果を返す「クエリ」の一種と考えることです。検証クエリを実行し、合格した場合はコマンドを実行します。これは、デコレータアプローチの代替手段になります。


それは最もクリーンな方法であるため、私は例外アプローチが好きです。しかし、これには例外が重すぎませんか?
EresDev

1
こんにちは@EresDev-はい、重いです。しかし、私はあなたのデータがどれほど「有効」であるかを知りません。1000個のレコードのうち2個が無効であるとしましょう。例外は本当に例外的な条件であるため、実行可能です。ここで、有効でない200の1000レコードを検討します。その原因では、データの20%が無効であるため、yes例外を避ける必要があります。したがって、現在のデータの有効性に基づいてこのアプローチを選択するかどうかを決定するのはあなた次第です。:)
ジョンレイナー

50%以上のユーザーが最初に無効なデータを挿入すると考えられる登録フォームの例をそのままにしておけば、例外は問題ないということを付け加えます。Webアプリケーションバックエンドでは、フロントエンドも検証を実行しているため、ほとんどの場合有効なデータを取得します。何らかのまれな機会によって何かがフロントエンドの検証に失敗した場合、またはだまされている場合にのみ取得できます。したがって、この場合、例外は大丈夫です。
EresDev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.