コマンドハンドラーとDDD


10

クエリサービスを使用してデータを取得し、コマンドサービスを使用してコマンドを送信するASP.NET MVCアプリケーションがあります。私の質問は、コマンド部分についてです。

リクエストが届くと、コマンドサービスはコマンドディスパッチャーを使用して、指定されたコマンドハンドラーにコマンドをルーティングします。このコマンドハンドラーは最初にコマンドを検証し、すべてが許容できる場合はコマンドを実行します。

具体例:AddCommentToArticleCommandHandlerは、ArticleId、CommentText、EmailAddressを持つAddCommentToArticleCommandを受け取ります。

最初; -記事が存在するかどうかを確認します-記事が閉じていないかどうかを確認します-コメントテキストが20〜500文字で入力されているかどうかを確認します-電子メールアドレスが入力されており、有効な形式であるかどうかを確認します

この検証をどこに置くか疑問に思っていますか?

1 /コマンドハンドラ自体。ただし、他のコマンドハンドラで再利用することはできません。

2 /ドメインエンティティ内。しかし、ドメインエンティティはリポジトリやサービスを認識していないため、必要な検証を実行できません(記事が存在するかどうかを確認できません)。ただし、一方で、エンティティにロジックが含まれていない場合は、DDDの原則に従っていない単純なデータコンテナーになります。

3 /コマンドハンドラーはバリデーターを使用するため、検証を他のコマンドハンドラーで再利用できます。

4 /その他のメカニズム?

この特定の例の一連の責任と、その中で役割を果たすオブジェクト(エンティティ、リポジトリなど)を探しています。

コマンドハンドラーからリポジトリまで、これをどのように実装するかについてのアイデアはありますか?


「しかし、ドメインエンティティはリポジトリやサービスを知らないため、必要な検証を行うことができません」?何故なの?DDDのどの原理がこれを必要としますか?
S.Lott

私は、エンティティーがリポジトリーなどについて知ってはならないことを随所に読みました。
L-Four

あなたが読んだもの、具体的には-の引用、リンク、または例を提供できますか?私たちはあなたが読んでいるDDDの説明を知りません。
S.Lott


Jimmy Bogardがこのトピックについていくつかの意見を共有しました... lostechies.com/jimmybogard/2009/02/15/validation-in-a-ddd-world
Mayo

回答:


4

この場合、2つのタイプの検証を分離する必要があると思います。ドメイン検証アプリケーション検証

アプリケーションの検証は、コマンドのプロパティ 'text'が20〜200文字であることを検証するときに行うものです。したがって、これをGUIおよびPOST後にサーバーでも実行されるview-model-validatorで検証します。同じことが電子メールにも当てはまります(ところで、 `32.d +" Hello World .42 "@mindomän.local"などの電子メールはRFCによれば有効であることを理解してください)。

次に、別の検証があります。記事が存在することを確認してください-GUIからコメントを添付するコマンドが送信された場合、記事が存在しない理由を自問する必要があります。GUIは最終的に整合性がとれており、データストアから物理的に削除できるアーティクルという集約ルートがありますか?その場合、コマンドハンドラーは集約ルートのロードに失敗するため、コマンドをエラーキューに移動するだけです。

上記の場合、有害なメッセージを処理するインフラストラクチャがあります。たとえば、メッセージを1〜5回再試行してから、メッセージをポイションキューに移動します。ここで、メッセージのコレクションを手動で検査し、関連するメッセージを再ディスパッチできます。監視するのは良いことです。

さて、ここで議論しました:

  • アプリケーションの検証

    • GUIでJavaScriptを使用
    • WebサーバーでMVC検証を使用
  • 集約ルート+ポイズンキューがありません

ドメインと同期していないコマンドについてはどうですか?おそらく、ドメインロジックに、記事への5つのコメントの後、400文字未満のコメントしか許可されないというルールがあるかもしれませんが、1人の男が5番目のコメントで遅すぎて6番目になった-GUIはそれをキャッチしなかったため彼がコマンドを送信した時点のドメインと一致していませんでした。この場合、ドメインロジックの一部として「検証エラー」が発生し、対応するエラーイベントを返します。

イベントは、メッセージブローカーまたはカスタムディスパッチャーへのメッセージの形式にすることができます。アプリケーションがモノリシックの場合、Webサーバーは、前述の成功イベントと失敗イベントの両方を同期的にリッスンし、適切なビュー/部分を表示できます。

多くのタイプのコマンドの失敗を意味するカスタムイベントがあることがよくあり、Webサーバーの観点からサブスクライブするのはこのイベントです。

現在取り組んでいるシステムでは、MassTransit + RabbitMQメッセージバス+ブローカーを介してコマンド/イベントでリクエスト/レスポンスを実行しており、この特定のドメインに(ワークフローの一部をモデル化している)というイベントがありますInvalidStateTransitionError。状態グラフのエッジに沿って移動しようとするほとんどのコマンドは、このイベントを発生させる可能性があります。今回のケースでは、最終的に一貫性のあるパラダイムに基づいてGUIをモデル化しているため、ユーザーを「コマンド承認」ページに送信し、その後、イベントサブスクリプションを通じてWebサーバーのビューを受動的に更新します。また、集約ルートでイベントソーシングも行っていることにも注意してください(sagasの場合も同様です)。

つまり、あなたが話している検証の多くは、実際にはアプリケーションタイプの検証であり、実際のドメインロジックではありません。ドメインは単純ですがDDDを実行している場合は、単純なドメインモデルを使用しても問題はありません。ただし、ドメインのモデリングを続けると、ドメインが最初に判明したほど単純ではない可能性があることに気付くでしょう。多くの場合、集約ルート/エンティティは、コマンドによって引き起こされたメソッド呼び出しを受け入れるだけで、検証を実行することなくその状態の一部を変更する可能性があります-特に、Webサーバーでコマンドを検証する場合のようにコマンドを信頼する場合あなたがコントロールします。

Norwegian Developer Conference 2011の DDDに関する2つのプレゼンテーションと、Öredev2010での Gregのプレゼンテーションをご覧になることをお勧めします。

乾杯、ヘンケ


ありがとう!検証に関する1つのこと:現時点では、この検証はUIによってトリガーされます(サービスにValidate(command)要求を発行することにより)。コメントエンティティ自体のValidate()を使用します。次に、コマンドが有効な場合、クライアントはExecuteコマンドを発行します。これにより、Validate()が確実に実行され、有効な場合、実際のコマンドが実行されます。したがって、すべてのコンテキストで検証が同じになるため(メールは常に有効である必要があり、コメントテキストは長すぎないなど)、メインの検証はCommentエンティティによって行われます。これは良いアプローチですか?
L-Four

あなたが説明しているように見える検証は、2フェーズ検証プロトコルでモデル化することを保証するものではないようです-確かに、単体テストでテストするようなエンティティのメソッド呼び出しのパラメーターを検証しますが、これに加えて; アプリケーション層/ GUIは、ドメインにコマンドを送信しなくても、記述しているルールを簡単に検証できます。イモ。クライアントが悪意のない限り、コマンドは機能します。クライアントが悪意のある場合、コマンドは失敗し、読み取りモデルは対応するイベントを取得しないため、エラーキューで問題のあるコマンドを検査できます。
Henrik

UI(ASP.NET MVC)では、検証属性を使用してすべての検証を実行しています。この検証が成功した場合、ドメインで再度検証する必要はありませんが、コマンドを実行するだけですか?
L-Four

1
はい、コマンドを実行しますが、コマンドがアプリケーション層とドメインの両方で無効でないことを確認してください。
Henrik

0

編集:WaybackMachineリンク:http ://devlicio.us/blogs/billy_mccafferty/archive/2009/02/17/a-response-to-validation-in-a-ddd-world.aspx

簡単な答えはありません。

検証がどこにあるべきかについて答えようとすると、2つの明確なプロジェクトシナリオが思い浮かびます。1つ目は、ビュー層とドメイン層の間で情報を転送するためにDTO層が使用される場合です。たとえば、ドメイン層にCustomerオブジェクトがあり、顧客情報をマップする別の層に関連付けられたCustomer DTOがある場合、顧客情報をユーザーに提示するためのビューを提供します。

2番目のシナリオは、ドメインレイヤー内のエンティティがビューと直接共有され、ユーザーにデータを提示するプロジェクトです。たとえば、個別のDTOレイヤーを維持し、ドメインオブジェクトをビューに与えるDTOにマッピングする代わりに、ビューに直接Customerオブジェクトを与えることができます。したがって、個別に管理されているDTOを介してCustomerの名前を表示する代わりに、ビューはCustomerオブジェクト自体に情報を要求するだけです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.