我阅读了Udi Dahan的Domain Events和几十个其他想法如何做这种事情,我决定使用基于NServiceBus的主机处理事件消息。我做了一些概念验证测试,这似乎工作很好。
我的问题是什么应用层应该实际引发的事件。我不想触发事件,直到有问题的对象已成功持久化(如果持久性失败,则无法发送电子邮件,说明已创建注释)。
另一个问题是,在某些情况下,事件绑定到聚合根下面的对象。在上面的示例中,通过将注释添加到Order.Notes集合并保存该订单来保存注释。这提出了一个问题,它使得很难评估当保存订单时应该触发的事件。我想避免在保存更新的副本之前必须拉取对象的当前副本并查找差异。
>是否适合UI提出这些事件?它知道发生了什么事件,并且只有在成功地让服务层保存对象后才能触发它们。关于控制器触发域事件似乎是错误的。
>存储库在成功存留后是否会关闭事件?
>我应该完全分离事件,让存储库存储一个Event对象,然后由轮询服务拾取,然后变成NServiceBus的事件(或直接从poller服务处理)?
>有没有更好的方法来做到这一点?也许让我的域对象排队事件,只有在对象被持久化后才被服务层触发?
>更新:我有一个服务层,但它似乎繁琐和过度,它通过一个比较过程,以确定什么事件应该被触发时,一个给定的聚合根保存。因为一些事件是粒状的(例如“订单状态改变”),我想我必须检索对象的DB副本,比较属性创建事件,保存新对象,然后发送事件到NServiceBus时保存操作已成功完成。
更新
我最后做的是,在我在下面(way below)发布的答案,是在我的域实体建立一个EventQueue属性,它是一个List< IDomainEvent> ;.然后我添加了事件作为域的变化值得它,这允许我保持在域内的逻辑,我相信是适当的,因为我基于事件发生在一个实体内的事件。 然后,当我在我的服务层持久化对象时,我处理该队列,并实际将事件发送到服务总线。最初,我计划使用使用身份PK的遗留数据库,所以我不得不后期处理这些事件来填充实体的ID,但我最终决定切换到Guid.Comb PK,它允许我跳过步。
解决方法
您的网域:
public class Order { public void ChangeStatus(OrderStatus status) { // change status this.Status = status; DomainEvent.Raise(new OrderStatusChanged { OrderId = Id,Status = status }); } public void AddNote(string note) { // add note this.Notes.Add(note) DomainEvent.Raise(new NoteCreatedForOrder { OrderId = Id,Note = note }); } }
您的服务:
public class OrderService { public void SubmitOrder(int orderId,OrderStatus status,string note) { OrderStatusChanged orderStatusChanged = null; NoteCreatedForOrder noteCreatedForOrder = null; DomainEvent.Register<OrderStatusChanged>(x => orderStatusChanged = x); DomainEvent.Register<NoteCreatedForOrder>(x => noteCreatedForOrder = x); using (var uow = UnitOfWork.Start()) { var order = orderRepository.Load(orderId); order.ChangeStatus(status); order.AddNote(note); uow.Commit(); // commit to persist order } if (orderStatusChanged != null) { // something like this serviceBus.Publish(orderStatusChanged); } if (noteCreatedForOrder!= null) { // something like this serviceBus.Publish(noteCreatedForOrder); } } }