关于依赖注入:如何从域对象中调度域事件?

How are domain events dispatched from within domain objects?

域对象不应该有任何依赖项,因此也没有依赖项注入。但是,当从域对象内部调度域事件时,我可能希望使用集中的EventDispatcher。我怎么能拿到一个?

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


I'll likely want to use a centralised EventDispatcher. How could I get hold of one?

把它作为一个论点传进来。

它可能不像一个EventDispatcher,而是像一些用特定领域术语描述所需能力的域服务。当组成应用程序时,您可以选择要使用的服务实现。


查看乌迪达汉的域事件

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

1
2
3
4
5
6
7
public class Customer
{
   public void DoSomething()
   {
      DomainEvents.Raise(new CustomerBecamePreferred() { Customer = this });
   }
}

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

1
2
3
4
5
6
7
8
9
10
public void DoSomethingShouldMakeCustomerPreferred()
{
   var c = new Customer();
   Customer preferred = null;

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

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

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


你要求两者兼得。您要么需要注入依赖项,要么反转控件,让另一个对象管理器在聚合和事件调度程序之间进行交互。我建议尽可能保持聚合的简单性,这样它们就不存在依赖关系,并且仍然是可测试的。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
        }
    }
}

那么您可能有一个处理程序,它看起来像:

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
    }
}

以及一个类似于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Repository
{
    private EventDispatcher dispatcher;

    // ... Constructor and dependency injection

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