8

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

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);
}


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

4

2 回答 2

7

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

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

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

带有回滚的事件聚合器错误处理


更新第 3 点:

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

不管你坚持的方式是什么,事件的顺序绝对重要(在一个聚合中)。

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

持久化处理事件是不同的关注点——不要使用事件处理程序持久化。先坚持,后处理。

于 2014-03-06T10:29:23.490 回答
-1

免责声明:我不知道我在说什么。⚠️

作为在 中引发事件的替代方法,ShoppingCartService您可以在OrderRepository.

作为对域事件进行排队的替代方法,您可以通过从提供方法的基类继承来将ShoppingCartService它们在聚合中排队。OrderAddDomainEvent

public class Order : Aggregate
{
    public Order(IEnumerable<ShoppingCartItem> cart, Customer customer)
    {
        AddDomainEvent(new OrderCreated(cart, customer))
    }
}

public abstract class Aggregate
{
    private List<IDomainEvent> _domainEvents = new List<IDomainEvent>();

    public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();

    public void AddDomainEvent(IDomainEvent domainEvent)
    {
        _domainEvents.Add(domainEvent);
    }
}
于 2019-05-19T12:29:15.297 回答