ドメインイベントと合わせてドメイン駆動設計について勉強しています。私は、これらのイベントが提供する懸念事項の分離が本当に気に入っています。ドメイン オブジェクトの永続化とドメイン イベントの発生の順序で問題が発生しました。ドメイン オブジェクトでイベントを発生させたいと考えていますが、永続性を考慮しないようにしたいと考えています。
ShoppingCartService
このCheckout
メソッドを使用して、基本的な を作成しました。
public void Checkout(IEnumerable<ShoppingCartItem> cart, Customer customer)
{
var order = new Order(cart, customer);
_orderRepositorty.Add(order);
_unitOfWork.Commit();
}
この例では、のコンストラクターは、特定のハンドラーによって処理できるイベントを発生させますOrder
。OrderCreated
ただし、エンティティがまだ永続化されていない場合、または永続化が何らかの形で失敗した場合に、これらのイベントが発生することは望ましくありません。
この問題を解決するために、私はいくつかの解決策を考え出しました:
1. サービスでイベントを発生させます。
ドメイン オブジェクトでイベントを発生させる代わりに、サービスでイベントを発生させることができます。この場合、Checkout
メソッドはOrderCreated
イベントを発生させます。このアプローチの欠点の 1 つは、Order
ドメイン オブジェクトを見ると、どのイベントがどのメソッドによって発生したかが明確でないことです。また、開発者は、注文が別の場所で作成されたときにイベントを発生させることを覚えておく必要があります。気分が悪い。
2. ドメイン イベントをキューに入れる
もう 1 つのオプションは、ドメイン イベントをキューに入れ、永続化が成功したときに発生させることです。using
これは、たとえば次のステートメントで実現できます。
using (DomainEvents.QueueEvents<OrderCreated>())
{
var order = new Order(cart, customer);
_orderRepositorty.Add(order);
_unitOfWork.Commit();
}
QueueEvents<T>
メソッドはブール値を に設定しtrue
、メソッドDomainEvents.Raise<T>
はイベントを直接実行するのではなくキューに入れます。の破棄コールバックでQueueEvent<T>
は、キューに入れられたイベントが実行され、永続化が既に行われていることが確認されます。これはややこしいようで、ドメイン オブジェクトでどのイベントが発生しているかをサービスが認識できるようにする必要がありました。私が提供した例では、発生するイベントの 1 つのタイプのみをサポートしていますが、これは回避できます。
3. ドメイン イベントに永続化する
ドメイン イベントを使用してオブジェクトを永続化できます。オブジェクトを永続化するイベント ハンドラーを最初に実行する必要があるという事実を除いて、これは問題ないように思えますが、ドメイン イベントは特定の実行順序に依存してはならないことをどこかで読みました。おそらくそれはそれほど重要ではなく、ドメイン イベントはハンドラーがどの順序で実行されるべきかをどうにかして知ることができます。例: ドメイン イベントハンドラーを定義するインターフェイスがあるとします。実装は次のようになります。
public class NotifyCustomer : IDomainEventHandler<OrderCreated>
{
public void Handle(OrderCreated args)
{
// ...
}
}
イベント ハンドラーを使用して永続化を処理したい場合は、同じインターフェイスから派生する別のハンドラーを作成します。
public class PersistOrder : IDomainEventHandler<OrderCreated>
{
public void Handle(OrderCreated args)
{
// ...
}
}
}
現在NotifyCustomer
、動作はデータベースに保存されている順序に依存するため、PersistOrder
イベント ハンドラーを最初に実行する必要があります。これらのハンドラーが、たとえば実行順序を示すプロパティを導入することは許容されますか? メソッドの実装からのスナップDomainEvents.Raise<OrderCreated>()
:
foreach (var handler in Container.ResolveAll<IDomainEventHandler<OrderCreated>>().OrderBy(h => h.Order))
{
handler.Handle(args);
}
私の質問は、他に選択肢はありますか?何か不足していますか?そして、私が提案した解決策についてどう思いますか?