纲要
- 核心思想
- 模型设计
- 应用落地
核心思想
- DDD,Domain Driven Design:一套软件架构设计方法论,基于领域模型驱动软件的设计和开发,最初由Eric Evans提出。
- DDD应用对象:任何领域的应用软件开发,特别是具备复杂业务逻辑的软件开发
- DDD的核心是领域模型,它试图解决如下几个问题:
- 跨部门沟通的通用语言
- 领域核心概念描述:针对需求端
- 方便编码落地:针对开发端,对应用OOP编码实现
- 反映变化,可适应敏捷开发的扩展性需求
- 基于DDD的软件开发,可以保证软件的概念完整性,利于软件整个生命周期的管理以及健壮发展。
- 软件设计方法横向比较
- 瀑布设计:分阶段推进;单向无反馈,设计定式
- 敏捷设计:持续迭代、单人全权把握;由于单人负责,软件开发流程中缺乏规约,产品可读性差,对外沟通成本高。
- DDD:开发的整个过程都以领域模型为蓝本,设计和实现都透明、可维护;另外拥抱变化,所有变化都能及时在领域模型中反映出来。
模型设计
操作步骤
1. 领域分析
- 与领域专家沟通,沉淀核心领域概念和知识
- 核心/本质:问题域分析;明确业务中需要解决的核心问题
2. 领域建模
- 对领域核心概念进行组织,表达领域内的逻辑、流程、操作
- 核心/本质:构建领域模型
3. 代码设计
- 以OOP编程思想为基础,将领域模型翻译成编码模型
DDD分层架构(layered architecture)
- 分层设计原则:每一层是一个内聚的集合,设计场景中每一层只依赖它的下一层
分层概览(自顶向下)
- 展示层(UI)
- 应用层(Application)
- 领域层(Domain)
- 基础设施层(Infrastructure)
领域模型要素
要素分类概览
- 元素
- 对象:实体、值对象
- 非对象:服务
- 逻辑抽象
- 模块
- 聚合
- 对象生命周期管理
- 访问:聚合
- 创建:工厂
- 持久化:repository
实体(entities)
- 带唯一标识(在整个软件生命周期中都不会发生变化)
- 需持久化
值对象(value objects)
- 只包含属性(属性的集合,无需维护唯一标识)
- 特征
- 无状态:无需持久化
- 可共享:推荐作为immutable实现(类似Java字符串对象)
- 生成副本简单
- 内部属性具有内聚性
服务(service)
- 不隶属于任何对象的操作,作为不同对象之间的连接
- 特征
- 无状态
模块(modules)
- 具有某种功能的相关对象的集合,体现对复杂模型的某种划分及粗粒度概括
- 产生背景:处理模型复杂性的一种手段
- 属性:功能内聚;模块划分的基本依据是对象之间的内聚性,而内聚性可以有多种不同维度(如功能性内聚、通信性内聚),其中上述提交的功能性内聚被认为是最佳的划分形式。
- 对外:接口
聚合(aggregates)
- 针对数据变化可以考虑成一个单元的一组关联对象(状态具有一致相关性的一组对象 or 状态变化在逻辑上相关的一组对象)、一组具有协同关系的对象
- 背景、作用:解决相关对象的状态一致性问题
- 对外:根;用来定义对象所有权和对外访问边界
- 原理:
- 将实体和值对象封装在聚合之中,并且定义聚合之间的边界。
- 为每个聚合选择一个实体作为根,并且通过根来控制所有对边界内对象的访问。
- 仅允许外部持有对根的引用以及内部对象的值传递
工厂(factories)
- 定义:封装对象创建所必须的知识
- 背景:解决对象构建过程涉及复杂领域知识的场景,由独立第三方控制对象创建
- 属性:原子性
- 对外:以接口形式暴露功能
资源库(repositories)
- 定义:封装对象持久化和获取所需的逻辑
- 背景:引用方直接访问DB的问题
- 功能:将领域对象和底层基础设施解耦
- 实现:对象CRUD以及针对属性的筛选功能
模型上下文(界定上下文 Bounded Context)
- 模型上下文划分原则:保证每个划分内模型的独立性、一致性,从而使之后各模型能独立演化,尽量不交叉影响。
- 上下文映射context map:定义了不同模型上下文件之间的关联,是不同模型集成的基础。
- 6种上下文映射类型
- 共享内核shared kernel
- 消费者/供应商 customer/supplier
- 顺从者 conformist
- 防崩溃层 anti-corruption layer
- 隔离通道 seperate ways
- 开放主机服务 open-host service
共享内核shared kernel
- 共享内容:模型、代码、数据库设计
- 注意事项
- 修改及时集成
- 修改及时与对方沟通
消费者/供应商 customer/supplier
- 定义:一个系统的结果作为另外一个系统的输入
- 存在条件:是供需双方互相感兴趣,并且彼此都处于成长阶段。
- 协作模式:
- 定期交流
- 需求排期
- 接口定义
- 交付验收
顺从者 conformist
- 定义:客户完全遵从供应商的模型和所提供的服务
- 存在条件:供应方的模型已经成熟并且标准化
防崩溃层 anti-corruption layer
- 背景:面对遗留系统(无法改造、无法和当前系统保持设计风格一致性),提供的保护一种转换机制,目的是保证当前系统模型设计的纯洁性、一致性。
- 定义:在两个系统之间扮演双向转换器的角色,隔离彼此的差异,从而使客户端模型不受外部系统模型的污染。
- 实现
- 外观模式facade
- 适配器adapter
- 转换器translator
隔离通道 seperate ways
- 背景:彼此独立的系统,之后也无集成
- 各系统独立开发
开放主机服务 open-host service
- 标准化封装和开放公用、通用服务
模型提炼
- 思想:提炼复杂模型中的 核心领域(core domain),将其与 普通子域(generic subdomain) 区别对待,从而聚焦精力使投入产出最大化。
- 核心域识别:领域本质、项目动机
核心领域
- 核心人才
- 持续迭代、投入
普通子域
- 普通人员
- 尽量复用已有方案
应用落地
DDD分层 vs 传统架构分层
建模方法
- DDD 的一个生命周期是这样的:在设计和实现一个系统的时候,这个系统所要处理问题的领域专家和开发人员以一套统一语言进行协作,共同完成该领域模型的构建,在这个过程中,业务架构和系统架构等问题都得到了解决,之后将领域模型中关于系统架构的主体映射为实现代码,完成系统的实现落地。
- 设计领域模型的一般步骤如下(摘自“美团实践”)
- 根据需求划分出初步的领域和限界上下文,以及上下文之间的关系;
- 进一步分析每个上下文内部,识别出哪些是实体,哪些是值对象;
- 对实体、值对象进行关联和聚合,划分出聚合的范畴和聚合根;
- 为聚合根设计仓储,并思考实体或值对象的创建方式;
- 在工程中实践领域模型,并在实践中检验模型的合理性,倒推模型中不足的地方并重构。
用例分析法
用例分析法是领域建模最简单可行的方式。大致可以分为获取用例、收集实体、添加关联、添加属性、模型精化几个步骤。 获取用例:提取领域规则描述 收集实体:定位实体, 添加关联:两个实体间用动词关联起来 添加属性:获取实体属性 模型精化:可选的步骤,可以用UML的泛华和组合来表达模型间的关系,同时可以做子领域的划分
四色建模法
四色建模法源于《Java Modeling In Color With UML》,它是一种模型的分析和设计方法,通过把所有模型分为四种类型,帮助模型做到清晰、可追溯。 简单来说,四色关注的是某个人的角色在某个地点的角色用某个东西的角色做了某件事情。
事件风暴法
事件风暴法类似头脑风暴,简单来说就是谁在何时基于什么做了什么,产生了什么,影响了什么事情。
4种模式(模型4种实现方式)
失血模型
- 模型中只有简单的get、set方法,是对一个实体最简单的封装,其他所有的业务行为由服务类来完成。
贫血模型【推荐】
- 在失血模型基础之上聚合了业务领域行为
- 领域对象的状态变化停留在内存层面,不关心数据持久化。
充血模型
- 在贫血模型基础上,集成数据的持久化。
胀血模型
- service都不需要,所有的业务逻辑、数据存储都放到一个类中。
DDD vs 微服务
- DDD 的本质是一种软件设计方法,而微服务架构是具体的实现方式。微服务架构虽好,但是他并没有给出如何对复杂系统进行分解的具体方法论,而 DDD 正好就是解决方案。
- 基于 DDD 的微服务设计和开发实战
- 设计面向 DDD 的微服务
- 为什么在做微服务设计的时候需要DDD?
- 为什么DDD是设计微服务的最佳实践
美团实践
参考资料
- 《Domain-Driven Design Quickly》,Abel Avran & Floyd Marinscu著,孙向辉等译
- 面试官:谈一下你对 DDD 的理解?
- 问题域 vs 解决方案域