2

介绍

这个问题是关于 DDD 和事件溯源,其中聚合中的实体(聚合根除外)具有事件生成行为。

例子

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

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

到目前为止还好。

我为此使用了 Event Sourcing 方法 - Greg Young教授并在Regalo 库中实施的方法。这意味着如果 AR ( DeliveryRun) 没有任何行为,则它们实际上不需要任何实体(例如,aSalesOrder可能没有SalesOrderLines,因为它记录了诸如ItemsAdded/之类的事件ItemsRemoved)。

但是,围绕DeliveryManifest. 具体来说,一旦首次请求清单,当项目被添加到交付时,需要创建清单的新版本。这意味着我们可以确保司机在没有可用的最新清单的情况下不会离开。

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

我正在考虑的选项

  • 事件是否应该由DeliveryManifest实体生成,但针对DeliveryRun自身保存(然后需要知道如何将这些事件重播到DeliveryManifest从事件存储加载时)?

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

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

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

4

2 回答 2

2

我会避免制作DeliveryManifest另一个聚合根,除非它是一个一致性边界。

许多样本不处理这个问题。聚合根似乎应该负责从其中的实体收集事件,并将它们分发给正确的实体以便稍后加载,这似乎是您的选择 1。

如果没有与DeliveryManifest.

于 2013-02-08T12:23:12.840 回答
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) {
    //...  
  }
}

PS我把这个“乱七八糟”编码了,请原谅我的编译错误。

于 2013-02-08T21:29:56.640 回答