DDD:如何保证聚合的一致性


聚合一致性

从时间维度考虑,一致性分为“实时一致性”和“最终一致性”。在企业应用中,多数情况都是使用实时一致性。在WEB应用中,为了最大限度的提高系统的吞吐量,经常使用最终一致性,如:博客园的积分和排名计算。

从聚合的维度考虑,一致性分为“内部一致性”和“外部一致性”。内部一致性是指一个聚合实例本身状态的一致性。外部一致性是指多个聚合实例之间状态的一致性。

代码示例

需求

    1. 订单要记录订单原始总额。(内部一致性)
    2. 订单有两种状态,未提交和提交,不能修改已提交的订单。(内部一致性)
    3. 订单要记录订单的总额,总额 = 原始总额 * 折扣,如果客户是VIP就打8折。(外部一致性)
    4. 客户信用评级。(最终一致性)

类图

核心代码

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 }

代码说明

  1. OrderItem的成员都实现成了只读,否则会导致封装失败,进而导致内部一致性的失败。
  2. Order实现了内部一致性,而且将API尽可能的声明为internal或private,这样就保证UI层不会跨越领域服务(外部一致性)直接调用Order。
  3. UnSubmitedService实现了外部一致性。

如果要完整的实现内部一致性,会尽可能多的采用值类型语义的实体和封装集合模式。

备注

关于最终一致性的理解我还很肤浅,这里就不丢人现眼了。实时一致性可以参考这篇文章:.NET:处理数据库事务中的并发

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM