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

Hen*_*ema 6 c# asp.net-mvc persistence domain-driven-design domain-events

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

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

public void Checkout(IEnumerable<ShoppingCartItem> cart, Customer customer)
{
    var order = new Order(cart, customer);

    _orderRepositorty.Add(order);
    _unitOfWork.Commit();
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,构造函数Order将引发OrderCreated可由某些处理程序处理的事件.但是,我不希望在实体尚未持久化或持久化以某种方式失败时引发这些事件.

为了解决这个问题,我找到了几个解决方案:

1.提升服务中的事件:

我可以在服务中引发事件,而不是在域对象中引发事件.在这种情况下,该Checkout方法会引发OrderCreated事件.这种方法的缺点之一是,通过查看Order域对象,不清楚哪些事件是由哪些方法引发的.此外,开发人员必须记住在其他地方创建订单时提出事件.感觉不对劲.


2.队列域事件

另一个选项是对域事件进行排队,并在保持成功时引发它们.这可以通过一个using声明来实现,例如:

using (DomainEvents.QueueEvents<OrderCreated>())
{
    var order = new Order(cart, customer);

    _orderRepositorty.Add(order);
    _unitOfWork.Commit();
}
Run Code Online (Sandbox Code Playgroud)

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


3.坚持域名活动

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

public class NotifyCustomer : IDomainEventHandler<OrderCreated>
{
   public void Handle(OrderCreated args)
   {
       // ...
   }
}
Run Code Online (Sandbox Code Playgroud)

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

public class PersistOrder : IDomainEventHandler<OrderCreated>
    {
       public void Handle(OrderCreated args)
       {
           // ...
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在NotifyCustomer行为取决于数据库中保存的顺序,因此PersistOrder事件处理程序应首先执行.是否可以接受这些处理程序引入一个属性,例如表明它们执行的顺序?从DomainEvents.Raise<OrderCreated>()方法的实现中获取:

foreach (var handler in Container.ResolveAll<IDomainEventHandler<OrderCreated>>().OrderBy(h => h.Order))
{
    handler.Handle(args);
}
Run Code Online (Sandbox Code Playgroud)


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

Mar*_*oek 6

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

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

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

使用回滚进行事件聚合器错误处理


第3点更新:

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

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

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

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