作爲上述討論的參與者之一,我可以分享一些見解。如果你看看一個像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方法如何與補液階段混合以達到相同的狀態。
現在,理解可能存在影響實體但不屬於任何特定實體的其他行爲(沒有一對一的映射可以這麼說)是很重要的。這只是發生的事情,對一個或多個實體有副作用。正是這些東西讓你體會到實體設計的靈活性。