我使用域事件模式已经有一段时间了——它使我们能够在域层中封装尽可能多的行为,并为我们应用程序的其他部分订阅域事件提供了一种很好的方式。
目前我们正在使用一个静态类,我们的域对象可以调用它来引发事件:
static class DomainEvents
{
public static IEventDispatcher Dispatcher { get; set; }
public static void Raise<TEvent>(TEvent e)
{
if (e != null)
{
Dispatcher.Dispatch(e);
}
}
}
正如您所看到的,这只不过IEventDispatcher
是实际执行调度或发布事件的工作的垫片。
我们的调度程序实现只是使用我们的 IoC 容器 (StructureMap) 来定位指定类型事件的事件处理程序。
public void Dispatch<TEvent>(TEvent e)
{
foreach (var handler in container.GetAllInstances<IHandler<TEvent>>())
{
handler.Handle(e);
}
}
这在大多数情况下都可以正常工作。但是,这种方法存在一些问题:
仅当实体成功持久化时才应分派事件
参加以下课程:
public class Order
{
public string Id { get; private set; }
public decimal Amount { get; private set; }
public Order(decimal amount)
{
Amount = amount;
DomainEvents.Raise(new OrderRaisedEvent { OrderId = Id });
}
}
在Order
构造函数中,我们提出了一个OrderRaisedEvent
. 在我们的应用层中,我们可能会创建订单实例,将其添加到我们的数据库“会话”中,然后提交/保存更改:
var order = new Order(amount: 10);
session.Store(order);
session.SaveChanges();
这里的问题是在我们成功保存 Order 实体(提交事务)之前引发了域事件。如果保存失败,我们仍然会分派事件。
更好的方法是将事件排队,直到实体被持久化。但是,我不确定如何在维护强类型事件处理程序的同时最好地实现这一点。
在实体被持久化之前不应创建事件
我面临的另一个问题是我们的实体标识符在存储实体(RavenDB - session.Store
)之前不会设置/分配。这意味着在上面的示例中,传递给事件的订单标识符实际上是null
.
由于我不确定如何实际预先生成 RavenDB 标识符,因此一种解决方案可能是将事件的创建延迟到实际保存实体之前,但我又不是如何最好地实现这一点 - 也许排队集合Func<TEntity, TEvent>
?