DDD诊所 —— 聚合过大综合症案例分析

活动简介DDD诊所是由Thoughtworks DDD社区举办的活动,旨在通过分析和解答实施领域驱动设计(DDD)过程中的问题,促进团队成员间的交流与成长,共同提升软件开发水平。未来,我们计划将此类活动对外公开,以分享经验、促进行业交流。

活动详情

  • 就诊日期:2022年6月8日- 患者:某DevOps平台持续集成模块- 诊金:0元(免费义诊)

患者主诉患者主要面临的问题为疑似问题设计,具体表现为:

功能复杂性DevOps平台的持续交付流水线设计功能需在用户界面上实现完整流程规划。该功能包含多个复杂子功能,如:

  • 阶段规划:允许用户定义流水线的各个阶段。- 前置触发条件设置:用户可设定流水线启动的前置条件。- 质量门禁控制:确保流水线在达到一定质量标准后才能继续。

功能扩展与复杂度提升随着功能的不断扩展,流水线设计页面的复杂度也在逐步增加。这种设计的初衷是帮助用户优化持续交付流水线的管理,提高交付的效率与质量。

结论与建议针对上述问题,DDD诊所建议采取以下措施:

  1. 模块化设计:将复杂的功能拆分为独立的模块,降低页面复杂度。2. 用户引导:提供清晰的用户指导和帮助文档,帮助用户理解并使用复杂功能。3. 持续反馈:收集用户反馈,持续优化功能设计,以满足用户需求。 通过这些措施,我们可以帮助患者——即DevOps平台的持续集成模块——解决设计上的问题,提升用户体验和交付效率。 图片

持续交付流水线界面功能介绍

一、阶段设计用户可以自定义流水线的不同阶段,例如:

  • 开发阶段:包含代码编写和初步测试。- 测试阶段:进行更全面的测试,确保代码质量。 每个阶段可以包含多个步骤,例如:
  • Checkout:代码检出。- 编译:将源代码转换为可执行文件。- 构建镜像:创建容器化应用的镜像。- 部署:将应用部署到生产环境。

二、触发条件设置流水线可以通过以下两种方式触发:

  • 代码仓库提交:代码提交到仓库时自动触发流水线。- 定时任务:设置定时触发,如每天凌晨执行。 用户可以设定多个前置触发条件,以确保在满足特定条件时才启动流水线。

三、质量门禁设置在流水线的各个阶段,用户可以设置质量门禁规则,例如:

  • 单元测试覆盖率需大于80%。 质量门禁规则在质量门禁管理功能中设置。如果流水线在执行过程中未能满足这些规则,将阻止其进入下一个阶段,确保代码质量。

四、持续交付流水线领域模型项目架构师根据需求设计了持续交付流水线领域模型,旨在简化用户操作,包括:

  • 代码质量检查- 编译代码- 构建镜像- 部署 这些操作被视为一个整体,并被组织在流水线聚合中。流水线聚合包括:
  • 质量门禁- 阶段配置- 触发规则 该设计确保了流水线中各个组成部分的一致性和协调性。

注意事项- 图中的《map from》表示对象映射自其他上下文,是一种自定义的衍型表示。- 流水线的各个阶段和步骤应根据项目需求进行合理配置。

图片 在持续交付流水线领域模型的实施过程中,我们团队遇到了一些挑战。以下是对这些问题的分析和总结:

引发问题

1. 认知负载上升聚合模型包含了7个实体,每个实体都承载着不同的业务逻辑,这使得认知负载显著增加。同时,集成了多个外部系统,如Sonar、定时任务组件、GitLab或GitHub等,这要求开发和维护人员必须掌握广泛的知识。当需要对系统进行修改时,如质量门禁功能,开发人员需要对整个系统有深入的理解。

2. 部分可用性难以实现由于功能集成了多个第三方系统,一旦某个系统不可用,整个功能都会受到影响。例如,当Sonar服务暂时不可用时,代码触发和阶段维护部分也会受到影响。这使得在部分系统不可用的情况下,实现部分可用性变得困难。

3. 牺牲了性能和并发性当前的设计要求用户在设计界面上对流水线的各个部分进行设计,然后一次性提交。这种设计导致即使用户只修改了部分内容,也需要更新整个聚合中的所有实体,造成性能浪费。此外,当多个用户同时操作不相互影响的部分时,如触发规则和质量门禁,系统会提示冲突,降低了并发处理能力。

诊断

初步诊断表明,我们面临的主要问题是聚合过大综合症。在领域驱动设计(DDD)实践中,合理划分聚合是一个具有挑战性的问题。

聚合过大综合症的病理分析

在DDD实践中,识别大聚合可以通过以下几种情况来识别:

1. 宽聚合一个聚合包含了多个同级实体,存在一个“父亲”和多个“儿子”。当聚合中的实体数量超过三个时,就存在大聚合的风险。

2. 深聚合聚合的层级过深,例如聚合根下有儿子、孙子甚至重孙实体。当层级达到三层时,就存在大聚合的风险。

3. 胖聚合尽管结构简单,但实体对象实例数量庞大。例如,一个订单聚合中可能包含数千个订单行,这种情况下也存在大聚合的风险。

结论为了避免上述问题,我们需要重新审视和设计我们的聚合模型,确保每个聚合的大小和复杂度都保持在合理的范围内。这不仅有助于降低认知负载,提高系统的可用性和并发性,还能避免构建出分布式单体架构,从而充分利用分布式系统的优势。

图片

聚合过大的三个征兆

聚合过大综合症是软件设计中常见的问题,它会导致认知负荷增加、部分可用性下降以及性能问题。本文将探讨如何识别和治疗这一问题。

治疗建议

要解决聚合过大综合症,首先需要合理划分聚合。《领域驱动设计》一书中对聚合模式有明确的定义,可以作为划分聚合的理论依据。

识别聚合的方法

根据《领域驱动设计:软件核心复杂性应对之道》,聚合的定义如下:

在具有复杂关联的模型中,维护对象更改的一致性是困难的。需要维护适用于密切相关的对象组的Invariant,而不仅仅是离散的对象。我们应该将Entity和Value Object聚集到Aggregate中,并定义每Aggregate的边界。选择一个Entity作为根,通过根控制对边界内其他对象的所有访问。

判断聚合的依据

  1. 存在整体部分关系 当一个Entity是另一个Entity的部分时,它们之间存在整体部分关系。例如,汽车轮胎是汽车的一部分,学生是班级的一部分,订单行是订单的一部分。
  2. 固定规则(Invariants) 固定规则或称为不变量,是业务约束下必须遵守的规则。例如,订单的总价必须等于订单行的总价之和,且总价必须小于3000。

应用实例

在订单系统中,如果订单的总价与订单行的总价之和相等,并且有业务约束,那么订单和订单行可以划分为同一个聚合。如果订单仅作为订单行的分组,用户需要按照订单行逐一结账,那么订单和订单行就无需划分成一个聚合。

结论

合理划分聚合是避免聚合过大综合症的关键。通过理解聚合的定义和识别方法,可以有效地维护系统的一致性和可用性。 图片 在软件设计中,固定规则是划分聚合的重要条件,它决定了数据和操作的一致性边界。然而,并非所有情况下都需要将聚合拆分为更小的单元。以下是对聚合模式的分析和应用方案的详细讨论:

固定规则与聚合划分固定规则,如数据库ID的唯一性或订单编号的不重复性,是全局性的约束条件,通常在整个系统中保持一致。这些规则虽然重要,但在业务逻辑的特定场景下,可能并不影响聚合的划分。

聚合规模的考量- 业务固定规则:通常决定了较小的业务一致性边界,有助于确定聚合的规模。- 大聚合的适用性:在某些业务场景中,大聚合可能是不可避免的。此时,需要评估使用大聚合的成本是否可接受。

聚合模式的权衡聚合模式是一种设计模式,用于解决复杂业务中的一致性问题。它通过将一组对象作为一个整体来处理,简化了固定规则的实现。然而,这也可能导致性能损失等代价。

案例分析:流水线的固定规则以流水线为例,分析了其中的固定规则,并提出了以下解决方案:

  1. 步骤顺序依赖:阶段内的步骤必须按照既定的顺序执行。这是由于后续步骤通常依赖于前一步骤的输出。2. 质量门禁规则:阶段的质量门禁和门禁项之间存在固定规则。当门禁项发生变更时,门禁版本也必须更新,以确保业务含义的一致性。

结论在设计聚合时,需要根据具体的业务场景和固定规则来决定聚合的规模。聚合模式提供了一种有效的解决方案,但应根据实际情况灵活应用,避免生搬硬套。

图片 在领域驱动设计(DDD)实践中,合理划分聚合是确保系统性能和业务一致性的关键。本文将探讨如何通过拆分聚合来优化DDD实践,并提供一些相关策略和方法。

聚合拆分的必要性

在DDD中,聚合是一个包含多个实体和值对象的集合,它们作为一个整体被管理和操作。然而,当聚合过大时,可能会带来维护难度、性能问题和可用性损失。因此,将一个大聚合拆分为多个小聚合,每个聚合只包含少量实体,是一种有效的解决方案。

聚合拆分的策略1. 识别核心实体:确定聚合中的核心实体,以及它们与其他实体的关系。2. 维护固定规则:聚合的划分应确保对象之间的固定规则得到维护。3. 考虑整体-部分关系:聚合的划分应反映实体间的整体-部分关系。4. 评估聚合模式的代价:当聚合模式的代价过大时,考虑使用其他方法,如锁机制。

聚合拆分的实践- 案例分析:通过一个具体的案例,展示如何将一个大聚合拆分成四个小聚合。- 性能与一致性的平衡:分析拆分聚合后,如何在性能和业务一致性之间找到平衡点。

聚合拆分的挑战与解决方案- 挑战:识别聚合边界、维护数据一致性、处理跨聚合的事务。- 解决方案:使用事件驱动架构、CQRS(命令查询责任分离)模式等策略来应对挑战。

结论在DDD实践中,合理划分和优化聚合对于保持系统的可维护性、性能和业务一致性至关重要。面对聚合模式的挑战,我们应该灵活运用不同的设计模式和技术,以达到最佳效果。

相关阅读- 如何编写技术文档?- 你有“搜商”吗?

图片