聚合一致性
從時間維度考慮,一致性分為“實時一致性”和“最終一致性”。在企業應用中,多數情況都是使用實時一致性。在WEB應用中,為了最大限度的提高系統的吞吐量,經常使用最終一致性,如:博客園的積分和排名計算。
從聚合的維度考慮,一致性分為“內部一致性”和“外部一致性”。內部一致性是指一個聚合實例本身狀態的一致性。外部一致性是指多個聚合實例之間狀態的一致性。
代碼示例
需求
-
- 訂單要記錄訂單原始總額。(內部一致性)
- 訂單有兩種狀態,未提交和提交,不能修改已提交的訂單。(內部一致性)
- 訂單要記錄訂單的總額,總額 = 原始總額 * 折扣,如果客戶是VIP就打8折。(外部一致性)
- 客戶信用評級。(最終一致性)
類圖
核心代碼
Order

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 狀態一致性.Code 8 { 9 public sealed class Order 10 { 11 private readonly List<OrderItem> _orderItems = new List<OrderItem>(); 12 private OrderStatus _status = OrderStatus.UnSubmited; 13 private Decimal _rawTotal = 0M; 14 private Decimal _total = 0M; 15 16 internal Order() { } 17 18 internal void AddOrderItem(OrderItem orderItem) 19 { 20 this.DoValidation(); 21 22 _orderItems.Add(orderItem); 23 24 this.CalculateAndSetRawTotal(); 25 } 26 27 internal void MarkAsSubmited() 28 { 29 this.DoValidation(); 30 31 _status = OrderStatus.Submited; 32 } 33 34 public Guid CustomerId { get; internal set; } 35 36 public Decimal Total 37 { 38 get 39 { 40 return _total; 41 } 42 internal set 43 { 44 this.DoValidation(); 45 46 _total = value; 47 } 48 } 49 50 public Decimal RawTotal 51 { 52 get 53 { 54 return _rawTotal; 55 } 56 } 57 58 private void CalculateAndSetRawTotal() 59 { 60 _rawTotal = _orderItems.Sum(x => x.Price); 61 } 62 63 private void DoValidation() 64 { 65 if (_status == OrderStatus.Submited) 66 { 67 throw new InvalidOperationException("處於提交狀態的訂單不能執行修改操作"); 68 } 69 } 70 } 71 }
OrderItem

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 狀態一致性.Code 8 { 9 public sealed class OrderItem 10 { 11 public OrderItem(Decimal price) 12 { 13 this.Price = price; 14 } 15 16 internal Decimal Price { get; private set; } 17 } 18 }
OrderStatus

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 狀態一致性.Code 8 { 9 public enum OrderStatus 10 { 11 UnSubmited = 0, 12 Submited = 1 13 } 14 }
Customer

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 狀態一致性.Code 8 { 9 public sealed class Customer 10 { 11 public Guid CustomerId { get; set; } 12 public bool IsVip { get; set; } 13 14 internal void ApplyDiscount(Order order) 15 { 16 var discount = 1M; 17 if (IsVip) 18 { 19 discount = 0.8M; 20 } 21 22 order.Total = order.RawTotal * discount; 23 } 24 } 25 }
UnSubmitedService

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 狀態一致性.Code 8 { 9 public sealed class UnSubmitedService 10 { 11 public void CreateOrder(Customer customer, IEnumerable<OrderItem> orderItems, bool autoSubmit) 12 { 13 var order = new Order(); 14 order.CustomerId = customer.CustomerId; 15 16 foreach (var orderItem in orderItems) 17 { 18 order.AddOrderItem(orderItem); 19 } 20 21 customer.ApplyDiscount(order); 22 23 if (autoSubmit) 24 { 25 order.MarkAsSubmited(); 26 } 27 } 28 } 29 }
代碼說明
- OrderItem的成員都實現成了只讀,否則會導致封裝失敗,進而導致內部一致性的失敗。
- Order實現了內部一致性,而且將API盡可能的聲明為internal或private,這樣就保證UI層不會跨越領域服務(外部一致性)直接調用Order。
- UnSubmitedService實現了外部一致性。
如果要完整的實現內部一致性,會盡可能多的采用值類型語義的實體和封裝集合模式。
備注
關於最終一致性的理解我還很膚淺,這里就不丟人現眼了。實時一致性可以參考這篇文章:.NET:處理數據庫事務中的並發。