实现领域驱动设计-聚合

最难理解的模式

将实体和值对像在一致性边界之内组成聚合。所以理解聚合的关键的一致性边界,可以直接理解为RDB的事务一致性,即一个关联关系在各种操作中,无论成功与失败,都能保证关系是成立的,也就是事务的一致性,在这里称为聚合的不变条件,所以划分聚合,就以满足“业务”的不变条件,去划分聚合的。这里特别指出是业务需要的不变条件,即领域模型的不变条件。

但无论从维护一个恒久的不变条件或一个原子事务来说,成本都很高的,所以要分析出真正的不变条件,就是业务上有需要维持严格一致性的地方,比如转账的减钱和加钱,比如书中示例的修改product名称和更新版本号。其它不确定是否需要或伪需求,都通通排除在聚合之外。这样就是两个原则:“在一致性边界之内建模真正的不变条件”和“设计小聚合”

在之前的章节也强调很多,在一个事务中,只对一个聚合执行命令操作或更新,这也是跟上面的原则是契合的。

那提倡设计为小聚合,一个限界上下文可能就存在很多聚合了,而一个事务又只允许更新一个聚合,那应该其它聚合怎么办呢?首先是通过唯一标识去引用其它聚合,然后通过“异步”事件去更新其它聚合,追求是最终一致性。这里强调是“异步”的,因为如果“同步”的,那按照常见的框架,比如spring,事务是继承的,会被当成一个事务内的,而且如果是同步的,那更新第二个聚合操作的耗时和成功与否就会影响对聚合一的更新了,所以跟最终一致性对应的,一定要实现异步事件,其实这在使用最终一致性的场景也是适合的。这就是另外两个原则了:“通过唯一标识引用其他聚合”和“在边界之外使用最终一致性”

这里插入一个概念澄清:聚合根一定是实体吗,可以是值对像吗?确实是实体,因为聚合的标识就是聚合根的唯一标识,那有唯一标识那肯定就是实体了。书中其实也多次提到“根实体”。除了根实体之外,其它聚合内部模型就建议使用值对像了。

打破原则的理由

书中提到四个可能导致打破原则的理由:“方便用户界面”,“缺乏技术机制”,“全局事务”,“查询性能”

在这几个理由中,比较难处理是第一个“方便用户界面”,可以说是用户体验与技术实现的矛盾,也可以是前端rd和后端rd的矛盾。后端使用最终一致性了,状态没能及时返回到展示层,确实是影响体验的,最差的就是由用户手工刷新,或者由前端定时刷新,确实有需要的情况可以使用服务端推送,相对来说,服务端推送基础设施的建设成本比较高。

“缺乏技术机制”这基本不成为理由,除非你不想改,消息机制、定时器、后台线程这些对平台、语言或框架都是有的。

而“全局事务”两阶段提交三阶段提交事务这种,很难有好的伸缩性,一般工程实践是不采用的。

“查询性能”方面,则建议使用CQRS架构了

实现的注意

迪米特法则和“告诉而非询问”原则

迪米特法则:强调了“最小知识”原则;告诉而非询问原则:一个对象不应该被告知如何执行操作。其实也就是消息隐藏和封装的原则,就是客户端只能使用公开接口,服务端不暴露内部结构,而从DDD来说,就是应用程序建议只使用聚合根的公开接口。其实这样的原则在服务端接口设计更要注意,接口设计要具有一定的封装不能暴露内部结构,而返回的数据只返回必要的属性,不返回多余的

意外的收获

P328 “这个过程将持续进行直到一致性得到满足或者达到重试上限为止”的脚注写着“可以考虑盖帽指数后退算法”,google了一圈竟然没找到盖帽指数后退算法的解释,于是下载到本书的英文版,制作粗糙竟然都没有脚注,于是按这句话的英文原文(This retry process can continue until consistency is achieved, or until a retry limit is reached)搜索一下,找到了这个网页http://www.informit.com/articles/article.aspx?p=2020371&seqNum=5,脚注原文是“Consider attempting retries using Capped Exponential Back-off. Rather than defaulting to a retry every N fixed number of seconds, exponentially back off on retries while capping waits with an upper limit. For example, start at one second and back off exponentially, doubling until success or until reaching a 32-second wait-and-retry cap.”,然后再用“Capped Exponential Back-off”再google就可以找到相应的解释的。其实也很简单,就是重试时不要按固定间隔时间去重试,而按1秒,2秒,4秒,8秒直到上限32秒这样去重试,具体可以参考:Simple Capped Exponential Back-Off。这本书翻译整体还是不错的,但这个脚注翻译得就有点糊弄人了。

你可能感兴趣的:(实现领域驱动设计-聚合)