没有服务定位器的域事件
本文关键字:事件 定位器 服务 | 更新日期: 2024-11-09 03:41:02
给出域事件的默认实现:
表示域事件的接口:
public interface IDomainEvent { }
表示泛型域事件处理程序的接口:
public interface IEventHandler<T> where T : IDomainEvent
用于引发新事件的中央接入点:
public static class DomainEvents
{
public static void Raise<T>(T event) where T : IDomainEvent
{
//Factory is a IoC container like Ninject. (Service Location/Bad thing)
var eventHandlers = Factory.GetAll<IEventHandler<T>>();
foreach (var handler in eventHandlers )
{
handler.Handle(event);
}
}
}
消费:
public class SaleCanceled : IDomainEvent
{
private readonly Sale sale;
public SaleCanceled(Sale sale)
{
this.sale = sale;
}
public Sale Sale
{
get{ return sale; }
}
}
引发事件的服务:
public class SalesService
{
public void CancelSale(int saleId)
{
// do cancel operation
// creates an instance of SaleCanceled event
// raises the event
DomainEvents.Raise(instanceOfSaleCanceledEvent);
}
}
是否有另一种方法可以在不使用服务位置反模式的情况下使用域事件?
我想在你的情况下你真的不必这样做。 使用依赖关系注入,可以将IDomainEventDispatcher
实现注入到服务中。
我认为像这样的单例已经成为主流的原因,它是一些著名开发人员提出的首批实现之一,起初感觉并不太错误。 另一个原因是可能需要从域内引发事件:
public class Customer
{
public void Enable()
{
_enabled = true;
DomainEvents.Raise(new CustomerEnabledEvent(_id));
}
}
在某个阶段,我看到了Jan Kronquist的这篇文章:http://www.jayway.com/2013/06/20/dont-publish-domain-events-return-them/
这是我第三次在我的答案中添加这个链接,因为我必须感谢它改变了我的想法。 但是,我想我现在不会这样做了。 对不起,扬·:)
所以关键是我们可以将我们的实现更改为以下内容:
public class Customer
{
public CustomerEnabledEvent Enable()
{
_enabled = true;
return new CustomerEnabledEvent(_id);
}
}
现在我们的服务可以更改为使用注入的调度程序:
public class CustomerService
{
private IDomainEventDispatch _dispatcher;
private ICustomerRepository _customerRepository;
public CustomerService(ICustomerRepository customerRepository, IDomainEventDispatch dispatcher)
{
_customerRepository = customerRepository;
_dispatcher = dispatcher;
}
public void Enable(Guid customerId)
{
_dispatcher.Raise(_customerRepository.Get(customerId).Enable());
}
}
因此,不需要单例,您可以愉快地注入依赖项。
我从未使用过静态的DomainPublisher,除了@Eben如何处理它之外,我还有其他论据。这只是我的个人经历,以下是我想分享的一些原因:
- 由于聚合根生成事件和经验法则是您不应该在对象中注入任何内容,因此当您使用可注入域发布器时,您必须引入域服务以在聚合上调用域逻辑并向域发布器引发事件,要么按照应用程序服务中的@Eben解释来处理它,要么打破经验法则并使实体可注入。
- 如果您选择在不需要时使用域服务,则仅用于注入域发布者会使您的代码更加混乱。业务意图不太明显,它添加的对象及其依赖项比需要的要多。
我所做的是让一组事件发布在聚合根实体上。每次应发布事件时,它都只是添加到集合中。我将域发布者注入聚合根存储库。因此,事件的发布可以由存储库中的域发布者在基础结构级别处理。由于域发布者实现通常必须处理队列和总线等中间件,因此基础结构级别是正确处理IMO的正确位置。您可以更轻松地处理如何处理发布事件的策略,例如,在将实体保存到数据库时出现异常。您不希望发布事件而不将实体保存在数据库中,或者相反。
如果使用泛型方法 Create<T>
创建类EventHandlerFactory
,并且 T 受IEventHandler<T>
类型的约束,则此类将不是服务定位器,而是工厂,因为您只创建IEventHandler<T>
实例。同时服务定位器就像上帝的对象,他什么都知道。
更多关于这个