2013-10-14 60 views
4

當涉及到重放聚合事件時,如何將這些事件Apply這些事件分配給子(非根)實體。事件採集:將事件重新集中到子實體中

到目前爲止,我對於如何解決這個問題有兩個想法。

  1. 得到聚合根路由事件到適當的實體
  2. 有通過ID加載實體的骨料裝載機,並直接應用於他們的活動

你採取什麼樣的方法,什麼工作,什麼沒有?


在我的搜索,我只發現了兩個鏈接,討論這個問題(都採取第一種方法):

Complex Aggregate structures (4.2.3.)
Aggregate roots coordinating their entities in an event sourcing system

回答

7

作爲上述討論的參與者之一,我可以分享一些見解。如果你看看一個像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方法如何與補液階段混合以達到相同的狀態。

現在,理解可能存在影響實體但不屬於任何特定實體的其他行爲(沒有一對一的映射可以這麼說)是很重要的。這只是發生的事情,對一個或多個實體有副作用。正是這些東西讓你體會到實體設計的靈活性。