如何从域对象内调度域事件?

时间:2018-10-25 15:03:15

标签: dependency-injection domain-driven-design domain-events

域对象不应该有任何依赖关系,因此也没有依赖关系注入。但是,当从域对象内调度域事件时,我可能要使用集中式EventDispatcher。我怎么能得到一个?

我不想将事件列表返回给调用者,因为我希望它们保持不透明并保证其分发。这些事件仅应由需要强制最终保持一致约束的其他域对象和服务使用。

3 个答案:

答案 0 :(得分:2)

请参见Udi Dahan's domain events

基本上,您为您的域事件注册一个或多个处理程序,然后引发这样的事件:

public class Customer
{
   public void DoSomething()
   {
      DomainEvents.Raise(new CustomerBecamePreferred() { Customer = this });
   }
}

所有注册的处理程序将被执行:

public void DoSomethingShouldMakeCustomerPreferred()
{
   var c = new Customer();
   Customer preferred = null;

   DomainEvents.Register<CustomerBecamePreferred>(p => preferred = p.Customer);

   c.DoSomething();
   Assert(preferred == c && c.IsPreferred);
}

这基本上是在实施好莱坞原则 (不要打电话给我们,我们会打电话给你),因为您不会直接调用事件处理程序-而是事件引发事件时执行处理程序。

答案 1 :(得分:2)

  

我可能要使用集中式EventDispatcher。我怎么能得到一个?

将其作为参数传递。

它可能看起来不像EventDispatcher,而是像某些域服务,以特定于域的术语描述了所需的功能。编写应用程序时,您选择要使用的服务实现。

答案 2 :(得分:0)

您要同时拥有这两种方式。您需要注入依赖项或反转控件,并让另一个对象管理器在Aggregate和EventDispatcher之间进行交互。我建议您使聚合尽可能简单,以便它们没有依赖关系并保持可测试性。

以下代码示例非常简单,并且不是您要投入生产的代码,而是说明了如何设计没有依赖项的聚合而不在需要它们的上下文之外传递事件列表。

如果您的聚合中包含事件列表:

class MyAggregate 
{
    private List<IEvent> events = new List<IEvent>();

    // ... Constructor and event sourcing?

    public IEnumerable<IEvent> Events => events;

    public string Name { get; private set; }

    public void ChangeName(string name)
    {
        if (Name != name) 
        { 
            events.Add(new NameChanged(name); 
        }
    }
}

然后,您可能会有一个类似于以下内容的处理程序:

public class MyHandler 
{
    private Repository repository;

    // ... Constructor and dependency injection

    public void Handle(object id, ChangeName cmd)
    {
        var agg = repository.Load(id);
        agg.ChangeName(cmd.Name);
        repository.Save(agg);
    }
}

还有一个看起来像这样的存储库:

class Repository
{
    private EventDispatcher dispatcher;

    // ... Constructor and dependency injection

    public void Save(MyAggregate agg)
    {
        foreach (var e in agg.Events)
        {
            dispatcher.Dispatch(e);                
        }
    }
}