DDD と PropertyChanged/ColactionChanged イベントの組み合わせが最善のアイデアになるのではないかと心配しています。問題は、これらのイベントに基づいてロジックを作成すると、1 つの PropertyChanged が別の PropertyChanged につながり、すぐに制御を失うため、複雑さを管理するのが非常に困難になることです。
ProportyChanged イベントと DDD が完全に適合しないもう 1 つの理由は、DDD ではすべてのビジネス オペレーションを可能な限り明示的に行う必要があるためです。DDD は技術的なものをビジネスの世界に持ち込むものであり、その逆ではないことに注意してください。また、PropertyChanged/CollectionChanged に基づくことはあまり明示的ではないようです。
DDD の主な目標は、集約内の一貫性を維持することです。つまり、集約を呼び出す操作が有効で一貫性があるように、集約をモデル化する必要があります (もちろん、操作が成功した場合)。
モデルを正しく構築すれば、「構築」トランザクションについて心配する必要はありません。集計に対する操作は、トランザクション自体である必要があります。
あなたのモデルがどのように見えるかはわかりませんが、PropertyChanged イベントに依存する代わりに、責任を集約ツリーの 1 レベル「上」に移動し、プロセスに追加の論理エンティティを追加することを検討するかもしれません。
例:
ステータスを持つ支払いのコレクションがあり、支払いが変更されるたびに、顧客注文の合計残高を再計算したいとします。支払いコレクションへの変更をサブスクライブし、コレクションが変更されたときに顧客でメソッドを呼び出す代わりに、次のようにすることができます。
public class CustomerOrder
{
public List<Payment> Payments { get; }
public Balance BalanceForOrder { get; }
public void SetPaymentAsReceived(Guid paymentId)
{
Payments.First(p => p.PaymentId == paymentId).Status = PaymentStatus.Received;
RecalculateBalance();
}
}
お気づきかもしれませんが、お客様全体の残高ではなく、単一の注文の残高を再計算します。ほとんどの場合、顧客は別の集計に属しており、必要に応じてその残高を照会するだけで問題ありません。これは、まさにこの「集計内のみの一貫性」を示す部分です。この時点では、他の集計は気にせず、単一の注文のみを処理します。それが要件に合わない場合、ドメインは正しくモデル化されていません。
私が言いたいのは、DDD では、すべてのシナリオに適した単一のモデルは存在しないということです。成功するには、ビジネスがどのように機能するかを理解する必要があります。
上記の例を見ると、トランザクションを「ビルド」する必要がないことがわかります。トランザクション全体がSetPaymentAsReceived
メソッド内にあります。ほとんどの場合、1 つのユーザー アクションは、集計を伴うエンティティの 1 つの特定のメソッドにつながる必要があります。このメソッドは、ビジネス オペレーションに明示的に関連しています (もちろん、このメソッドは他のメソッドを呼び出すこともできます)。
DDD のイベントにはドメイン イベントという概念がありますが、PropertyChanged/CollectionChanged の技術イベントとは直接関係ありません。ドメイン イベントは、集計によって完了したビジネス オペレーション (トランザクション) を示します。
全体として、ドメイン モデルのすべてのロジックをプッシュすると、ドメイン モデルがかなり複雑になるようです。
もちろん、複雑なビジネス ロジックを使用するシナリオで使用されることになっているためです。ただし、ドメインが正しくモデル化されていれば、この複雑さを簡単に管理および制御できます。これが DDD の利点の 1 つです。
例を提供した後に追加:
では、Project という名前の集約ルートを作成するのはどうでしょうか。Repository から集約ルートを構築する場合、NetworkData で埋めることができ、操作は次のようになります。
public class Project
{
protected List<Network> networks;
protected List<NetworkData> networkDatas;
public void Mutate(string someKindOfNetworkId, object someParam)
{
var network = networks.First(n => n.Id == someKindOfNetworkId);
var someResult = network.DoSomething(someParam);
networkDatas.Where(d => d.NetworkId == someKindOfNetworkId)
.ToList()
.ForEach(d => d.DoSomething(someResult, someParam));
}
}
NetworkEditor は、NetworkId を使用する Project を通じてではなく、Network 上で直接動作しません。