聚合一致性
从时间维度考虑,一致性分为“实时一致性”和“最终一致性”。在企业应用中,多数情况都是使用实时一致性。在WEB应用中,为了最大限度的提高系统的吞吐量,经常使用最终一致性,如:博客园的积分和排名计算。
从聚合的维度考虑,一致性分为“内部一致性”和“外部一致性”。内部一致性是指一个聚合实例本身状态的一致性。外部一致性是指多个聚合实例之间状态的一致性。
代码示例
需求
- 订单要记录订单原始总额。(内部一致性)
- 订单有两种状态,未提交和提交,不能修改已提交的订单。(内部一致性)
- 订单要记录订单的总额,总额 = 原始总额 * 折扣,如果客户是VIP就打8折。(外部一致性)
- 客户信用评级。(最终一致性)
类图
核心代码
Order
View Code
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
View Code
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
View Code
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
View Code
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
View Code
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:处理数据库事务中的并发。