参考资料
掘金小册-深入浅出 DDD
一文带你落地DDD
DDD汇总文集
什么是DDD
从业务出发自顶向下地去思考一个系统里面各个功能模块的职责与能力。
领域驱动设计(Domain Driven Design),它的宗旨是内聚与解耦。
通过事件风暴消除信息不对称,让业务相关人员都参与设计,确定每个业务领域的职责边界;
将常规 MVC 三层架构中自底 (数据模型) 向上的设计方式做一个反转,以业务为主导,自顶 (业务模型) 向下地进行业务领域划分;
将大的业务需求进行拆分,建立业务领域模型,分而治之。
DDD核心思想:解耦与内聚。建立领域模型形成聚合根,将原先散落在Service层的业务逻辑收拢到领域模型内部,变成充血模型,聚合即为业务。业务不是像炒大锅饭一样混在一起,而是一道道工序复杂的美食,都有它们自己独立的做法。
- 系统内部交互
任何业务都是某个业务领域模型的职责体现。为了完成某一个需求功能,将核心的业务逻辑定义在领域内部,应用服务层编排调用领域中的业务方法来实现功能点的需求。也就是说,业务功能是领域所供的能力的组合。
这样,每个领域只会做自己业务边界内的事情,最小细粒度地去定义需求的实现。原先模型层空空的贫血模型摇身一变,变成了充血模型。进到应用服务层,你的代码就是你的业务逻辑。逻辑清晰,可维护性高!
- 系统与外部交互
定义适配器包装对外部系统的依赖。系统内部直接依赖适配器,由适配器去调用外部接口,减小外部系统的变动对本系统业务逻辑的影响。
适合DDD:中大规模系统,产品化模式,业务可持续迭代,可预见的业务逻辑复杂性的系统。
战略设计
- 领域
领域本身并不是一个学术性很强的概念,任何边界明确的业务都能被称为领域。
- 子域
针对一个领域做二次划分它就是子域了。领域和子域都是相对的概念。
对于同一父级领域而言,根据子域在父级领域下的业务价值又可以将子域划分为核心域、支撑域和通用域。
核心域的划分标准是根据系统的定位而决定的。
支撑域的业务定制性,强业务相关,但又非核心。
通用域的核心诉求是稳定与高兼容性,它能够被移动至其他的领域下。
- 界限上下文
限界上下文意味着特定的、具有明确边界的语义环境,定义了领域的业务边界。
- 通用语言
通用语言表示着对领域内的一切动词、名词、形容词达到了一致的认知。
- 上下文映射图
合作关系/共享内核/客户方-供应方开发/追随者/防腐层/开放主机服务/发布语言/另谋他路/大泥球
共享内核的定位是工具基础能力,是为了提供领域完成业务所需要的能力。通用域本质上还是一个子域,它可以去使用共享内核,而共享内核不能关联通用域。
DDD中防腐层是系统内上下文与系统外上下文交互的最主要手段。
DDD六边形架构
从外往里看,领域模型(对应领域层)完全独立,可以自由地开展自己的业务。应用服务包含了领域服务进行逻辑编排,完成功能点的业务组装。并且应用服务作为业务系统的统一门面,提供各种适配的接口给外部来访问。
实体 = 唯一标识 + 生命周期(可以理解为属性可变)
值对象 = 不变性 + 通过属性判断相等(没有唯一标识)
聚合:它是领域的抽象体现,包含了当前领域内的一切事务。它在代码层面主要呈现的方式是模块的划分。
聚合根 = 领域强关联的实体、值对象 + 核心业务逻辑。
聚合根就是领域的具象体现,它是一种特殊的实体。聚合根内部定义了当前领域需要的业务属性(实体与值对象),并且包含了该领域内所有的业务逻辑定义。
应用服务与领域服务
应用服务可以看作是一个流程编排引擎,它本身不承担任何业务逻辑处理。
应用服务是整个系统的门面,也是六边形架构中的出入口,外部服务通过访问应用服务提供的接口来执行功能用例。
领域服务:A聚合根需要做一个原子化的逻辑处理,但是这个逻辑处理需要B聚合根的逻辑协作才能完成。
领域服务其实是对业务的一种妥协,理想情况下是没有领域服务的。
仓储
数据层对应的是数据模型,为了桥接数据模型与领域模型,DDD 在战术设计中提出了仓储的概念。
仓储的定位就是持久化聚合与检索聚合。让应用服务专注逻辑编排,聚合根专注逻辑处理,不用关心领域模型的持久化方式与存储介质。
事件模型
下单完成后,发布一个下单完成的领域事件,让需要感知这个事件的服务自行监听并处理,忽略不相关的领域活动。
事件风暴
一个是界定出一个系统中有多少个聚合,即划分多少个业务模型;另一个是界定出每个聚合之间的限界上下文,即划分清楚领域的业务边界。
事件风暴是一套 Workshop(类似于头脑风暴)的方法。它以事件为出发点,通过多人协作来划分业务领域与业务边界。事件风暴的分析过程就像在讲述一个个的用户故事。
角色/执行者
命令/动作
事件
事件风暴的核心流程就是:用户执行了命令,从而产生了事件。
策略/业务规则
数据/读模型
汇聚点即为某一个业务领域的聚合,一个个事件与动作的组合就是领域的业务逻辑,根据业务逻辑来设计领域所需要的属性。
根据约定的事件进行反推与逻辑归并,最后将得到业务领域聚合。自顶向下将业务模型开始转化为数据模型。让数据模型更加适配业务模型。
分层架构
依赖倒置:高层模块不应该依赖于底层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
- 传统分层
Infrastructure(基础设施层)
User Interface(用户接口层)
Application(应用服务层)
Domain(领域层)
灰度分层
能力分层
你的代码是不是跟业务流程分支走向相关的,如果是,那就是编排逻辑;如果不是,那就是业务逻辑。
领域模型需借助其他领域模型的能力来完成当前领域模型的原子化业务逻辑,为了不污染领域模型,建立领域服务来充当桥梁。
领域服务之间允许互相调用;
领域服务入参仅为基础变量(比如 String)或者聚合根。
从使用上来看,领域服务是领域逻辑,也就是说可以处理逻辑代码,而能力层本质上还是应用服务,因此我们还是需要遵循应用服务的编写规范来写。
仓储
仓储就相当于一个功能强大的仓库,你告诉它唯一标识,例如用户ID,它就能把你想要的数据组装成领域模型一口气返回给你。
数据模型, 仅仅只是一个底层的数据结构,也就是传统的 ER 模型,内部没有任何业务逻辑;
领域模型, 模型本身即是业务逻辑的体现,基于该模型的原子化业务逻辑均是内聚在模型内部的。
数据模型只存在于数据层,领域模型在领域层,而衔接了这两层的关键对象,就是仓储。
CQRS,英文全称 Command Query Responsibility Segregation,翻译过来就是命令查询职责分离。
事件驱动模型
与聚合核心逻辑有关的,走应用服务编排;与核心逻辑无关的,走事件驱动模型,采用独立事务模式。
防腐
不同限界上下文之间如果需要进行业务往来,它们并不会直接通过 RPC 等方式进行通信,而是通过一个中间方或者一个约定好的规则来进行互相通信。上下文映射图中的开放主机服务、发布语言以及防腐层就是防腐思想的体现。
防腐层又称适配层,用于转义内部上下文依赖的外部上下文。
实践
DDD中最重要的就是需求分析。业务相关的人员通过事件风暴分析建立起领域模型,依据领域模型再去套用框架。
只要涉及到业务逻辑处理或者对领域模型属性进行修改的逻辑,都不可以直接在应用服务内进行操作。需要将业务逻辑定义在领域模型内部,应用服务只是无脑地调用领域服务的逻辑,像搭积木一样拼凑出一个功能。
不允许在一个应用服务内调用两个不同仓储的save方法。
DDD在战略设计上解耦与内聚业务,让我们从业务出发能够更好地理解系统的全貌。在战术设计上切合业务逻辑进行架构落地,能够让我们的系统更加容易维护与迭代。
技术架构服务于业务。