web-dev-qa-db-ja.com

CQRSを使用したDDDでのコマンド検証

私はDDDを学び、CQRSパターンを利用しています。データストアから読み取らずにコマンドハンドラーでビジネスルールを検証する方法がわかりません。

たとえば、クリスはアシュリーに贈り物をしたいと思っています。

コマンドはGiveGiftCommandである可能性があります。

クリスが贈りたいギフトを実際に所有していることをどの時点で確認しますか?そして、データベースから読み取らずにそれを行うにはどうすればよいですか?

16
chris

コマンドハンドラーでの検証については、さまざまな見解や意見があります。

コマンド拒否できます、コマンドが無効な場合はいいえと言うことができます。

通常、UIで実行され、コマンドハンドラー内で複製される可能性のある検証があります(一部の人々はそれをドメインに配置する傾向があります)。次に、コマンドハンドラーは、正しい形式のデータ、期待値など、エンティティの外部で発生する可能性のある簡単な検証を実行できます。

一方、ビジネスロジックは、コマンドハンドラーに含めるべきではありません。それはあなたのドメインにあるはずです。

だから私は根本的な質問は...

コマンドハンドラーから読み取り側にクエリを実行する必要がありますか?

私はノーと言うでしょう。コマンドハンドラまたはドメインロジックで読み取りモデルを使用しないでください。ただし、クライアントから常に読み取りモデルを照会できますコマンドに必要なデータをフェッチし、コマンドを検証します。クライアントの読み取り側にクエリを実行して、クリスが贈りたいギフトを実際に所有しているかどうかを確認します。もちろん、読み取りモデルを含む検証は結果整合性がある可能性があります。これはもちろん、コマンドハンドラー内でコマンドがアグリゲートから拒否される可能性があるもう1つの理由です。

コマンドにデータを含める必要がある場合、ハンドラーがコマンドを検証する必要があると言う人もいます。クライアントにも影響を与えずに、ハンドラー/ドメイン内の検証ロジックを変更することはできません。これは、ドメイン知識の多くをクライアントに公開し、クライアントが意図を表現したいだけであるという事実に反します。そのため、コマンドハンドラーにGiftServiceインターフェイス(ユビキタス言語の一部)を提供し、必要に応じてインターフェイスを実装する傾向があります。これには、読み取り側のクエリが含まれる場合があります。

クライアントは、発行するコマンドが成功すると常に想定する必要があるだと思います。コマンドを検証するために読み取り側を呼び出す必要はありません。 2つの矛盾するコマンドを取得する可能性はほとんどありません(ユーザーが同じ電子メールアドレスでアカウントを作成する)。次に、たとえばSaga/ProcessManagerのような修正措置を発行する手段が必要です。したがって、代わりに修正措置を講じることは、コマンドが検証され、そもそもディスパッチされなかった場合よりも問題が少なくなります。

15
Tomasz Jaskuλa

操作が非同期であるかどうか、つまりユーザーが即時の応答を期待しているかどうかによって異なります。ギフトの所有権は基本的にセキュリティ機能であり、実際のサービスを呼び出したり、GiveGiftCommandを送信したりする前に、「準備」操作として実行できます。

実行できる唯一のコマンド検証は、必要な形式のデータが含まれていること(UI検証)と、ユーザーがそのアクションを実行するためのアクセス許可を持っていることを確認することです。ただし、コマンドが送信された後、他のビジネス上の制約を尊重するかどうかを決定するのはドメイン次第です。

ユーザーが即時のフィードバックを期待している場合は、コマンドが完了するまで実際に「待機」できます。そのために、 コマンドハンドラーがメディエーターを使用して送信者に結果を提供できるアプローチ を使用できます。ただし、これは、少なくとも一部のコマンドが即座に実行されることを意味し、アプリではそうではない場合があります。ただし、これは、補正やその他のものを実装するのではなく、メッセージエラーを返すだけの場合に最も簡単なアプローチです。いくつかのユースケースは単純です。

コマンドハンドラーとビジネスロジックについては、TomaszJaskuλaに同意しません。コマンドハンドラーは関数であり、技術的な詳細です。コマンドハンドラーまたは静的関数にビジネスロジックを配置できますが、それは問題ではありません。メッセージとそのハンドラーは、機能を実装するために使用できるインフラストラクチャコンポーネントです。たとえば、アプリでは、ドメインイベント、アプリケーションイベントなどを設定できます。これらはすべてイベントです。つまり、何かが変更されたことを通知し、ドメインまたは他の場所にイベントハンドラーを配置できます。

データベースから「読み取る」ことを妨げるルールはありませんが、少なくとも読み取りモデルは理論的には古くなっています。ただし、99%の場合、これはそれほど問題にはならない可能性があります。残りの1%については、非常に具体的なソリューションが必要です。

2
MikeSW

私は知識のある友人にまったく同じ質問をしましたが、彼の答えは、コマンドハンドラー内(私の場合はコマンドを解釈してイベントをジャーナルに書き込むakka永続アクター内)でこの検証を行っても問題ないというものでした。

ただし、パフォーマンス上の理由でそれが不可能な場合(永続アクター内で検証を処理するとアクターがブロックされ、アプリ全体のスケーラビリティのボトルネックになるため)、楽観的ロック(OCC)を使用できます。

言い換えると、検証は他のアクターで実行できます(これを検証アクターと呼びます-永続アクターにはありません)。これは永続アクターをブロックしませんが、検証に使用されるデータに検証が検証アクターで実行されている間に永続アクターで変更されました)。

検証アクターがOKで戻り、検証に使用されたすべてのデータが、コマンドが到着した時点と同じ永続アクター(OCCと同じバージョン-バージョン)でまだ同じである場合コマンドハンドラー(永続アクター)の場合、コマンドは永続アクターによって受け入れられます。それ以外の場合は、検証アクターに再評価するために検証を再送信する必要があります。

0
jhegedus