在为聚合重放事件时,您如何将Apply
这些事件发送到子(非根)实体。
到目前为止,我对如何解决这个问题有两个想法。
- 获取聚合根以将事件路由到适当的实体
- 有一个聚合加载器,它通过 id 加载实体并直接应用它们的事件
你采取了哪些方法,哪些有效,哪些无效?
在我的搜索中,我只找到了两个讨论这个问题的链接(都采用了第一种方法):
在为聚合重放事件时,您如何将Apply
这些事件发送到子(非根)实体。
到目前为止,我对如何解决这个问题有两个想法。
你采取了哪些方法,哪些有效,哪些无效?
在我的搜索中,我只找到了两个讨论这个问题的链接(都采用了第一种方法):
作为上述讨论的参与者之一,我可以分享一些见解。如果您查看像 NCQRS 这样的项目,它以一种相当明确的方式将实体的构造和水合方式正式化,您会注意到这种方法具有一定的刚性。对于实体,我发现我将它们的存储作为聚合状态从简单的字段、列表或字典发展为专用的集合类,具体取决于维护它们的分散行为。较少的刚性带来了自由,在聚合边界内选择建模。我非常重视这一点。
再水化时的事件路由发生在聚合体的内部。这不是应该外化的IMO。有多种方法可以解决。在我自己的项目中,这就是我以非常轻量级的方式对其进行形式化的方式(此处仅显示实体):
/// <summary>
/// Base class for aggregate entities that need some basic infrastructure for tracking state changes on their aggregate root entity.
/// </summary>
public abstract class Entity : IInstanceEventRouter
{
readonly Action<object> _applier;
readonly InstanceEventRouter _router;
/// <summary>
/// Initializes a new instance of the <see cref="Entity"/> class.
/// </summary>
/// <param name="applier">The event player and recorder.</param>
/// <exception cref="System.ArgumentNullException">Thrown when the <paramref name="applier"/> is null.</exception>
protected Entity(Action<object> applier)
{
if (applier == null) throw new ArgumentNullException("applier");
_applier = applier;
_router = new InstanceEventRouter();
}
/// <summary>
/// Registers the state handler to be invoked when the specified event is applied.
/// </summary>
/// <typeparam name="TEvent">The type of the event to register the handler for.</typeparam>
/// <param name="handler">The state handler.</param>
/// <exception cref="System.ArgumentNullException">Thrown when the <paramref name="handler"/> is null.</exception>
protected void Register<TEvent>(Action<TEvent> handler)
{
if (handler == null) throw new ArgumentNullException("handler");
_router.ConfigureRoute(handler);
}
/// <summary>
/// Routes the specified <paramref name="event"/> to a configured state handler, if any.
/// </summary>
/// <param name="event">The event to route.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="event"/> is null.</exception>
public void Route(object @event)
{
if (@event == null) throw new ArgumentNullException("event");
_router.Route(@event);
}
/// <summary>
/// Applies the specified event to this instance and invokes the associated state handler.
/// </summary>
/// <param name="event">The event to apply.</param>
protected void Apply(object @event)
{
if (@event == null) throw new ArgumentNullException("event");
_applier(@event);
}
}
行为执行期间的事件路由遵循实体生命周期的轮廓:创建、修改和删除。在创建期间,Apply 方法会创建一个新实体(请记住,这是我们唯一可以更改状态的地方)并将其分配给字段,将其添加到列表、字典或自定义集合中。Apply 方法中的修改通常涉及查找受影响的一个或多个实体,或者将事件路由到实体,或者在实体上使用专用的内部方法来使用来自事件的数据应用更改。删除时,Apply 方法会进行查找并删除受影响的一个或多个实体。请注意这些 Apply 方法如何与补水阶段混合以达到相同的状态。
现在,重要的是要了解可能存在影响实体但不“属于”任何特定实体的其他行为(可以说没有一对一的映射)。这只是发生的事情,对一个或多个实体有副作用。正是这些东西让你欣赏实体设计的灵活性。