文章目录
- 书籍信息
- 开篇
- 软件复杂度剖析
- 复杂系统
- 理解能力
- 预测能力
- 领域驱动设计概览
- 基本概念
- 控制软件复杂度
- 领域驱动设计统一过程
- 现存的不足
- 领域驱动设计统一过程
- 全局分析
- 问题空间探索
- 全局分析的 5W 模型
- 高效沟通
- 高效协作
- 商业模式画布
- 业务流程图
- 服务蓝图
- 用例图
- 事件风暴
- 学习循环
- 价值需求分析
- 识别利益相关者
- 明确系统愿景
- 确定系统范围
- 业务需求分析
- 业务流程
- 业务场景
- 子领域
- 架构映射
- 同构系统
- 系统上下文
- 限界上下文
- 定义
- 特征
- 限界上下文的识别
- 上下文映射
- 概述
- 通信集成模式
- 防腐层
- 开放主机服务
- 发布语言
- 共享内核
- 团队协作模式
- 上下文映射的设计误区
- 上下文映射的确定
- 服务契约设计
- 消息契约
- 服务契约
- 设计服务契约
- 领域驱动架构
- 菱形对称架构
- 系统分层架构
- 领域驱动架构风格
- 领域建模
- 模型驱动设计
- 领域模型驱动设计
- 领域分析建模
- 快速建模法
- 领域分析模型的精炼
- 领域分析模型与限界上下文
- 领域模型设计要素
- 领域设计模型
- 实体
- 值对象
- 聚合
- 聚合生命周期的管理
- 领域服务
- 领域事件
- 领域设计建模
- 角色构造型
- 设计聚合
- 服务驱动设计
- 领域实现建模
- 稳定的领域模型
- 测试优先的领域实现建模
- 领域建模过程
- 融合
- 领域驱动设计的战略考量
- 限界上下文与微服务
- 限界上下文之间的分布式通信
- 命令查询职责的分离
- 事务
- 领域驱动设计的战术考量
- 设计概念的统一语言
- 领域模型的持久化
- 资源库的实现
- 领域驱动设计体系
- 领域驱动设计的精髓
- 领域驱动设计能力评估模型
- 领域驱动设计参考过程模型
- EAS案例背景
- EAS的全局分析
- EAS的架构映射
- EAS的领域建模
- EAS的融合设计
- 附录
- 领域建模范式
- 结构建模范式
- 对象建模范式
- 函数建模范式
- 事件建模范式
- 领域驱动设计魔方
- 领域驱动设计统一过程交付物
- 全局分析规格说明书
- 架构映射战略设计方案
书籍信息
书名:《解构领域驱动设计》
作者:张逸
开篇
软件复杂度剖析
复杂系统
复杂系统:由大量互相作用的部分组成的系统。这些组成部分相对简单,没有中央控制,组成部分之间也没有全局性的通信,并且组成部分的相互作用导致了复杂行为。
理解能力
影响阻碍理解能力的要素:
- 规模
- 结构
预测能力
影响阻碍预测能力的要素:
- 过度设计
- 设计不足
领域驱动设计概览
基本概念
软件系统的构建实则是对问题空间的求解,以获得构成解空间的设计方案。
子领域、限界上下文、分层架构和聚合皆为领域驱动设计的核心元模型,分属战略设计和战术设计,贯穿了从问题空间到解空间的全过程。
控制软件复杂度
领域驱动设计(DDD)需要应对软件复杂度的挑战:
- 规模:通过分而治之控制规模;对领域驱动设计而言:以子领域、限界上下文对问题空间与解空间分而治之;
- 结构:通过边界保证清晰有序;对领域驱动设计而言:以分层架构隔离业务复杂度与技术复杂度,形成清晰的架构;
- 变化:顺应变化方法;对领域驱动设计而言:通过领域建模抽象为以聚合为核心的领域模型,响应需求之变化。
领域驱动设计统一过程
现存的不足
- 领域驱动设计缺乏规范的统一过程;
- 领域驱动设计缺乏与之匹配的需求分析方法;
- 领域驱动设计缺乏规范化的、具有指导意义的架构体系;
- 领域驱动设计的领域建模方法缺乏固化的指导方法。
领域驱动设计统一过程
领域驱动设计的开放性是其生命长青的基石,但是它过于灵活的特点也带来了诸多不足。
领域驱动设计统一过程,正是要在开放的方法体系指导之下,提供一种简单有效的最佳实践。
领域驱动设计统一过程是对领域驱动设计进行解构的核心内容!
整个统一过程分为3个连续的阶段:
- 全局分析阶段;
- 架构映射阶段;
- 领域建模阶段。
全局分析
问题空间探索
全局分析的 5W 模型
- Who:利益相关者
- Why:系统愿景
- Where:系统范围
- When:业务流程
- What:业务服务
价值需求和业务需求,它们共同组成了目标系统的问题空间。
价值需求需要从系统价值的角度进行分析获得。5W 模型中前3个共同组成了价值需求。
业务需求由动态的业务流程与静态的业务场景、业务服务构成。
业务服务是全局分析阶段获得的基本业务单元。
高效沟通
形成统一的领域术语,尤其是基于模型的语言概念,是让沟通达成一致的前提。
高效协作
商业模式画布
商业模式画布由9个板块儿构成:
业务流程图
业务流程图(TFD)善于表现业务流程,它通过使用诸如任务流程图、泳道图等图形形象地描述真实世界中各种业务流程的执行步骤与处理过程。
服务蓝图
服务蓝图是用于服务设计的主要工具,它以更加全面的视角展现了各种角色之间的协作关系。从上到下有3条分界线:
用例图
用例是对一系列活动的描述,通过用例图对主体行为进行可视化建模。
事件风暴
事件风暴是以一种工作坊形式对复杂业务领域进行探索的高效协作方法。
- 以事件为核心驱动力对业务开展探索;
- 强调可视化的互动,更好地调动所有参与者共同对业务展开探索。
学习循环
学习循环:开始于对意图和任务焦点的想象,接着是探索与投入,然后是思考和发现模式,最后是决定行动与应用。
该方法将个人的学习过程转变为群体共同工作的学习过程。
价值需求分析
识别利益相关者
利益相关者分为支持者和受益者。
在价值需求分析阶段,支持者更加重要,他们决定目标系统的愿景与范围,确定开发目标系统的约束条件;在业务需求分析阶段,受益者更加重要,他们决定了目标系统的业务流程与业务场景,前提是这些业务需求必须获得支持者的认可。
明确系统愿景
系统愿景是对目标系统价值需求的精炼提取,若能以精简的话语清晰描述出来,就能帮助团队就项目需要达成的目标达成共识。
确定系统范围
系统范围保证了问题空间的开放性,同时又能确保问题空间内业务需求的收敛性。
业务需求分析
业务流程
系统内部以及系统之间通过一系列的协作来为用户提供业务价值,这一协作的过程可以成为业务流程。
业务流程有2个关键点:完整和边界。
业务流程分为:主业务流,变体业务流,支撑业务流。
业务场景
场景就是角色之间为了实现共同的业务目标进行互动的时空背景。业务场景就是体现业务目标的场景。
业务场景 5W 模型:角色、时间、空间、活动、业务目标。
业务服务是角色主动向目标系统发起服务请求完成的一次完整的功能交互,体现了服务价值的业务行为。
子领域
问题空间太大,业务服务又太小,而子领域就是一个不大不小、粒度合理的业务单元。
架构映射
同构系统
同构系统:两个复杂结构可以相互映射,并且它们的组成部分可以形成一一对应的映射关系。
整个架构映射阶段由以下3组同构系统构成:
- 概念层次:架构定义的概念系统与架构设计的模式系统;
- 设计层次:问题空间的真实系统与解空间的软件系统;
- 管理层次:设计方案的架构系统与团队组织的管理系统。
系统上下文
系统上下文对应解空间的范围,它站在组织层面思考利益相关者、目标系统和伴生系统之间的关系。
系统上下文图展现的是利益相关者、目标系统和伴生系统的关系的静态视图。
业务时序图则展现了目标系统与伴生系统之间的动态协作关系。
限界上下文
定义
限界上下文其实是动态的业务流程被边界静态切分的产物。
特征
限界上下文是领域驱动设计战略层面最重要、最基本的架构设计单元。
故限界上下文应该是自治的架构单元:
- 最小完备
- 自我履行
- 稳定空间
- 独立进化
限界上下文的识别
识别限界上下文的过程,就是将问题空间的业务需求映射到解空间限界上下文的过程。
V型映射过程:
具体分为以下阶段:
- 业务知识归类
- 业务知识归纳
- 业务主体的边界梳理
- 呈现界限上下文
对限界上下文的边界的合理性的验证方法:
- 正交原则:如果两个或更多事物中的一个发生变化,不会影响其他事物,这些事物就是正交的。
- 单一抽象层次原则:保证一个方法中的所有操作都在同一个抽象层次。
- 奥卡姆剃刀原理:切勿浪费较多东西去做较少的东西同样可以做好的事情。
每个团队会负责一到多个完整的界限上下文,多个界限上下文之间应该是允许并行开发的。
上下文映射
概述
上下文映射建立了限界上下文之间的关系。
上下文映射的目的是让软件模型、团队组织和通信集成之间的协作关系能够清晰呈现,为整个系统的各个团队提供一个清晰的视图。
通信集成模式
与通信集成有关的上下文映射模式有:
防腐层
计算机科学中的大多数问题都可以通过增加一层间接性来解决。防腐层的引入正是“间接”设计思想的一种体现。
防腐层从未提供真正的业务实现,而仅仅是扮演了“隔离”的作用;业务实现被放到了另一个限界上下文中,防腐层会向它发起调用。
当多个限界上下文都需要防腐层时,为了避免代码重复,可以将防腐层升级为一个独立的限界上下文。
开放主机服务
开放主机服务,就是定义公开服务的协议,包括通信的方式、传递消息的格式(协议)。
发布语言
发布语言是一种公共语言,用于两个限界上下文之间的模型转换。
共享内核
我们将一个限界上下文标记为共享内核时,就意味着它实际上暴露了自己的领域模型,并削弱了限界上下文边界的控制力。
共享内核的成本是耦合导致的面临变化的风险,但是收益确是使用非常便利。我们应该只将那些稳定且具有复用价值的领域模型对象封装到共享内核上下文中。
团队协作模式
- 合作者:2个团队的限界上下文一起成功或一起失败。存在强耦合关系,是一种“不当”设计引起的“适当”的团队协作模式。
- 客户方/供应方:一个团队的限界上下文为另一个团队的限界上下文提供服务。
- 发布者/订阅者:脱胎于设计模式中的观察着模式。本身是一种通信集成模式,事件订阅者可以视为开放主机服务,事件发布者则是防腐层的一部分。使用该模式的2个团队的协作是围绕着“事件”进行的。
- 分离方式:2个团队的限界上下文之间没有一点关系,这种“无关系”就是一种最好的关系。
- 遵奉者:当上游团队不积极响应下游团队的需求时,下游团队可以选择“遵奉”于上游团队设计的模型,即遵奉者模式。这实际上透漏出了一种组织管理的风险。
上下文映射的设计误区
- 语义关系形成的误区:不要使用语义之间的关系去揣测限界上下文之间的关系。
- 对象模型形成的误区:对象模型关系也并非和限界上下文关系完全一致。
上下文映射的确定
- 任务分解的影响:最小知识法则
- 呈现上下文映射:上下文映射图
- 菱形表示限界上下文采用菱形对称架构(结合了防腐层、开放主机、发布语言模式);椭圆形表示采用共享内核模式
- 客户方/供应方:两端为C/S
- 发布者/订阅者:两端为P/S,且是虚线标注
- 遵奉者:连线上标注文字
- 合作者:可省略标注
服务契约设计
消息契约
消息契约对应上下文映射的发布语言模式,根据客户端发起对服务操作的类型,分为命令(动作)、查询(请求)和事件(通知)。
服务契约
服务契约对应上下文映射的开放主机服务模式,通常指采用分布式通信的远程服务。
不包含领域逻辑的业务服务应被定义为应用服务。
设计服务契约
服务时序图的本质是 UML 的时序图,通过消息流产生一种不断向前的设计驱动力。
通过服务契约表列出每个服务契约的定义和设计信息。一般包括服务功能、服务功能描述、服务方法、生产者、消费者、模式、业务服务、服务操作类型等属性。
领域驱动架构
菱形对称架构
六边形架构:
- 入口适配器:响应边界外客户端的请求,需要实现进程间通信以及消息的序列化、反序列化。
- 入口端口:负责协调外部客户端请求与内部应用程序之间的交互。
- 应用程序:承担了整个限界上下文的领域逻辑,包含了当前限界上下文的领域模型。
- 出口端口:作为一个抽象的接口,封装了对外部设备和数据库的访问。
- 出口适配层:访问外部设备和数据库的真正实现。
开放主机服务模式的设计目标与菱形对称架构的北向网关完全一致。
如果将防腐层防止腐化的目标从上游限界上下文扩大至当前限界上下文的所有外部环境(包括数据库等环境资源和伴生系统),则防腐层就承担了菱形对称架构的南向网关的角色。
保证限界上下文自治的一个关键在于隔离领域模型。除了共享内核模式和尊奉者模式之外,限界上下文不应暴露领域模型,因此需要为北向网关的服务、南向网关的客户端建立消息契约模型。
菱形对称结构:
系统分层架构
系统上下文界定了目标系统解空间的范围,对系统上下文进行分层架构来确定整个系统的结构:
- 关注点分离:分解复杂系统;
- 映射子领域:从价值角度将所有的限界上下文分为2个层次;
- 业务价值层:映射核心子领域;
- 基础层:映射通用子领域和支撑子领域;
- 边缘层:后端提供一个供前端访问的边缘,例如:BFF(UI适配、服务聚合)、API网关等;
领域驱动架构风格
领域驱动设计在架构层面获得的抽象元素就是:系统上下文和限界上下文,他们围绕领域为核心驱动力,以业务能力为核心关注点,分别形成了两个层次的架构模式:系统分层架构模式与菱形对称架构模式。
领域建模
模型驱动设计
领域模型驱动设计
领域模型是以“领域”为关注核心的模型,是对领域知识严格的组织且有选择的抽象。具体分为领域分析模型(概念模型)、领域设计模型(设计)、领域实现模型(代码)。
分析、设计和实现是领域模型驱动设计的3个建模活动。在这个过程中,我们通过统一语言维护领域模型的一致性。
领域分析建模
快速建模法
在分析之初,不考虑任何技术实现手段,一切围绕着领域知识进行建模,是领域模型驱动设计的关键。
快速建模法,分为4个步骤:
- 名词建模:识别业务服务规约中的名词,判断是否属于领域概念。
- 动词建模:识别业务服务规约中的动词,判断动词对应的领域行为是否产生了过程数据,如果有,则将该过程数据识别为领域概念。
- 归纳抽象:针对有定语修饰的已有的领域概念进行归纳和抽象,分辨是类型的差异还是值的差异,如果是值的差异,则类型相同,应该合并为一个领域概念。
- 确定关系:确定得到的领域概念之间的关系;如果一种关系用领域概念来描述更为合理,则将该关系建模为一个领域概念。
领域驱动设计鼓励在领域分析建模阶段形成细粒度的领域概念。在分不清一个领域概念是应该保留还是被合并时,应优先考虑保留,待到领域设计建模时再做进一步甄别。
领域分析模型的精炼
对相同或近似的领域进行建模分析时,可以抽象出相同或相似的模型,形成可以参考和复用的概念模型,即分析模式。分析模式的抽象层次要高于分析模型。
分析模式是由领域专家寻找和总结的,是企业的一份重要资产,可以用于改进领域分析模型。
实际上,在领域分析建模活动中,扮演重要作用的不是开发团队,而是领域专家。
领域分析模型与限界上下文
限界上下文是领域模型的的业务边界,即领域模型的知识语境。
- 一个限界上下文包含了多个相互之间存在关系的领域分析模型;
- 尽量避免位于不同限界上下文的领域分析模型之间存在关系,因为这种关系意味着耦合。
领域模型设计要素
领域设计模型
战术设计元模型的各种模式与模型元素特点:
- 领域模型对象如何实现数据的持久化?资源库模式隔离了领域逻辑与数据库实现,并将领域模型对象当作生命周期管理的资源,将持久化领域对象的介质抽象为资源库。
- 领域模型对象的加载以及对象间的关系该如何处理?领域驱动设计引入聚合划分领域模型对象的边界,并在边界内管理所有领域模型对象之间的关系,使其在对象的协作与完整性之间取得平衡。
- 领域模型对象在身份上是否存在明确的差别?领域驱动设计使用实体与值对象区分领域模型对象的身份,避免了不必要的身份跟踪与额外的并发控制要求。
- 领域模型对象彼此之间如何能弱依赖地完成状态的变更通知?领域驱动设计引入了领域事件,通过发布和订阅领域事件解除聚合与聚合之间的依赖,体现状态变迁的特性。
实体
- 实体是我们要描述的主体,而这个主体的状态在相当长一段时间内会持续地变化。
- 实体需要一个唯一的身份标识。
- 实体必须包括属性与行为。
- 实体的属性用来说明主体的静态特征,又分为原子属性和组合属性。一个属性是否应该划分为组合属性,判断标准是:该属性是否存在约束规则、组合因子或属于自己的领域行为。
- 实体的领域行为用来说明主体的动态特征,又分为:变更状态的领域行为(改变实体的属性值)、自给自足的领域行为(仅读取本实体的属性值的方法)、互为协作的领域行为(方法的入参等为其他实体)。
值对象
值对象通常作为实体的属性,值对象应该是不可变的,也不会被分配任何标识。
使用值对象还是实体?
- 业务的参与者对它的相等判断是依据值(值对象),还是依据身份标识(实体)。
- 对象的属性值是否会变化?如果变化了,是产生一个完全不同的对象(值对象),还是维持相同的身份标识(实体)。
- 是否需要生命周期管理。值对象不需要,实体则需要。
设计建议:
- 尽量将值对象设计为不变类。
- 值对象可以拥有自给自足的领域行为,比如:自我验证、自我组合、自我运算。
值对象比内建类型的优势有:
- 内建类型无法展现领域概念,而值对象就可以,值对象可以更加直观地体现业务含义。
- 内建类型无法封装显而易见的领域逻辑,而值对象就可以。
- 内建类型缺乏验证能力,而值对象就可以。
聚合
类的关系:
- 泛化:子类继承父类。
- 关联:一种连接关系,组合关系是一种特殊的关联关系,关联双方为整体与部分。组合关系又分为合成关系、聚合关系。
- 合成关系:体现了强烈的“所有权”特征,在生命周期上存在啮合关系;
- 聚合关系:没有“所有权”特征,也不约束生命周期。
- 依赖:一个类使用了另一个类的信息或服务,故依赖关系存在方向。
控制类的关系要从以下几点下手:
- 去除不必要的关系;
- 降低耦合的强度;
- 避免双向耦合。
同时,还需要引入边界来降低和限制领域类之间的关系,防止关系之间的传递无限蔓延。
领域设计模型并非真实世界的直接映射。如果真实世界缺乏清晰的边界,在设计时,我们就应该给它清晰地划定边界。
聚合的定义:
将实体和值对象划分为聚合并围绕着聚合定义边界。选择一个实体作为每个聚合的根,并允许外部对象仅能持有聚合根的引用。作为一个整体来定义聚合的属性和不变量,并将执行职责赋予聚合根或指定的框架机制。
聚合的基本特征:
- 聚合是包含了实体和值对象的一个边界,而不是对象。
- 聚合内包含的实体和值对象形成一棵树,只有实体才能作为这棵树的根。这个根称为聚合根,这个实体称为根实体。
- 一个最小的聚合就只有一个实体。
- 外部对象只允许持有聚合根的引用,以起到边界的控制作用。
- 聚合作为一个完整的领域概念整体,其内部会维护这个领域概念的完整性,体现业务上的不变量约束。
- 由聚合根统一对外提供履行该领域概念职责的行为方法,实现内部各个对象之间的行为协作。
- 在领域设计模型,聚合才是最小的设计单元。
聚合的设计原则:
- 最高原则是:只有聚合根才是访问聚合边界的唯一入口。
- 独立性:优先保证独立性,再考虑完整性。聚合应设计为一个稳定的不依赖于任何外部环境的设计单元,不要在聚合内部引入对南向网关端口的依赖。
- 完整性。
- 不变量:在数据变化时必须保持的一致性规则,涉及聚合成员之间的内部关系。对聚合而言,就是一种业务约束。
- 一致性:一致性本质是一种特殊的不变量,即一致性约束。
聚合的协作办法:
- 关联关系:通过身份标识建立关联。发出导航的聚合称为主聚合,将导航指向的聚合称为从聚合,则从聚合通过主聚合根实体的ID建立与主聚合的隐含关联,而主聚合完全不知从聚合。
- 依赖关系:依赖关系的聚合只要位于同一个限界上下文中,就应该允许一个聚合的根实体直接引用另一个聚合的根实体。
聚合生命周期的管理
创建时使用工厂方法涉及的设计模式:
- 工厂方法
- 抽象工厂
- 构建者模式
创建时使用工厂方法的方式:
- 由被依赖聚合担任工厂;
- 引入专门的聚合工厂;
- 聚合自身担任工厂;
- 消息契约模型或装配器担任工厂;
- 使用构建者组装聚合。
资源库是对数据访问的一种业务抽象,可以代表数据库、网络或其他硬件环境。引入资源库,主要目的是管理聚合的生命周期:工厂负责聚合实例的诞生,垃圾回收负责聚合实例的消亡,资源库就负责聚合记录的查询与状态变更。
资源库与数据访问对象(DAO)的在技术层面一致,但区别在于:
- DAO:访问数据时,不受聚合的边界约束,可以操作任何层次和粒度的数据;
- 资源库:一个聚合对应一个资源库。获取过程,也是先通过资源库获取整个聚合到内存,然后,通过根实体来在内存中访问封装在聚合边界内的非根实体对象。
领域服务
领域服务也是一种领域模型。但是它并不代表一个具体的领域概念,而是封装了领域行为。
针对领域行为建模时,优先考虑通过聚合的方式实现,领域服务是领域设计建模的最后选择。
适合使用领域服务的场景:
- 与状态无关的领域行为;
- 变化方向与聚合不一致的领域行为;
- 聚合之间协作的领域行为;
- 聚合和端口(如南向网关的端口)之间协作的领域行为。
领域事件
领域驱动设计将对象的状态提升到更高地位,赋予它领域事件的身份。它是实体、值对象和领域服务的一个重要补充,目的是更好地跟踪实体状态的变更,并在状态发生变更时,通过事件消息的通知完成领域模型对象之间的协作。
领域事件的特征:
- 领域事件表达了实体的状态变更,属于领域概念;
- 领域事件是已经发生的事实;
- 领域事件是不可变的领域对象;
- 领域事件会基于某个条件而触发。
领域设计建模
角色构造型
各个角色都具有自身的特征,这些特征就是构造型。角色构造型不包括具体行为的描述。
角色构造型分类:(对应关系参考菱形对称架构)
- 信息持有者:提供信息;对应:领域模型对象。
- 服务提供者:执行工作;对应:领域服务。
- 构造者:维护对象之间的关系;对应:工厂。
- 协调者:通过向其他对象委托任务来响应事件;对应:本地服务层的应用服务。
- 控制器:进行决策并指导其他对象的行为;对应:领域服务。
- 接口:连接系统的各个部分,并在它们之间进行信息和请求的转换;对应:北向网关的远程服务、南向网关的端口、消息契约对象。
设计聚合
设计聚合的过程:
- 理顺对象图:弄清楚对象图的结构,辨别类为实体还是值对象。
- 分解关系薄弱处:理清实体之间的关系,保证类关系的单一导航方向,以关系强弱为界,以聚合边界为刀,逐一分解。
- 调整聚合边界:针对聚合边界模糊的地方,运用聚合设计原则做进一步推导。
分解关系薄弱处:
- 若继承体系为一个整体的概念,且子类没有各自特殊的属性,考虑将整个继承体系放入同一个聚合。否则,为各个子类定义单独的聚合。
- 优先考虑将具有合成关系(物理包容)的实体放在同一个聚合,除此之外,存在其他关系的实体都应考虑放在不同的聚合。
调整聚合边界:
- 聚合设计原则的优先次序依次为:完整性、独立性、不变量和一致性。
- 合理的聚合边界设计要优于盲目地追求聚合的细粒度。
- 当聚合边界存在模糊之处时,小聚合显然要优于大聚合,概念独立性就成为非常有价值的参考。
服务驱动设计
业务服务是全局分析阶段对业务场景进行分解获得的独立的业务行为。
业务服务分为3个层次:最底层的原子任务对应具体的功能实现;其上层的组合任务对应业务功能;最上层的业务任务对应服务价值,即业务服务需要满足的服务请求。
服务驱动设计过程:
- 分解任务:按照层次进行分解,直至分解到最底层的原子任务。一个任务若能被一个聚合对象单独完成,即可认为是一个原子任务。
- 分配职责:远程服务、应用服务匹配业务服务;领域服务匹配组合任务;聚合、接口匹配原子任务。
领域实现建模
稳定的领域模型
领域层的设计目标就是要达到逻辑层的自给自足,一个稳定的领域模型也是最容易执行单元测试的模型。
单元测试保护下的领域核心逻辑,是企业系统的核心资产。确保了正确性、可安全重构、可持续演化。
菱形对称架构和测试金字塔的对应关系:
- 远程服务:契约测试;
- 应用服务:集成测试;
- 领域服务:单元测试(模拟端口);
- 聚合:单元测试;
如果测试编写得体,测试代码也可以认为是一份精炼文档,且这样的文档还具有和实现与时俱进的演进能力,形成一种活文档。
测试优先的领域实现建模
在领域实现建模阶段,保证从设计到实现一脉相承的简单性,最好的方式就是测试驱动开发。
测试驱动开发实际上是要求需求分析优先,任务分解优先。任务分解可以进一步划分为多个可以验证的测试用例,然后按照“测试-开发-重构”的节奏进行编码实现。
测试驱动开发三定律:
- 一次只写一个刚好失败的测试,作为新加功能的描述;
- 不写任何产品代码,除非它刚好能让失败的测试通过;
- 只在测试全部通过的前提下做代码重构,或开始新加功能;
功能正确、减少重复、代码可读性是简单设计的根本要求。
领域建模过程
- 聚合设计:理顺对象图、分解关系薄弱处、调整聚合边界。
- 服务驱动设计
- 实现建模:编写测试、快速实现、代码重构(简单设计原则)。
融合
领域驱动设计的战略考量
限界上下文与微服务
一般而言,微服务的粒度要细于或等于限界上下文的粒度:
- 限界上下文、微服务都不能跨团队边界。
- 微服务不能跨不同进程,但是限界上下文可以。
限界上下文之间的分布式通信
主要采用的分布式通信机制包括:REST、RPC、消息传递。
命令查询职责的分离
CQRS模式是将模型分为命令模型和查询模型。同时,根据命令操作的特性以及质量属性的要求,酌情考虑引入命令总线、事件总线和事件存储。
命令总线是将同步的命令请求改为异步方式,以提高系统的响应能力。一般采用消息队列来实现。
命令端与查询端可以进一步引入事件总线实现两端的完全独立。事件自身携带了事件处理器需要的聚合数据,交由资源库完成对聚合的持久化。
事务
事务是用来确保一致性的技术手段。满足数据强一致性(即ACID)要求的分布式事务称为刚性事务,则满足数据最终一致性的分布式事务称为柔性事务。
业界常用的柔性事务模式包括:
- 可靠事件模式:结合了本地事务与可靠消息传递的特性。(消息可能重复传递,需要保证处理逻辑的幂等性)
- TCC模式:业务补偿模式,上游服务根据下游服务执行的结果判断是否需要进行补偿操作(失败情况时上游服务进行反向等操作来消除错误影响)。
- Sega模式:将长事务拆分为多个短事务并进行事务之间的协调。协调也是一种补偿操作,但是与TCC不同,不需要预先预留或冻结资源。其核心是事务消息与事件的发布者-订阅者模式。
领域驱动设计的战术考量
设计概念的统一语言
- 领域模型对象包含实体、值对象、领域服务和领域事件,有时候也可以单指组成聚合的实体与值对象。
- 领域模型必须是富领域模型。
- 远程服务与应用服务接口的输入参数和返回值为遵循DTO模式的消息契约模型,若客户端为前端UI,则消息契约模型又称为视图模型。
- 领域模型对象中的实体与值对象同时作为持久化对象。
- 只有资源库对象,没有数据访问对象。资源库对象以聚合为单位进行领域模型对象的持久化,事件存储对象则负责完成领域事件的持久化。
领域模型的持久化
要持久化领域模型对象到关系数据库中,就要为对象与关系建立映射,即ORM。
聚合是最小的设计单元。一个资源库对应一个聚合,故而聚合也是领域模型最小的持久化单元。
资源库的实现
资源库的实现取决于开发人员对ORM框架的选择,如Hibernate或MyBatis等。每种框架都有各自的设计思想和原则,也有不同的最佳实践指导。
领域驱动设计体系
领域驱动设计的精髓
边界是核心:
- 分析边界(问题空间)
- 第一重边界:目标系统范围;
- 第二重边界:子领域;(核心子领域、通用子领域、支撑子领域)
- 设计边界(解空间)
- 第一重边界:系统上下文;
- 第二重边界:限界上下文;
- 第三重边界:逻辑分层;(菱形对称架构)
- 第四重边界:聚合;
纪律是关键:
- 三大纪律:
- 领域专家与开发团队在一起工作;
- 领域模型必须遵循统一语言;
- 时刻坚守两重分析边界与四重设计边界。
- 八项注意:
- 问题空间和解空间不要混为一谈;
- 一个限界上下文不能由多个特性团队开发;
- 跨进程协作通过远程服务,进程内协作通过应用服务;
- 保证领域分析模型、领域设计模型与领域实现模型的一致;
- 不要将领域模型暴露在网关层之外,共享内核除外;
- 先有领域模型,后有数据模型,保证二者的一致;
- 聚合的关联关系只能通过聚合根ID引用;
- 聚合不能依赖访问外部资源的南向网关。
领域驱动设计能力评估模型
领域驱动设计参考过程模型
EAS案例背景
EAS(企业应用套件)是一款根据软件集团应用信息化的要求开发的企业级一个要办公软件。EAS为企业搭建了一个数据共享与业务协同平台,实现了人力资源、客户资源和项目资源的整合,包括了人力资源管理、客户关系管理、项目过程管理等主要模块。
EAS的全局分析
- 价值需求分析
商业模式画布:
价值需求:
- 业务需求分析
核心业务流程:
泳道图:
服务蓝图:
业务服务图:
流程图:
业务需求:
- 划分子领域
子领域映射图:
EAS的架构映射
- 映射系统上下文
- 映射限界上下文
业务主体视图:
领域模型:
限界上下文:
- 上下文映射
服务序列图:
上下文映射图:
- 服务契约设计
服务契约:
- 映射系统分层结构
系统架构图:
代码层次结构:
EAS的领域建模
- 领域分析建模
业务服务规约:
- 领域设计建模
识别聚合:
1)领域分析模型:
2)识别出值对象:
3)识别出实体:
4)确定实体之间的关系:
5)根据关系强弱确定聚合边界:
6)调整边界后的领域设计模型:
- 领域实现建模
领域层的代码层次结构:
EAS的融合设计
资源库的实现、应用服务的实现、远程服务的实现。
整体的代码层次结构:
附录
领域建模范式
即使采用领域模型驱动设计,不同人针对同一个领域设计的领域模型也会千差万别,其中一个重要原因是:对模型产生根本影响的是建模范式。范式是指一种公认的模型或模式。
结构建模范式
面向过程设计,即结构化编程。特点是:数据结构和算法分离,算法用来操作数据结构。
结构建模范式由持久化对象(数据表的映射)和服务对象(实现业务服务)组成。是典型的贫血模型。
对象建模范式
领域驱动设计通常采用面向对象的编程范式。核心是职责与抽象。
职责,是从角色拥有何种能力的角度做出的思考。角色则由对象承担。关键点:单一职责原则。
抽象,关键点:依赖注入,封装变化
函数建模范式
函数范式的主要特征为:模块化、抽象化和可组合。
事件建模范式
若领域驱动设计的整个过程围绕着“事件”进行,就会因此改变我们观察真实世界的方式,这种方式被作者称为事件建模范式。
领域驱动设计魔方
将整个目标系统视为一个正方体,X维度限定领域驱动设计的内容,Y维度分解领域驱动设计的过程,Z维度涵盖领域驱动设计的实践。
领域驱动设计统一过程交付物
全局分析规格说明书
1. 价值需求
(可附上商业模式画布)
1.1 利益相关者
(终端用户、企业组织、投资人等)
1.2 系统愿景
(对准企业的战略目标)
1.3 系统范围
(问题空间的范围和边界)
1. 当前状态
(已有资源、已有系统、当前业务执行流程)
2. 未来状态
(希望如何)
3. 业务目标
(按利益相关者分类)
2. 业务需求
2.1 概述
(可附上子领域映射图)
2.2 业务流程
(可附上服务蓝图、泳道图或活动图)
2.3 子领域1...n
2.3.1 业务场景1...n
1. 业务服务1...n
(子领域-业务场景-业务服务,3级结构)
(1)编号
(唯一编号)
(2)名称
(动词短语描述业务服务名称)
(3)描述
(作为<角色>,我想要<服务功能>,以便于<服务价值>)
(4)触发事件
(点击UI控件、伴生系统发送的消息等)
(5)基本流程
(主流程,执行成功的场景)
(6)替代流程
(扩展流程,失败的场景)
(7)验收标准
(以点形式罗列可以接受的条件或业务规则)
架构映射战略设计方案
1. 系统上下文
1.1 概述
(系统上下文图)
1.2 系统协作
1. 业务流程1...n
(业务序列图,彼此之间的协作关系)
2. 业务架构
2.1 业务组件
(限界上下文作为业务组件,业务服务图、服务图)
2.2 业务架构视图
3. 应用架构
3.1 应用组件
(业务组件映射为应用组件)
3.2 应用架构视图
(系统架构图)
4. 子领域架构
4.1 核心子领域1...n
4.1.1 概述
(上下文映射图)
4.1.2 应用组件1...n
(组件名、组件描述、组件类型,服务契约定义:服务功能、服务功能描述、服务方法、生产者、消费者、模式、业务服务、服务操作类型)
1. 服务契约1...n
(服务契约定义,服务质量相关要素:幂等性、安全性、同步/异步,其他设计要素:性能、兼容性、环境等)
4.2 支撑子领域
(同核心子领域)
4.3 通用子领域
(同核心子领域)