此问题与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保存它们。我需要考虑加载/重放的解决方案......
答案 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。我把它写成“我的头脑”,请原谅我编译错误。