これらのクエリは、優れたアーキテクチャのどこに適合しますか?
私はしばらくの間問題について考えていました、そして私はこれにどのようにアプローチするかを釘付けにしたとは本当に感じません。
私は可能な限りDDDを使用しようとしていますが、一部のプレゼンテーションシナリオは、パフォーマンスの点でこれに実際には適合しません。
レンタカーのシステムがあり、そこにすべての車とそれらが貸し出された回数を表示する必要があるビューがあるとします。
DDDの世界では、レンタカー用のリポジトリと「貸し出し」用のリポジトリがあると思います。また、それらの間の相互作用を処理したり、契約を作成したりするサービスがあるかもしれないと思います。
でも、レンタカーの見方や貸し出し件数については、どこに置けばいいのかよくわかりません。要件はこのビューに非常に固有であるため、最もパフォーマンスの高いバージョンは、リポジトリによって返されるオブジェクトである「RentalCarsOverview」のようなものを持つことだと思います。しかし、これは非常にビュー固有のクエリでリポジトリを「汚染」します。
それで、誰かが同じ問題に直面したかどうか、そしてそれがどのように解決されたか疑問に思いますか?私はこれを分離するためにCQRSを検討しており、これらの種類のビューのクエリと更新用のドメインモデルを使用しています。これは良いアプローチですか?
また、これらのクエリハンドラに生のSQLを含めるだけでも「問題ありません」か、それとも基盤となるリポジトリを使用してこれらを抽象化するのでしょうか。
回答
最近の通常の答えは、クエリコードパスが「ドメインモデル」をバイパスし、情報のコピーを直接処理するというものです。
ここでの議論は、クエリはドメインモデルに格納されている情報を変更しないため、その情報をドメインオブジェクトに入れて、再び取り出すための一連のセレモニーの恩恵を受けないというものです。
たとえば、すべての情報が同じリレーショナルデータベースに格納されている場合、データベースにクエリを実行し、結果セットの情報を正しい形状に変換して、クライアントに送り返す可能性が高くなります。
クエリに回答するために必要な情報が複数のシステムに分散している場合は、レポートを生成するための特注作業が増えます。レポートの生成に必要な時間がレイテンシーの目標を超えると、レポートの高価な部分をキャッシュできる設計を検討し始めます。場合によっては、これはレポート全体がキャッシュされることを意味します。
CQRSとDDDは完全に一致します。
コマンドは、リポジトリとDDDモデルを経由します。クエリは、生のSQLまたは軽量のORMであり、ビューごとに個別のモデルがあります。
このようにシステムを設計することで、複雑なビジネスルールを確実に処理し、読み取りのパフォーマンスを最大限に高めることができます。
スマートクエリを持つことはDDDコードの臭いであることを指摘し始めましょう。ほとんどの場合、あなたがしなければならないのはすることだけです
- IDで集計を取得します(RESTfulと話します:GET / users / xyz)
- 指定されたルートを持つすべての集計を取得します(/ users?page = 0&limit = 10)
- いくつかの単純なフィルターを使用して集計のサブセットを取得します(RESTfulと話します:GET / users?username_startsWith = johnd&page = 0&limit = 10)
複雑なアプリケーションは複雑なことを明確に行う必要があるため、集約からプロジェクションを構築する必要が生じる可能性があります。明らかに、アグリゲートにはプロジェクションが何であるかについての手がかりがないはずですが、ドメインを一貫して変更するだけでなく、観察するように設計しているため、アグリゲートを構築するために必要なすべてのデータを見つけることができます。
これは、CQRSの概念を導入しなくても合理的なアプローチです。私たちのアプリケーションのほとんどは、CRUDアプリケーションといくつかのドメイン検証ルールです。要件/ストーリー/コードで「概要」「レポート」「統計」「要約」「履歴」などの単語に数回遭遇した場合は、特定のクエリを介してメモリに集計を読み込み、集計を呼び出すだけで管理できることがわかります。メソッド(カウント、合計、平均)eいくつかの投影DTOに対してマッピングを行います
前述のケースがプロジェクトで目立つ場合、アグリゲートをロードして一部のアグリゲート情報のみを取得することはシステムにとって大きな負担になるため、そうです、CQRSが役立つ2つのユースケースのいずれかを満たしています。
顕著な状態の変化がコミットされるたびにドメインイベントをトリガーする機能をアグリゲート(つまり、書き込み側アグリゲート)に提供します
投影を更新および保存するために、投影固有のリポジトリとイベントハンドラー(この場合はonEvent(CarRentedOutEvt evt)など)を持つ別のモジュールを構築します。
あなたは(再び安らかに言えば)非常に馬鹿げた振る舞いをするGET / rents / overview管理APIを持っているでしょう。すべての複雑さは更新時にオフロードされます
複数の読み取りモデルを使用できます。この場合、非正規化された「レポート」読み取りモデルは、「RentedOut」イベントを受信したときに自身を更新するために必要なイベントをサブスクライブするだけです。