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