《解构领域驱动设计》全书的脉络是按照领域驱动设计统一过程的脉络开展叙述的,核心内容就是构成领域驱动设计统一过程的三个阶段:
- 全局分析阶段
- 架构映射阶段
- 领域建模阶段
思维导图总结的正是这三部分内容。
01全局分析阶段
我将需求分为价值需求和业务需求,它们构成了需求分析的5W模型,即:
- Who:利益相关者
- Why:系统愿景
- Where:系统范围
- When:业务流程
- What:业务场景和业务服务
1
价值需求对业务需求产生指导和约束作用。利益相关者分为受益的利益相关者(简称受益者)和解决问题的利益相关者(简称支持者)。系统愿景是对目标系统价值需求的精炼提取,能帮助团队就项目需要达成的目标形成共识。系统范围是目标系统问题空间的边界,可以通过识别目标系统的当前状态与未来状态明确。
2
识别业务流程时,需要注意:
- 要注意区分线上流程和线下流程
- 不要受到当前业务流程的影响,要考虑流程的优化
- 注意识别可自动化的环节
- 要使用上帝视角
按照业务目标对流程进行纵向切分,获得业务场景。根据业务场景,进一步识别业务服务。
业务服务的组成要素包括:角色、服务请求和服务价值,并将整个目标系统视为一个黑盒子。角色包括用户、策略(封装了业务规则的定时器)和伴生系统。
业务服务如果需要细化,应为其编写业务服务规约,包括:
- 业务服务名称:以动词短语形式
- 描述:作为(角色)我想要(服务功能)以便于(服务价值)
- 触发事件:可用于判断是业务服务,还是执行步骤
- 基本流程(成功场景):流程的第一步一定是BC收到了服务请求后执行的第一步
- 替代流程(失败场景)
- 验收标准:包含了领域规则
问题空间可以被划分为多个子领域,可以根据价值之高低划分为:核心子领域、通用子领域和支撑子领域。
02架构映射阶段
架构映射阶段对应于领域驱动设计解空间的战略设计阶段。
1
系统上下文用于确定解空间的边界,而在架构层面,它代表了整个目标系统,可以运用系统分层架构来表现。系统分层架构分为(自下而上):
- 基础层:对应通用或支撑限界上下文
- 业务价值层:对应核心限界上下文
- 边缘层(可选):包括API网关、UI适配和服务聚合
- 客户端层
2
限界上下文是战略层次的基本架构单元,它也是领域驱动设计最重要的模式。书中通过多方面归纳限界上下文:
- 限界上下文六要素:领域知识、领域对象、知识语境、角色、活动和业务能力
- 限界上下文两本质:领域模型的知识语境和业务能力的纵向切分
- 限界上下文四特征:最小完备、自我履行、稳定空间和独立进化
限界上下文内部通过菱形对称架构来体现,菱形对称架构体现了“内外分离、南北对称”的设计思想:
- 内外分离:内部为领域层,外部为网关层
- 南北对称:分为南向网关和北向网关
作为架构的重要要素,该如何识别限界上下文?书中从业务维度、团队维度与技术维度分别加以阐述。
业务维度对限界上下文的识别顺序如下:
- 根据业务相关性(语义相关性、功能相关性)对业务服务进行归类
- 对归类的业务服务提炼其共同特征,归纳为业务主体
- 调整业务主体的边界,包括根据亲密度调整业务服务,根据限界上下文的本质调整业务服务
- 根据验证原则进一步验证限界上下文,验证原则包括:单一抽象层次原则、奥卡姆剃刀原则、正交原则和最小惊讶原则。
团队维度要求我们为限界上下文建立领域特性团队。领域特性团队的组建基于康威定律、2PTs团队与特性团队的要求。
至于技术维度,则需要根据质量属性如高并发、性能、安全等因素,要求资源隔离,从而独立定义限界上下文。
3
我将上下文映射的模式归为两类:通信协作模式和团队协作模式。
通信协作模式有四个,都是领域驱动设计本身定义的模式,菱形对称架构模式基本已经覆盖:
- 防腐层:菱形对称架构的南向网关
- 开放主机服务:菱形对称架构的北向网关
- 发布语言:菱形对称架构的消息契约
- 共享内核:去掉菱形对称架构的网关层
团队协作模式有五个,其中的发布者-订阅者模式是我个人引入的,当然,它也是业界惯用的模式了:
- 合作伙伴:反模式
- 遵奉者:反模式
- 分离关系
- 客户方-供应方
- 发布者订阅者
通过服务序列图可以确定限界上下文的上下文映射模式,并驱动出限界上下文的服务契约。
4
结合这些架构因素与模式,我总结为领域驱动架构风格。主要内容为:
- 以领域为核心驱动力
- 以业务能力为核心关注点
- 系统上下文层次:系统分层架构
- 限界上下文层次:菱形对称架构
03领域建模阶段
领域建模阶段属于领域驱动设计的战术设计阶段,我将其分为三个环节:领域分析建模、领域设计建模和领域实现建模。
1
领域分析建模的参与人包括领域专家和开发团队,应考虑由领域专家作为整个分析建模过程的主导。建模的输入为业务服务规约,输出为领域分析模型。
我采用快速建模法获得领域分析模型。它分为五个步骤:
- 名词建模:识别业务服务规约中表达领域概念的名词。
- 动词建模:识别业务服务规约中基本流程中的动词。需要确定领域行为,判断该领域行为是否产生过程数据(凭证),将该过程数据表达的领域概念放入领域分析模型。这一个概念实际上就是四色建模中的时标型对象,即确定什么事(what)、什么时间发生(when),并判断有没有留存的价值(why)。
- 归纳抽象:遵循统一语言原则,对相似的领域概念给出清晰定义,确定是否相同含义;或者是否可以进一步归纳。
- 确定关系:确定领域概念之间的关系,重点关注多对多与一对多,关系也可能是一个领域概念。
- 分配限界上下文:根据领域知识的相关性,并明确知识语境的边界,将领域分析模型的各个领域概念分配到各个限界上下文。
2
领域分析建模的输入为领域分析模型与业务服务规约的基本流程,输出则包含静态的领域设计模型(由聚合组成的类图)和动态的领域设计模型(序列图脚本或序列图)。
与领域设计模型有关的模式包括了实体、值对象、聚合、领域服务、领域事件、资源库和工厂。
实体体现了领域概念,具有ID,值对象体现了领域概念,只关注值。
聚合是边界,边界内为实体与值对象组成的对象树,根为实体,它用于维护领域概念的完整性。聚合之间只能通过聚合根实体建立关系,根实体是聚合唯一的入口和出口。
聚合作为领域建模阶段基本的设计单元,同样具有自治的特征:
- 不变量:对聚合内各个领域概念之间关系的一种约束
- 完整性:约束概念关系的一种特殊不变量
- 一致性:约束数据关系的一种特殊不变量
- 独立性:如果某个实体具有独立管理生命周期的需求,则独立为单独的聚合
领域服务主要负责:
- 管理多个聚合之间的协作
- 管理聚合与端口之间的协作
- 封装无状态的领域行为
- 分离独立变化的领域行为
领域事件封装了状态的变化。
工厂负责聚合从无到有的创建,资源库负责聚合生命周期的管理,包括添加、加载、变更、移除。
在获得在限界上下文限定下的领域分析模型后,需要确定各个领域模型对象的聚合边界。过程为:
- 梳理对象图:梳理领域分析模型,分辨实体和值对象。分辨实体和值对象的依据包括了相等性、不变性、独立性和优先级。
- 分解关系薄弱处:根据实体关系耦合强度划分聚合。
- 调整聚合边界:根据聚合的自治特性调整聚合边界。
服务驱动设计分为两个步骤:分解任务和分配职责。它的基础则是角色构造型,结合菱形对称架构,一个限界上下文的角色构造型包括:远程服务、本地服务(应用服务)、领域服务、聚合和端口。
分解的任务会形成一棵任务树,业务服务为任务树的根,组合任务为枝,原子任务为叶。过程为:
- 流程转任务:将业务服务规约中的基本流程转换为任务
- 向上归纳:将不可分割的相邻任务归纳为更高的组合任务
- 向下分解:判断目前未分解的任务是否是原子任务,如果不是,则继续分解;如果当前任务需要的领域知识是一个聚合拥有的,识别为原子任务,如果当前任务访问了外部资源,识别为原子任务。
分配职责是一个固定的流程,将各个任务分配给角色构造型:
- 业务服务分配给远程服务与应用服务
- 组合任务分配给领域服务
- 如果没有访问外部资源,原子任务分配给聚合,否则分配给端口
服务驱动设计最终输出的是动态的序列图脚本,驱动出了领域模型对象的方法,明确了领域对象之间的协作方式。
3
领域实现建模的输入是领域设计模型,以及业务服务规约的验收标准;输出为领域实现模型,包括了领域层的产品代码和测试代码。
领域实现建模推荐采用测试驱动开发。对于限界上下文而言,菱形对称架构与测试金字塔存在一定的对应关系。聚合不依赖任何外部资源,非常易于编写单元测试。领域服务的单元测试可以通过模拟端口来编写。应用服务编写集成测试。远程服务编写契约测试。
服务驱动设计与测试驱动开发可以很好的结合起来。服务驱动设计的方向是由外向内,测试驱动开发的方向是由内向外,服务驱动设计输出的序列图脚本可以作为测试驱动开发的输入。
测试驱动开发遵循Kent Beck提出的简单设计原则:
- 通过所有的测试:可测试性
- 尽可能消除重复:可重用性
- 尽可能清晰表达:可读性
- 更少代码元素:简单性
测试驱动开发还要遵循Robert Martin提出的三定律:
- 在编写不能通过的单元测试前,不可编写生产代码
- 只可编写刚好无法通过的单元测试,不能编译也算不通过
- 只可编写刚好足以通过当前失败测试的生产代码
测试驱动开发对于领域驱动设计虽非必要,但它有助于提升领域实现模型的质量。