前言

原有用户业务线上,属于大熔炉模式,所有和用户相关的业务逻辑都放在用户工程下,用户账号,用户会员,用户积分,活动等等在各个业务逻辑中都有互相掺杂,在面对业务快速发展的新零售电商行业显得力不从心。在进行业务迭代,新需求开发,让整个用户系统伤筋动骨。

一、为什么要使用 DDD

1、贫血症引起的失忆症

项目中的对象都是数据库的映射,俗称 data model,没有任何的业务含义,只是承担了数据库载体,通过脚本式进行业务逻辑开发。 这种模式在简单业务或者单一业务情况下是很好的实现方式,快速开发,快速迭代,通过定义接口方法满足单一职责。但当业务逻辑复杂,业务逻辑发生变更迭代,原有的接口方法会进行膨胀,会出现更多的方法,一条调用链调用的方法数会持续增加,业务逻辑会散落在更多的方法中,原本的代码业务逻辑开始不明确,如果需要明确业务需要进行回溯代码,更复杂情况下需要配合数据库,根据脚本式执行找寻原有的业务逻辑,这就是贫血症引起的失忆症。

二、DDD 能带来什么

DDD 通过划分限界上下文,使用通用语言来表达限界上下文内的业务含义。通过限界上下文映射图清晰表达各上下文的职责与联系,通过创建领域对象模型,聚合业务逻辑。总结来说 DDD 可以给你带来一个有用的领域模型,清晰的业务定义,清晰的模型边界,DDD 让你的系统简化清晰而不是复杂化。

三、积分服务实践 DDD

1、限界上下文

通常来说限界上下文的划分可以通过产品逻辑转化通用语言进行提炼相关概念。

以积分服务来说

1.1、满足用户积分操作要求,兑换,扣减,增加等。
1.2、满足运营要求,可进行多个积分定义,比如各种值。
1.3、能有积分的流水记录供用户查看。
1.4、支持积分规则,比如兑换上限等。
1.5、需要满足用户安全,防止黑产用户的操作。

img

2、上下文映射图

img

3、实体

实体最重要的属性是具有唯一性,在逻辑执行中具有生命周期。在积分账户上下文中,通过创建积分账户实体 CreditAccount,通过领域对象行为进行业务逻辑处理。在实践过程中,所有的实体都去除了 setter 方法,避免了领域对象受外部调用的影响。

4、聚合根

聚合将实体和值对象在一致性边界上进行聚合。

聚合其实并不简单,在积分账户上下文中,操作积分等逻辑会涉及到积分流水,积分操作订单校验等。如果按照业务用例进行设计,CreditAccount 就是一个聚合,在 CreditAccount 中会存在很多的值对象,但这种设计会让 CreditAccount 变得臃肿,一方面,加载 CreditAccount 对象需要查询这些值对象影响性能切很多业务下是没必要尽心。同时对于某些涉及臃肿的聚合在解决事务性也是个大问题,当聚合所涉及的实体过多,如何解决事务一致性也需要进行考虑。

通过上诉描述,想表达的是聚合并不是简单的实体,值对象的聚合,在聚合过程设计中需要考虑聚合的伸缩性,事务一致性。设计用例并不是唯一,通过设计小聚合,使用唯一性数据进行关联,通过领域服务,对象工厂方法进行关联操作。

5、领域服务

领域服务不是应用服务,通常在一个行为无法在一个领域对象上进行明确表达时需要领域服务。比如多个领域对象作为输入进行显著业务处理,对领域对象进行转换。注意需要谨慎使用领域服务,会产生贫血性失忆症。在实践过程中,并没有大量使用领域服务,使用的场景主要在对于某些领域对象进行组合转换。

6、领域事件

通过领域事件进行业务耦合,可通过观察者模式,mq 队列等实现领域事件。在实践过程中,对于操作积分相关业务进行发布 mq 消息,相关业务方只需要监听积分操作 mq 消息即可。

7、资源库

作为 domain model 和数据库实体的桥梁。同时也是 OHS 到外部上下文的媒介。在实践中,资源库将领域对象转换为数据库实体进行入库操作,查询等。还有对于用户权限查询,规则引擎查询也通过资源库进行操作,通过资源库将外部上下文对象适配成内部上下文对象,承担了防腐层的作用。

8、工程结构

img

上图为 DDD 依赖倒置架构,在对工程结构分析之后,对结构进行了取舍

img

具体各层提供能力如下图

img

总结

DDD 是一种架构思想,并没有固定的模式,在实践过程中,需要更多的进行自我理解。通过 DDD 可以让你更好的理解业务逻辑,剖析项目,避免了过去面向数据库开发,即 CRUD。上诉文章内容是我在实践过程中的总结,结合我自己的理解,提出了相关的看法。对于不同的项目,不同的行业,在实践 DDD 过程中,需要根据实际情况做一些取舍。

参考文章:实现领域驱动设计 (Vaughn Vernon 著)

转载:https://zhuanlan.zhihu.com/p/165475638