• 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:处理数据库事务中的并发

  • 相关阅读:
    理解 QEMU/KVM 和 Ceph(2):QEMU 的 RBD 块驱动(block driver)
    Neutron VxLAN + Linux Bridge 环境中的网络 MTU
    理解 QEMU/KVM 和 Ceph(1):QEMU-KVM 和 Ceph RBD 的 缓存机制总结
    [译] 企业级 OpenStack 的六大需求(第 3 部分):弹性架构、全球交付
    [译] 企业级 OpenStack 的六大需求(第 2 部分):开放架构和混合云兼容
    [译] 企业级 OpenStack 的六大需求(第 1 部分):API 高可用、管理和安全
    Javascript中的Array(数组) 、{}(映射) 与JSON解析
    HTML中显示特殊字符,如尖括号 “<”,">"等等
    Ubuntu 12.04 安装配置 Apache2
    Python中函数的参数传递与可变长参数
  • 原文地址:https://www.cnblogs.com/happyframework/p/3061157.html
Copyright © 2020-2023  润新知