4

チケットと顧客を管理するアプリケーションがあります。顧客は多数のチケットを所有しています。顧客が削除されると、そのチケットも削除されます。これは、オブジェクトが集合体であるかどうかを確認するためのテストの 1 つです。私は、それらが両方とも集約ルートの下にないエンティティであることを選択しました。通常、多くの顧客にまたがるチケットのリストを読み込んで表示します。SQL Server を使用してデータをリレーショナル形式で保存しています。

私のアーキテクチャは次のとおりです。

  1. ASP.NET MVC アプリケーション。
  2. WCF サービス (ファサード)。このサービスには、GetTicket、GetTickets、GetCustomer、GetCustomers などのパブリック メソッドがあります。これには、リポジトリを直接使用します。また、Run というパブリック メソッドもあります。Run は、AssignTicket、ChangeTicketName などのコマンドを受け入れます。コマンドを処理するために、サービス、ITicketService および ICustomerService がファサードに挿入されます。これらのサービスのメソッドは、ファサードからコマンドを実行するために使用されます。サービスは、ITicketRepository と ICustomerRepository を使用してデータを読み込みます。
  3. 関係 SQL Server データベース

Tickets から Customers への参照を維持することに夢中になっています。関係的に、チケットには Customers テーブルにマップされる FK があります。UI が個々のチケットを表示すると、Facade.GetTicket(id) と Facade.GetCustomers() が呼び出されます。すべてのチケットの詳細とそれが属する顧客の名前を表示できます。それはすべて元気でダンディです。しかし、多くの異なる顧客が所有する大量のチケットのリストはどうでしょうか。Ticket は、顧客を参照する Guid のみを保持することに注意してください。UI にリストするチケットごとに顧客の名前を取得するためにファサードを呼び出す必要はありません。チケットに CustomerInfo( CustomerId および CustomerName ) という値オブジェクトを含めることは有効ですか? SQL ステートメントを使用してこのデータを結合することは有効ですか? もし私がいなかったら?リレーショナルデータベースを使用していますか? 顧客の名前が変更された場合はどうなりますか? システムでは、チケットは実際には他の複数のエンティティへの参照も保持しています。

DDD のビジネス ロジックと、UI が表示する必要があるものに断絶があるようです。

4

1 に答える 1

17

顧客が削除されると、そのチケットも削除されます。これは、オブジェクトが集合体であるかどうかを確認するためのテストの 1 つです。

ドメインの専門家が「顧客を削除する」と言うとは思いません。顧客は禁止されるか、アカウントが停止され、チケットはキャンセルまたは譲渡される可能性がありますが、「削除」という用語自体が非常に CRUD 中心です。理想的には、データをハード削除するべきではありません (別のデータ ストアにアーカイブすることはできますが、おそらく?)。IMHO カスケード削除は危険な動きです。あなたのORMにそれを処理させてください。

集約を定義することは、ドメインの専門家によってその集約に対して定義された不変条件に従って、集約の状態が「正しい」という「一貫性の境界」を定義することです。これが、集計境界を定義する方法です。

チケットから顧客への参照を維持することに夢中になっています

エンティティと値オブジェクトが集合体に組み立てられるドメイン モデルと、実際には選択した OOP 言語でのデータベースの単なるコード表現であるデータ モデルとの間には違いがあることを明確にしてください。データ モデルはテーブルであり、ドメイン モデルとはまったく異なる形状になる可能性があります。データ モデルは、実際の参照が存在しないテーブル間に関係を持つことができます。つまり、集計の境界を越えて外部キー参照を持つことができます。これは、データベースで参照整合性を強制するためだけのものです。これらのテーブルをクラスにマップするために ORM を使用する場合、「ナビゲーション プロパティ」はなく、ID 値のみになります。

DDD のビジネス ロジックと、UI が表示する必要があるものに断絶があるようです。

ここで、CQRS について話し始めます。DDD は、問題のドメインでの動作をモデル化することです。ドメイン モデルには多くの動作が含まれている必要があります (また、公開状態が存在しない可能性もありますが、これについては後述します)。ドメイン ロジックを呼び出すときは、1 つのトランザクションで集計を読み込み、必要な動作を呼び出して、結果を保存する必要があります。したがって、リポジトリは次のようになります。

public interface IRepository<TEntity>
{
    TEntity Get(Guid id);
    void Save(TEntity item);
}

ORM マッピングには集約外のエンティティへの参照が含まれていないため、実装は常にすべてを熱心にロードします。実際、文書データベースの方が集約を格納するのに適しているとすでに考えているかもしれませんが、私見ではそのとおりです。

しかし、その後、どのようにクエリを実行しますか? 簡単。クエリを書きます。CQRS を実装している場合、リポジトリを使用せず、慎重に作成されたエンティティとその ORM マッピングを使用しないThin Read Layer があります。クエリを書くだけです。SQL Server を使用している場合は、超高速の Linq to SQL データ コンテキストを組み合わせるか、Dapper または Simple.Data を使用することを検討してください。または、ADO.NET だけでも構いません。ポイントは、クエリはデータベースからデータを取得することであり、そのための動作は必要ありません。

集約永続性のためにドキュメント データベースを使用している場合、CQRS パターンを適切に実装すると、ドメインの動作から生成されたイベントを使用して、完全に別のデータベースを構築できる可能性があります。その「Read Store」のオプションは無限にあります。ここにいくつかのアイデアがあります:

  • ドキュメント db の同じまたは別のインスタンスですが、事前に構築されたビューモデルがあります。
  • SQL データベース (OLTP パフォーマンスではなく読み取りパフォーマンスを最適化するため、第 3 正規形をあきらめて複製を使用できます)。
  • ディスク上に事前に作成された HTML ファイル?

ボーナス チャター (イベント ソーシング)

ドメインからのイベントを使用して、アプリの Thin Read Layer でアクセスする読み取りストアを構築するというアプローチを取ると仮定します (リポジトリや大規模な ORM フレームワークを気にしないでください。少しの Dapper または単純なものを使用してください)。 、適切なイベントと適切なデータを常に保持するにはどうすればよいでしょうか? では、集計自体を実際に保存したことがない場合はどうなるでしょうか。集約によって発行されたイベントを Event Store に格納し、集約ごとに Event Stream を使用するとどうなるでしょうか? 集計をロードする場合は、そのイベントをロードしてメモリ内で再生し、集計の最新の状態を構築します。

これ以上詳しく説明することはしませんが、CQRS とイベント ソーシングは DDD と非常に相性が良く、非常にうまく機能するため、調査する価値があります。:)

于 2013-11-14T00:04:09.897 に答える