具有持久性无知对象的持久性和域事件

我一直在研究域驱动设计和域事件 。 我非常喜欢这些事件提供的关注点的分离。 我遇到了持久化域对象和提升域事件的问题。 我想在域对象中引发事件,但我希望它们是持久性无知的。

我用这个Checkout方法创建了一个基本的ShoppingCartService

 public void Checkout(IEnumerable 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()) { var order = new Order(cart, customer); _orderRepositorty.Add(order); _unitOfWork.Commit(); } 

QueueEvents方法会将布尔值设置为true ,而DomainEvents.Raise方法会将事件排队,而不是直接执行它。 在QueueEvent的dispose回调中,执行排队事件,确保已经发生了持久化。 这似乎相当棘手,它需要服务知道域对象中引发了哪个事件。 在我提供的示例中,它也只支持一种类型的事件,但是,这可以解决。


3.坚持域名活动

我可以使用域事件来持久化对象。 这似乎没问题,除了持久化对象的事件处理程序应该首先执行的事实,但我在某处读到域事件不应该依赖于特定的执行顺序。 也许这并不重要,域事件可能以某种方式知道处理程序应该执行的顺序。 例如:假设我有一个定义域事件处理程序的接口,实现将如下所示:

 public class NotifyCustomer : IDomainEventHandler { public void Handle(OrderCreated args) { // ... } } 

当我想处理持久使用事件处理程序时,我会创建另一个处理程序,派生自同一个接口:

 public class PersistOrder : IDomainEventHandler { public void Handle(OrderCreated args) { // ... } } } 

现在NotifyCustomer行为取决于数据库中保存的顺序,因此PersistOrder事件处理程序应首先执行。 是否可以接受这些处理程序引入属性,例如指示其执行顺序? 快速执行DomainEvents.Raise()方法:

 foreach (var handler in Container.ResolveAll<IDomainEventHandler>().OrderBy(h => h.Order)) { handler.Handle(args); } 

现在我的问题是,我还有其他选择吗? 我错过了什么吗? 你怎么看待我提出的解决方案?

您的(事务性)事件处理程序在(可能是分布式的)事务中登记,或者在事务提交后发布/处理事件。 您的“QueueEvents”解决方案获得了正确的基本想法,但有更优雅的解决方案,例如通过存储库或事件存储发布。 举个例子来看看SimpleCQRS

您可能还会发现这些问题和答案很有用:

CQRS:存储事件并发布它们 – 如何以安全的方式执行此操作?

使用回滚进行事件聚合器error handling


第3点更新:

…但我在某处读到域事件不应该依赖于特定的执行顺序。

无论你的坚持方式如何,事件的顺序绝对重要(在一个集合内)。

现在NotifyCustomer行为取决于数据库中保存的顺序,因此PersistOrder事件处理程序应首先执行。 是否可以接受这些处理程序引入属性,例如指示其执行顺序?

持久化处理事件是一个单独的问题 – 不要使用事件处理程序持久化。 先坚持,然后处理。