我一直在研究结合领域事件的领域驱动设计。我真的很喜欢这些事件提供的关注点分离。我遇到了持久化域对象和引发域事件的顺序问题。我想在域对象中引发事件,但我希望它们对持久性无知。
我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
事件。这种方法的缺点之一是,通过查看Order
域对象,并不清楚哪些事件是由哪些方法引发的。此外,开发人员必须记住在其他地方创建订单时引发事件。感觉不对。
2.队列域事件
另一种选择是将域事件排队并在持久化成功时引发它们。这可以通过以下using
语句来实现,例如:
using (DomainEvents.QueueEvents<OrderCreated>())
{
var order = new Order(cart, customer);
_orderRepositorty.Add(order);
_unitOfWork.Commit();
}
该QueueEvents<T>
方法将设置一个布尔值,true
并且该DomainEvents.Raise<T>
方法会将事件排队而不是直接执行它。在 dispose 的回调中QueueEvent<T>
,队列中的事件被执行,以确保持久化已经发生。这看起来相当棘手,它需要服务知道在域对象中引发了哪个事件。在我提供的示例中,它也只支持引发一种类型的事件,但是,这可以解决。
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);
}
现在我的问题是,我还有其他选择吗?我错过了什么吗?您如何看待我提出的解决方案?