简体   繁体   English

没有服务定位器的域事件

[英]Domain Events without Service Locator

Giving the default implementation of Domain Events: 提供域事件的默认实现:

Interface that represents an domain event: 表示域事件的接口:

public interface IDomainEvent { }

Interface that represents a generic domain event handler: 表示通用域事件处理程序的接口:

public interface IEventHandler<T> where T : IDomainEvent

Central access point to raise new events: 筹集新活动的中央接入点:

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

Consuming: 消费:

public class SaleCanceled : IDomainEvent
{
    private readonly Sale sale;

    public SaleCanceled(Sale sale)
    {
        this.sale = sale;
    }

    public Sale Sale
    {
        get{ return sale; }
    }
}

Service that raises the event: 提升活动的服务:

public class SalesService
{
     public void CancelSale(int saleId)
     {
          // do cancel operation

          // creates an instance of SaleCanceled event

          // raises the event
          DomainEvents.Raise(instanceOfSaleCanceledEvent);
     } 
}

Is there another approach to use Domain Events without use of Service Location anti-pattern? 是否有另一种方法可以在不使用服务位置反模式的情况下使用域事件?

I guess in your case you really do not have to. 我想在你的情况下,你真的不需要。 Using dependency injection you could inject a IDomainEventDispatcher implementation into your service. 使用依赖注入,您可以将IDomainEventDispatcher实现注入到您的服务中。

The reason why I think a singleton like this has made it into the mainstream it was one of the first implementations to be proposed by some prominent developers and at first it doesn't feel too wrong. 我之所以认为像这样的单身人士已经成为主流,它是一些着名开发人员提出的首批实现之一,起初并不觉得太错误。 The other reason is that events may need to be raised from within the domain: 另一个原因是可能需要从域内引发事件:

public class Customer
{
    public void Enable()
    {
        _enabled = true;

        DomainEvents.Raise(new CustomerEnabledEvent(_id));
    }
}

At some stage I came across this post by Jan Kronquist: http://www.jayway.com/2013/06/20/dont-publish-domain-events-return-them/ 在某些阶段,Jan Kronquist发现了这篇文章: http//www.jayway.com/2013/06/20/dont-publish-domain-events-return-them/

This is the third time I have added that link to my answers since I have to give credit to this for changing my thinking. 这是我第三次将这个链接添加到我的答案中,因为我必须赞扬这一点以改变我的想法。 However, I think I'll stop doing that now. 但是,我想我现在就不再那样做了。 Sorry Jan :) 对不起Jan :)

So the point is that we can change our implementation to the following: 所以关键是我们可以将实现更改为以下内容:

public class Customer
{
    public CustomerEnabledEvent Enable()
    {
        _enabled = true;

        return new CustomerEnabledEvent(_id);
    }
}

Now our service can be changed to use an injected dispatcher: 现在我们的服务可以更改为使用注入的调度程序:

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

So no singleton is required and you can happily inject the dependency. 所以不需要单例,你可以愉快地注入依赖。

I have never used a static DomainPublisher and I have other arguments than @Eben on how I handle it. 我从未使用过静态DomainPublisher,除了@Eben之外我还有其他参数来处理它。 This is just my personal experience and here are some of the reasons that I want to share : 这只是我的个人经历,以下是我想分享的一些原因:

  • because aggregate root produce events and the rule of thumb is that you should not inject anything inside your entitites, when you use injectable Domain Publisher you have to either introduce a domain service to invoke domain logic on the aggregate and raise an event with the Domain Publisher, either you handle it like explained by @Eben in the application service or you break the rule of thumb and you make your entitites injectable. 因为聚合根产生事件和经验法则是你不应该在你的权利中注入任何东西,当你使用可注入的域发布者时,你必须引入域服务来调用聚合上的域逻辑并使用域发布者引发事件要么你在应用程序服务中像@Eben解释的那样处理它,要么你违反了经验法则,你可以使你的权利注入。
  • if you chose to use a domain services when not needed, just for injecting a domain publisher makes your code more cluttered. 如果您在不需要时选择使用域名服务,那么仅注入域名发布者会使您的代码更加混乱。 The business intent is less obvious and it adds more objects to manage and their dependencies than needed. 业务意图不那么明显,它增加了更多的对象来管理及其依赖性。

What I do, is to have a collection of events to publish on the aggregate root entity. 我所做的是在聚合根实体上发布要发布的事件集合。 Each time an event should be published, it is just added to the collection. 每次发布一个事件时,它都会被添加到集合中。 I inject the domain publisher into the aggregate root repository. 我将域发布者注入聚合根存储库。 Thus publishing of events can be handled at the infrastructure level by the domain publisher in the repository. 因此,事件的发布可以由存储库中的域发布者在基础结构级别处理。 Because a domain publisher implementation very often has to deal with middleware like queues and buses, infrastructure level is right place to handle it correctly IMO. 由于域名发布者实施经常需要处理队列和总线等中间件,因此基础架构级别是正确处理IMO的正确位置。 You can more easily deal with strategies of how you deal with publishing events when for example there is an exception when saving your entity to the database. 您可以更轻松地处理有关如何处理发布事件的策略,例如将实体保存到数据库时会出现异常。 What you would not want is to publish events and not save entitites in your database or the other way round. 您不希望的是发布事件而不是保存数据库中的权限或反之。

If you create class EventHandlerFactory with generic method Create<T> and T is constrained by IEventHandler<T> type, then this class will not service locator, but factory, because you create only IEventHandler<T> instances. 如果使用泛型方法Create<T>创建类EventHandlerFactory并且T由IEventHandler<T>类型约束,则此类不会为locator提供服务,而是为factory提供服务,因为您只创建IEventHandler<T>实例。 At the same time Service locator is like God Object , he know everything. 同时服务定位器就像上帝对象 ,他知道一切。

More about this here 更多关于这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM