DDD,对象图和事件采购

时间:2013-02-08 11:03:43

标签: oop domain-driven-design event-sourcing

简介

此问题与DDD和事件采购有关,其中聚合根以外的聚合中的实体具有事件生成行为。

实施例

以下是我描述的情况示例,其中我确定我想在Aggregate中的其他实体中封装一些逻辑。这可能涉及暂停对实际例子的怀疑以及它是否是一个好的模型。 :)

我正在尝试建模DeliveryRun聚合根(AR),这是车辆执行交付的行程。在它离开之前,它必须具有最新的DeliveryManifest。它的“最新性”告诉我DeliveryManifest是由AR定义的DeliveryRun一致性边界内的实体。

好的,到目前为止。

我正在使用事件采购方法 - Greg Young所教导的方法,并在Regalo library中实施。这意味着如果没有任何行为,AR(DeliveryRun)实际上不需要任何实体(例如SalesOrder可能没有SalesOrderLines,因为它记录了诸如{{1}之类的事件而不是} {/ {}}。

然而,ItemsAdded周围会有一些逻辑。具体而言,一旦首次请求清单,当项目被添加到交付时,需要创建新版本的清单。这意味着我们可以确保在没有最新清单的情况下驾驶员不会离开。

如果我要将逻辑封装在ItemsRemoved对象中(不会被序列化和存储;我们使用的是事件源,而不是AR),我该如何捕获事件?

我正在考虑的选项

  • 事件是由DeliveryManifest实体生成的,而是针对DeliveryManifest本身保存的(然后需要知道如何将这些事件重播到DeliveryManifest时从事件商店加载)?

  • 是否应该没有DeliveryRun(除了可能作为数据结构)并且所有逻辑/事件都由DeliveryManifest直接实现?

  • DeliveryManifest是否应该拥有AR并确保DeliveryRun被告知当前舱单的ID?由于这会将清单对象置于DeliveryManifest的一致性边界之外,因此我需要构建一些事件处理来订阅与清单相关的DeliveryRun中的更改,以便更新/无效等等。

  • 实现一种不同的样式来捕获类似于Udi的DomainEvents模式的事件。这意味着更改Regalo库,但我认为可以相当容易地支持这两种模式。这将允许捕获聚合内的所有实体生成的所有事件,以便可以针对AR保存它们。我需要考虑加载/重放的解决方案......

2 个答案:

答案 0 :(得分:1)

除非是一致性边界,否则我会避免使DeliveryManifest成为另一个聚合根。

许多样本都没有解决这个问题。似乎聚合根应该从其中的实体收集事件,并将它们分发到正确的实体以便稍后加载,这似乎是您的选择1.

如果没有与DeliveryManifest相关联的行为,则选项2也非常好。

答案 1 :(得分:0)

机械答案......你可以在这里做出很多变化。基本上,您必须决定谁将收集所有这些事件:根(此处显示)或每个实体(此处未显示的方法)分别。从技术上讲,你有很多选择来实现下面显示的观察行为(想想Rx,手动编码的中介等)。我将大部分基础设施代码映射到实体中(这里缺少抽象)。

public class DeliveryRun {
  Dictionary<Type, Action<object>> _handlers = new Dictionary<Type, Action<object>>();
  List<object> _events = new List<object>();

  DeliveryManifest _manifest;

  public DeliverRun() {
    Register<DeliveryManifestAssigned>(When);
    Register<DeliveryManifestChanged>(When);
  }

  public void AssignManifest(...) {
    Apply(new DeliveryManifestAssigned(...));
  }

  public void ChangeManifest(...) {
    _manifest.Change(...);
  }

  public void Initialize(IEnumerable<object> events) {
    foreach(var @event in events) Play(@event);
  }

  internal void NotifyOf(object @event) {
    Apply(@event);
  }

  void Register<T>(Action<T> handler) {
    _handlers.Add(typeof(T), @event => handler((T)@event));
  }

  void Apply(object @event) {
    Play(@event);
    Record(@event);
  }

  void Play(object @event) {
    Action<object> handler;
    if(_handlers.TryGet(@event.GetType(), out handler)) {
      handler(@event); //dispatches to those When methods
    }
  }

  void Record(object @event) {
    _events.Add(@event);
  }

  void When(DeliveryManifestAssigned @event) {
    _manifest = new DeliveryManifest(this);
    _manifest.Initialize(@event);
  }

  void When(DeliverManifestChanged @event) {
    _manifest.Initialize(@event);
  }
}

public class DeliveryManifest {
  Dictionary<Type, Action<object>> _handlers = new Dictionary<Type, Action<object>>();
  DeliveryRun _run;

  public DeliveryManifest(DeliveryRun run) {
    _run = run;
    Register<DeliveryManifestAssigned>(When);
    Register<DeliveryManifestChanged>(When);
  }

  public void Initialize(object @event) {
    Play(@event);
  }

  public void Change(...) {
    Apply(new DeliveryManifestChanged(...));
  }

  void Register<T>(Action<T> handler) {
    _handlers.Add(typeof(T), @event => handler((T)@event));
  }

  void Play(object @event) {
    Action<object> handler;
    if(_handlers.TryGet(@event.GetType(), out handler)) {
      handler(@event); //dispatches to those When methods
    }
  }

  void Apply(object @event) {
    _run.NotifyOf(@event);
  }

  void When(DeliveryManifestAssigned @event) {
    //...  
  }

  void When(DeliveryManifestChanged @event) {
    //...  
  }
}

P.S。我把它写成“我的头脑”,请原谅我编译错误。