web-dev-qa-db-ja.com

CQRSコマンドとクエリ-ドメインに属していますか?

CQRSでは、コマンドとクエリはドメインに属していますか?

イベントもドメインに属していますか?

それが事実である場合、コマンド/クエリハンドラーはインフラストラクチャでの単なる実装ですか?

現在、私はそれを次のようにレイアウトしています:

Application.Common
Application.Domain
  - Model
    - Aggregate
  - Commands
  - Queries
Application.Infrastructure
  - Command/Query Handlers
  - ...
Application.WebApi
  - Controllers that utilize Commands and Queries

別の質問、どこからイベントを発生させるのですか?コマンドハンドラーかドメイン集約か?

34
Sam

コマンドイベントは、非常に異なる懸念事項になる可能性があります。それらは、技術的な問題、統合の問題、ドメインの問題になる可能性があります...

ドメインについて質問した場合、ドメインモデルを実装していると思います(おそらくドメイン駆動設計でも)。

これが事実である場合、私はあなたに本当に簡単な応答を提供しようとするので、あなたは出発点を持つことができます:

  • Command:ビジネス上の意図であり、システムで実行したいものです。ドメイン内のコマンドの定義を保持します。技術的には、単なるDTOです。コマンドの名前は常に必須である必要があります "PlaceOrder"、 "ApplyDiscount" 1つのコマンドは1つのコマンドハンドラーによってのみ処理され、有効でない場合は破棄できます(ただし、失敗しないように、コマンドをドメインに送信する前にすべての検証を可能にする必要があります)
  • Event:これは過去に起こったことです。ビジネスにとって、それは変更できない不変の事実です。ドメインの定義をドメイン内で維持します。技術的には、これはDTOオブジェクトでもあります。ただし、イベントの名前は常に「OrderPlaced」、「DiscountApplied」である必要があります。イベントは一般にpub/subです。 1つの発行元の多くのハンドラ。

その場合、コマンド/クエリハンドラーはインフラストラクチャでの単なる実装ですか?

コマンドハンドラーは、意味的にはアプリケーションサービスレイヤーに似ています。一般に、アプリケーションサービスレイヤーはドメインのオーケストレーションを担当します。多くの場合、「注文する」などのビジネスユースケースを中心に構築されます。これらのユースケースでは、集約ルート、クエリなどを介してビジネスロジック(常にドメインにカプセル化する必要があります)を呼び出します。これは、トランザクション、検証、セキュリティなどの 横断的関心事項 を処理するのにも適した場所です。 。

ただし、アプリケーション層は必須ではありません。これは、機能要件と技術要件、および行われたアーキテクチャの選択によって異なります。あなたの産卵は正しいようです。システムの境界にコマンドハンドラーを置いたほうがよいでしょう。適切なアプリケーション層がない場合は、コマンドハンドラーがユースケースオーケストレーターの役割を果たすことができます。ドメインに配置すると、横断的な問題を簡単に処理できなくなります。それはトレードオフです。ソリューションの長所と短所を認識する必要があります。あるケースでは機能し、別のケースでは機能しない場合があります。

イベントハンドラについても同様です。私はそれを一般的に扱います

  • イベントが同じ境界コンテキスト内の別のAggregateの変更をトリガーする場合、またはイベントがインフラストラクチャサービスをトリガーする場合のアプリケーションレイヤー。
  • イベントを複数のコンシューマーに分割するか、他の境界コンテキストを統合する必要がある場合のインフラストラクチャレイヤー。

とにかくルールを盲目的に守ってはいけません。常にトレードオフがあり、さまざまなアプローチを見つけることができます。

別の質問、どこからイベントを発生させるのですか?コマンドハンドラーかドメイン集約か?

ドメイン集約ルートから実行しています。ドメインがイベントの発生を担当しているためです。常に技術的なルールがあるため、集約の変更を永続化する際に問題が発生した場合はイベントを発行しないようにしてください。逆の場合も、イベントソーシングで使用したアプローチを採用したのは実用的です。私の集約ルートにはUnpublishedイベントのコレクションがあります。私のリポジトリの実装では、Unpublishedイベントのコレクションを検査して、イベントの発行を担当するミドルウェアに渡します。集約ルートを永続化する例外がある場合、イベントが発行されないように制御するのは簡単です。それはリポジトリの責任ではないと言う人もいますし、私も同意しますが、誰が気にするのでしょう。選択は何ですか。すべてのインフラストラクチャの懸念事項(トランザクション、例外処理など)を使用してドメインに侵入するイベントパブリッシングの扱いにくいコード、または実用的でインフラストラクチャレイヤーですべてを処理するコードがありますか?私は両方を行い、私を信じています、私は実用的であることを好みます。

要約すると、物事を行うための単一の方法はありません。常にビジネスニーズと技術要件(スケーラビリティ、パフォーマンスなど)を把握してください。それに基づいて選択するよりも。ほとんどの場合に私が一般的に行ったことと、それがうまくいったことを説明しました。それは私の意見です。

45
Tomasz Jaskuλa

一部の implementations では、コマンドとハンドラーはアプリケーション層にあります。 Inothers 、彼らはドメインに属しています。前者はしばしばOOシステムで見られ、後者はより機能的な実装で見られます。これも私が自分でやっていることですが、YMMVです。

イベントとは、ドメインイベントを意味する場合、そうですね...はい、ドメインレイヤーで定義して、ドメインオブジェクトから発行することをお勧めします。ドメインイベントはユビキタス言語の重要な部分であり、たとえば Event Storming を実践すれば、ドメインの専門家によって直接造られることもあるので、それらをそこに置くことは間違いなく理にかなっています。

ただし、これらの技術的な詳細の大部分について、確固たるに値するルールはないということを覚えておく必要があると思います。 DDDテンプレートプロジェクト、SOのレイヤー化とコードの「トポグラフィ」については無数の質問がありますが、率直に言って、これらの問題は、特にコンテキストに依存しているため、堅牢でパフォーマンスが高く、保守可能なアプリケーションを作る上で決定的なものだとは思いません。おそらく両方ともDDDアプローチで設計されていても、50人が使用するブログ公開プラットフォームと同じように、毎分何百万もの変更があるトレーディングシステムのコードを編成することはないでしょう。状況に応じて自分で試してみて、途中で学ばなければならないこともあります。

9
guillaume31

コマンドとイベントはDTOです。コマンドハンドラーとクエリを任意のレイヤー/コンポーネントに含めることができます。イベントは、何かが変更されたことを通知するだけです。すべてのタイプのイベント(ドメイン、アプリケーションなど)を持つことができます。

イベントは、ハンドラーによって生成することも、それを集約することもできます。ただし、それらが生成される場所に関係なく、コマンドハンドラーはサービスバスを使用してイベントを発行する必要があります。集約ルート内でドメインイベントを生成することを好みます。

DDDの戦略的観点から見ると、ビジネスの概念と使用例しかありません。ドメインイベント、コマンド、ハンドラーは技術的な詳細です。ただし、すべてのドメインの使用例は通常コマンドハンドラーとして実装されるため、コマンドハンドラーはドメインの一部であり、ドメインで使用されるクエリを実装するクエリハンドラーでもある必要があります。 UIで使用されるクエリは、UIの一部などになる場合があります。

CQRSのポイントは、少なくとも2つのモデルを持つことであり、Commandはドメインモデル自体である必要があります。ただし、ドメインの使用に特化したQueryモデルを使用することはできますが、それでも読み取り(単純化)モデルです。コマンドモデルは更新のみに使用され、読み取りモデルはクエリにのみ使用されると考えてください。ただし、複数の読み取りモデル(特定のレイヤーまたはコンポーネントで使用される)または一般的な(すべてのクエリに使用される)読み取りモデルを使用できます。

2
MikeSW