写点什么

领域驱动设计 101 - 聚合

用户头像
luojiahu
关注
发布于: 2021 年 07 月 08 日
领域驱动设计101 - 聚合

考虑一个支持作者通过多个渠道发布文章的系统,识别业务领域中的实体可以得到:文章、作者、渠道三个明显的实体类型。假设文章支持文字、图片、视频以及几种形式的混合,那么还需要一个文章内容实体,一篇文章中可能由一段文字+若干张图片+一个视频+一段文字构成,那么文字、图片和视频可以分别作为独立的文章内容,这样便于作者分别编辑。因此就有了如下的实体关系:



现在进一步考虑这个系统应该支持的功能。除了正常的文章、作者维护以外,系统可能还需要提供文章和作者的下架操作,因为可能某些内容或者作者出现了不适宜展示的内容。


在对文章进行下架时,我们需要考虑是否删除文章内容,答案似乎显而易见:既然删除了文章肯定要删除文章内容。那么下架作者呢,是否应该删除作者名下的文章呢?


有经验的开发者可能立即识别到,可以不做物理删除,通过增加逻辑删除状态就可以实现下架的功能。但是问题依然存在,即使采用状态字段,我们是否需要在下架的同时处理关联的内容,对于作者来说,如果作者下架了我们是否还能让读者浏览他已经发布的内容?这似乎依赖于需求。而对于文章来说,如果文章下架了,文章的内容一定要修改为逻辑删除状态吗?既然已经在各种入口浏览不到下架的文章了,其内容是否删除似乎并不重要。


在我们的业务领域中,充满着各种实体间关系,类似上面的例子,我们在设计系统时,必须考虑这些关系以及在不同情况下面如何处理这些关系。


上面的例子相对比较简单,在复杂一些的系统中,各个实体之间可能都有联系,而且通常这些关系并不好维护,在不同的状态下如何维护关系通常取决于业务规则,那么有没有一种相对系统的方法来指导我们处理这些复杂的关系呢。

聚合的定义

在领域驱动设计中,解决这种问题的有力武器便是聚合。


我们面临的复杂业务对象关系之所以呈现显得复杂,通常是由于没有定义出他们之间的边界。这些由业务规则约束着的关系只是表面,我们真正需要处理的实际上是业务规则,由那些不变的业务规则约束着的一组实体和值对象就构成一个聚合。


一个聚合有明显的边界,在这个边界内部的对象之间有比较紧密的联系,而所有业务规则所关注的核心实体便是这个聚合的根。


所以,对于一个聚合来说,最关键的是确定其 边界


例如,对于上面的例子,我们可以划分出这样的聚合:



上面划分出了三个聚合,其中作者和渠道都只有一个实体构成,在实际的系统中这种仅由一个实体构成的聚合通常占绝大多数。文章由文章实体和文章内容构成。


那么这么划分的原则是什么呢?

聚合的原则

  • 在一个聚合范围内,生命周期的每个阶段满足一些不变的固定规则

  • 文章和文章内容划分至一个聚合内似乎是显而易见的。因为文章内容是依托与文章而存在的,内容和文章必须保持修改同步,在实现上可能会通过事务来控制他们之间的一致性。

  • 通过事务实现一致性规则的控制看起来顺理成章,但是也需要考虑一些比较大的聚合,如果聚合内实体数量较多,那么一个大的事务可能会影响系统的整体性能表现。因此也需要结合实际情况进行一定取舍。一般情况下,采用一个事务处理聚合中的多个实体的变化是最好的选择。

  • 而文章和作者、文章和渠道之间虽然有一定联系,但是并没有需要保持一致的业务规则约束,作者的状态变化并不一定会决定文章的状态,一个作者发布文章数量的变化也不会影响其本身的状态。随着系统的演化,可能出现关于作者的其他需求,这些需求可能与文章并没有任何关系,因此将他们划分至不同的聚合也便于应对将来的需求。


  • 设计小聚合

在上面的划分中,作者和渠道被划分独立的聚合,这是比较常见的也是符合系统要求的划分方法。将聚合设计得尽可能小有以下几方面的考虑:

  • 性能优势。在一个事务里面处理单个实体构成聚合的变化并不需要多少计算资源,在操作数据库表时也通常对应着一张表。这会显著提升系统的性能表现。

  • 降低事务规模,降低冲突,提高执行成功率。较小规模事务带来性能优势的同时也降低了事务之间冲突的可能性,因此能够提供系统中交易执行成功率。

  • 系统可维护性的优势。在系统的任何阶段,如果没有必要,将聚合设计得尽可能小意味着为系统中各个聚合独立应对需求变化提供了更多的可能,在后续需求变化时也更加应对自如。


  • 通过唯一标识引用其他聚合

虽然在聚合之间划分了清晰的边界,但是聚合之间通常不可能完全独立,他们之间还会存在联系。

在前面的聚合划分示意图中,文章中标注了属性 author_id 和 channel_id,这和作者以及渠道本身的唯一标识相关联,通过这种唯一标识引用聚合,以相对轻量的方式实现了聚合之间关联关系的维护,在通过关联关系进行查找时也可以轻松定位关联的其他聚合实体。


  • 在边界之外,使用最终一致性

在聚合的边界之外,没必要再通过强一致性事务维护聚合之间的联系。因为强一致性通常意味着较低的性能,对于那些不紧密的联系,在业务允许的前提下,最好通过引入消息组件等形式来实现最终一致性,既提供关联关系的维护能力,又不会带来系统性能的显著降低。

发布于: 2021 年 07 月 08 日阅读数: 16
用户头像

luojiahu

关注

喜欢思考组织、过程、产品的后端开发 2017.01.08 加入

还未添加个人简介

评论

发布
暂无评论
领域驱动设计101 - 聚合