web-dev-qa-db-ja.com

複数の集約ルートを処理する必要があるCQRSコマンド

完全な顧客情報を含む注文要求を受け取るビジネスプロセスがあります。

その注文リクエストの外部顧客IDがDBに見つからない場合、その顧客の注文を実際に保存する前に、「注文の作成」コマンドの一部を処理する必要があります。

一致する顧客IDは見つかったが、DBの住所レコードが注文リクエストの住所レコードと異なる場合は、その顧客IDの新しい住所レコードとして挿入する必要があります(多くのCustomerAddressから1の顧客)-注この住所も注文の配送先住所の値オブジェクトの一部として保存されることに注意してください。なぜなら...そうですね...それらは変更されますが、注文は特定の時点のものです。

最初に、Orderエンティティを集約ルートとしてOrders集約を設計しました(AggregateRoot抽象クラスも拡張するベースEntityクラスで定義されたIdプロパティがありますが、簡潔にするためにここでは省略しています)。

public class Order : AggregateRoot 
{
   //ctor ....

   public long CustomerId { get; }
   public Address ShippingAddress { get; } //value type

   private readonly List<OrderItems> _items = new List<OrderItems>();
   public IReadOnlyCollection<OrderItems> Items => _items.AsReadOnly();

   // more props and behavior methods here....
}

「customers」という名前の別の集計に、Customer集計ルートとCustomerAddressエンティティがあります。

public class Customer : AggregateRoot
{
   public string ExternalId { get; }

   private readonly List<CustomerAddress> _addresses = new List<CustomerAddress>();
   public IReadOnlyCollection<CustomerAddress> Addresses => _addresses.AsReadOnly();
   // more props and behavior methods...
   public void AddNewAddress(string externalKey, Address addressInfo)
   {
       _addresses.Add(new CustomerAddress(externalKey, addressInfo, this));
   }
}
public class CustomerAddress : Entity
{
   //ctor ...

   public string ExternalAddressKey { get; }
   public Customer Customer { get; }
   public Address AddressInfo { get; }

   // more props and behavior methods...
}

注文のコンテキストではなく、UIを介してすべてを単独で顧客データを管理(新規追加、更新)する場合があります。ただし、顧客情報が実際に注文リクエストAPIの一部として到着し、内部コマンドハンドラーに送信される時間の約50〜60%です。上記のように、すでに存在するかどうかを確認する必要があります。

集計の境界が正しく設計されていないのではないかと思いますが、適切なCQRS + DDDパターンを使用して上記のビジネス要件を達成する方法を頭の中で何度も行っているため、わかりません。

コマンドハンドラーを一種のプロセスマネージャー/アプリサービスとして扱う必要があるかどうかはわかりません。そうする場合、同じコマンドで複数のトランザクション(2つの異なるアグリゲートで動作)を作成することになるでしょう。 DDDの信条に対して。

これに関するいくつかのガイダンスをいただければ幸いです。ありがとう!

3
Thiago Silva

Eric Evans DDD Bookからのコメント:

一方、あるアカウントから別のアカウントに資金を送金できる機能はドメインサービスです。これは、重要なビジネスルール(たとえば、適切な口座への貸方記入と借方記入)が組み込まれており、「資金送金」が意味のある銀行用語です。この場合、SERVICEはそれ自体ではあまり機能しません。2つのAccountオブジェクトにほとんどの作業を実行するように要求します。ただし、「転送」操作をAccountオブジェクトに配置するのは面倒です。操作には2つのアカウントといくつかのグローバルルールが含まれます

エヴァンス、エリック。

ドメイン駆動設計:ソフトウェアの中心にある複雑さへの取り組み(S.107)。

ピアソン教育。 Kindleバージョン

場合によっては、単一の集約ルートで即時の整合性を実現することが不可能であり、長時間実行されるプロセスを実装することが望ましくない場合があります。次に、ドメインサービスを使用して、複数の集約ルートの変更を調整することは完全に問題ありません。

2
Roman Weis