設計Akka.NET領域事件和命令的最佳實踐 | Petabridge

banq發表於2019-08-07

這是一篇.NET中Akka的領域事件和命令設計文章,闡述如何透過設計事件使Akka.NET程式設計更容易。詳細點選標題見原文:

1. 慷慨地使用標記/身份介面
如果我們有大量的域事件用於交易股票,所有這些事件都有幾個常見的識別符號和屬性,對於路由,分片或分發這些訊息非常有用:

  1. 股票程式碼(MSFT,TEAM,AMD等......);
  2. 訂單的ID; 
  3. 它們都代表由於交易者活動而發生的實時交易事件 - 這與交易所發出的事件不同,該事件表明特定股票程式碼的最新“市場價格”是什麼。


2. 始終使您的域訊息不可變
這是因為如果您向同一程式內的本地執行的許多actor傳送相同的訊息 - 所有這些actor都會收到對該訊息的同一副本的引用。如果您使此訊息型別變為可變,如果一個actor修改此訊息上的任何屬性或欄位,那些更改將傳播到處理相同訊息的所有其他actor - 這就是所謂的在併發程式設計中“副作用” 。
因此,我們需要確保預設情況下在actor之間傳送的所有域訊息都是不可變的。以下是如何做到這一點:

  1. 將所有屬性和欄位設定為只讀 - 可以透過訊息的建構函式設定值的唯一方法;
  2. 切勿將正常集合,即List<T>或Dictionary<K,V>,作為一個訊息類的屬性-總是暴露System.Collections.Generic集合為IReadOnlyCollection<T>,IReadOnlyList<T>,IReadOnlyDictionary<K,V>,等;
  3. 確保傳遞給訊息建構函式的物件本身是不可變的。

3. 可以輕鬆將域實體複製到不可變訊息中
根據我們的“如何使您的訊息不可變”清單中的第3項,確保您的actor透過不可變訊息與其他actor共享的資料也是不可變的本身是一種很好的做法。
我們將所有內部狀態複製到新集合中並將其返回到不可變訊息中。

4. 將ToString()方法重寫為漂亮輸出領域事件
在對生產進行故障排除時,Akka.NET系統開發人員通常依賴於Akka.NET的日誌記錄基礎設施和Phobos actor跟蹤等工具- 但從這些系統中獲取良好資訊通常需要詳細列印出在整個系統中傳播的關鍵訊息的內容。
管理此方法的最簡單方法是覆蓋object.ToString()每個域事件上的方法,並將其自定義為“非常列印”事件的狀態。

/// <summary>
/// Concrete <see cref="IPriceUpdate"/> implementation.
/// </summary>
public sealed class PriceChanged : IPriceUpdate, IComparable<PriceChanged>
{
    public PriceChanged(string stockId, decimal currentAvgPrice, DateTimeOffset timestamp)
    {
        StockId = stockId;
        CurrentAvgPrice = currentAvgPrice;
        Timestamp = timestamp;
    }

    public DateTimeOffset Timestamp { get; }

    public decimal CurrentAvgPrice { get; }

    public string StockId { get; }

    public int CompareTo(PriceChanged other)
    {
        if (ReferenceEquals(this, other)) return 0;
        if (ReferenceEquals(null, other)) return 1;
        return Timestamp.CompareTo(other.Timestamp);
    }

    public int CompareTo(IPriceUpdate other)
    {
        if (other is PriceChanged c)
        {
            return CompareTo(c);
        }
        throw new ArgumentException();
    }

    public override string ToString()
    {
        return $"[{StockId}][{Timestamp}] - $[{CurrentAvgPrice}]";
    }
}


當您遵循這種方法時,所有關於如何將域事件呈現為文字的邏輯都會成為訊息本身的一部分,以及許多不同的基礎結構,這些基礎結構可能都需要列印出訊息的內容(日誌記錄,異常)處理等...如果ToString()正確實施,都可以使用一致的顯示格式。

相關文章