七八年前大家就开始流行聊DDD了,但那时很少有人能说得明白。现在DDD逐渐变成了能听得懂、说得清、看得见、摸得着的方法。今天咱们就来聊一聊。

对复杂的理解

DDD领域驱动设计是软件复杂性应对之道。这个复杂性怎么理解呢?

看得见摸得着的DDD领域驱动设计_领域驱动设计

Jurgen Appelo从理解力与预测能力两个维度分析了复杂系统理论。理解力的复杂度体现在规模和结构上。预测能力的复杂度体现在变化上。从对应的手段上,规模复杂度可以通过分解分而治之;结构上的复杂度可以通过领域编程模型等结构来清晰;对于变化可以通过抽象来提高扩展性。其实扩展性问题总体而言无非两点:预测变化和封装变化。规模复杂度分解上领域驱动设计有4重边界。

领域驱动设计的四重边界

看得见摸得着的DDD领域驱动设计_基础设施_02

根据上图所示,我们通过四重来进行架构设计:

分而治之:DDD通过规划四重边界,把领域知识做了合理的固化和分层。业务有核心领域和支撑域、业务域中又拆分成多个限界上下文,一个限界上下文中又根据领域知识核心与否进行分层,领域层中按照多个业务(子域)的强相关性进行聚合成一个子域。

【第一重边界】确定项目的愿景与目标,确定问题空间,确定核心子领域、通用子领域(多个子领域可以复用)、支撑子领域(额外功能,如数据统计、导出报表)

【第二重边界】解决方案空间里的限界上下文就是一道进程隔离层面的物理边界

【第三重边界】每个限界上下文内,使用分层架构划分为:接口层、领域层、应用层、基础设施层之间的最小隔离

【第四重边界】领域层里为了保证各个领域的完整性和一致性,引入聚合的设计作为隔离领域模型的最小单元

领域编程模型

对结构复杂度的模型,近来比较推崇的是菱形对称架构。

菱形对称架构(Rhombic Symmetric Architecture) 主要针对领域层次的架构,借鉴了六边形架构、分层架构、整洁架构的知识,并结合了领域驱动设计的元模型,使其能够更好地运用到限界上下文的架构设计中。

看得见摸得着的DDD领域驱动设计_数据库_03

菱形对称架构去掉了应用层和基础设施层的概念,以统一的网关层进行概括,并以北向与南向分别体现了来自不同方向的请求。如此形成的对称结构突出了领域模型的核心作用,更加清晰地体现了业务逻辑、技术功能与外部环境之间的边界。

资源库视为防腐层的端口与适配器,作为领域建模时的角色构造型,与场景驱动设计更好地结合,增强了领域模型的稳定性。应用层被去掉之后,被弱化为开放主机服务层的本地服务,相当于从设计层面回归到服务外观的本质,也有助于解决应用服务与领域服务之间的概念之争。

代码模型示例:

看得见摸得着的DDD领域驱动设计_数据库_04

ohs 为开放主机服务模式的缩写,acl 是防腐层模式的缩写,pl 代表了发布语言;也可以使用北向(northbound)与南向(sourthbound)取代 ohs 与 acl 作为包名,使用消息(messages)契约取代 pl 的包名。

实际使用中遇到的问题

在实际DDD实践时经常会遇到和spring依赖的冲突。比如在构建DDD充血模型时封装的行为可能会依赖spring注入。如果使用了spring注入,这个充血模型就不是简单的实体对象。这时候一般的解决方法是使用XXHolder来做一层封装。

在使用DDD分层架构来作为工程架构时,如果分层松散,会导致管理混乱;如果严格分层,对于一些额外的层次就不好处理。比如定时任务,严格来做定时任务受外界调度,属于用户接口层,那要专门为定时任务做一个严格的4层模型必要性又不是很大。这时候可以使用依赖倒置进行接口。基础设施层可以贯穿各层注入数据来做松耦合。

看得见摸得着的DDD领域驱动设计_领域驱动设计_05