跳转到主要内容
Chinese, Simplified

我写这篇博客的动机是想分享一些关于聚合的理解——它们是什么以及它们为什么重要。在过去的几个月里,我经历了从对DDD一无所知到深入研究DDD的痛苦,我希望它能帮助初学者,因为他们可能要经历同样的事情。

聚合是概念上属于一起的实体和值对象(域对象)的封装。它还包含一组可以对这些域对象进行操作的操作。

例如,聚合可以是一辆汽车,其中封装的域对象可以是引擎、车轮、车身颜色和灯光;类似地,在制造汽车的环境中,操作可能是:车身油漆、安装轮、安装引擎、安装灯和船。您的业务规则可能是:

  • 一辆准备装运的汽车必须有四个轮子
  • 没有黄色汽车被制造出来
  • 车身喷漆后必须安装车灯
  • 一辆准备装运的汽车必须正好有16盏灯
  • 一辆准备装运的汽车必须有一个发动机和一个油漆过的车身

在不过度批判我对汽车和汽车制造的知识的情况下,我们在c#中有以下集合:

 

public class Car
{
    public Engine Engine { get; private set; }
    private List<Wheel> _wheels;
    public IReadOnlyList<Wheel> Wheels => _wheels;
    public string BodyColour { get; private set; }
    private List<Light> _lights;
    public IReadOnlyList<Light> Lights => _lights;
    public bool IsShipped { get; private set; }    public Car(List<Light> lights, List<Wheel> wheels, string bodyColour, Engine engine, bool isShipped)
    {
        _lights = lights;
        _wheels = wheels;
        BodyColour = bodyColour;
        Engine = engine;
        IsShipped = isShipped;
    }    public void PaintBody(string colour)
    {
        if (colour == "yellow")
        {
            throw new ArgumentException("No yellow cars are allowed");
        }
        if (_lights.Count > 0)
        {
            throw new ArgumentException("Some lights are already installed");
        }
        BodyColour = colour;
    }
    
    public void InstallLight()
    {
        if (_lights.Count == 16)
        {
            throw new ArgumentException("Lights are fully installed");
        }
        if (BodyColour != null)
        {
            throw new ArgumentException("Body is already painted");
        }
    }    public void InstallEngine(Engine engine)
    {
        Engine = engine;
    }    public void InstallWheel(Wheel wheel)
    {
        if (_wheels.Count == 4)
        {
            throw new ArgumentException("Wheels are already fully installed");
        }
        _wheels.Add(wheel);
     }     public void Ship()
     {
        if (_wheels.Count != 4)
        {
            throw new ArgumentException("Wheels are not fully installed yet");
        }
        if (_lights.Count != 16)
        {
            throw new ArgumentException("Lights are not fully installed yet");
        }
        if (Engine == null)
        {
            throw new ArgumentException("Engine is not installed yet");
        }
        if (BodyColour == null)
        {
            throw new ArgumentException("Body is not painted yet");
        }
        IsShipped = true;
    }
}

 

聚合是数据存储传输的基本元素——请求加载或保存整个聚合。事务不应该跨越聚合边界。

-马丁

在本例中,当您从持久层检索Car对象时,您必须检索它的引擎、灯光、车轮和车身颜色。类似地,在保存时,还必须保存这些属性。

为什么这个模式很重要?

聚合本质上是关于定义一致性边界和加强不变性的。

一致性边界是聚合定义的边界。在汽车制造的大背景下,Car保持了汽车制造规则的一致性。不变量只是规则的一个花哨的词。它通过保持Car对象的一致性来执行这些规则。这样做有几个好处:

  • 鼓励采用自顶向下的方法,其中实现由业务需求决定
  • 作为一个抽象层,因此允许应用程序是持久性的未知的

通常,一组定义良好的聚合会覆盖整个持久性层。这意味着业务规则只有一个单一的事实来源,这使得以下工作很容易:

  • 适应业务需求的变化
  • 新加入项目的开发人员要确定项目是关于什么的

风险和缓解

聚合是向持久层写入模型的基础。拥有一组定义良好的聚合是绝对重要的。一组定义良好的聚合是什么样的?

  1. 首先,集合中的不变量必须是成对互斥的。这意味着两个聚合体不能有重叠的不变量和实体,这些不变量和实体可能相互冲突,也可能相互不冲突。这样做的结果是,您的数据存储之间可能存在不一致的地方。
  2. 其次,如果一个集合变得很大,并且有许多域对象,通常这意味着检索/更新数据存储的性能会降低,这显然很大程度上取决于所使用的数据存储的类型。

 

原文:https://medium.com/ingeniouslysimple/aggregates-in-domain-driven-design-5aab3ef9901d

本文:https://pub.intelligentx.net/node/822

讨论:请加入知识星球【首席架构师圈】或者飞聊小组【首席架构师智库】

 

Article
知识星球
 
微信公众号
 
视频号