介绍
#
介绍
Domain-driven Design
针对传统软件开发流程(分析-设计-编码)各阶段业务割裂问题,一开始定义好领域
目标
维护概念完整性(纯洁),避免语义泄露和腐化
概念
#
领域(Domain)
界限上下文(Bounded Context)
领域模型(Domain Model)
领域通用语言
分层架构
展示层、应用层、领域层、基础设施层
最佳实践
关联尽量少、尽量单项、尽量降低整体复杂度
实体(Entity)
领域中的唯一标识,属性尽量少
值对象(Value Object)
没有唯一标识,属性值不变
领域服务(Domain Service)
协调多个领域对象,只有方法没有状态
应用层服务、领域层服务、基础层服务
聚合、聚合根(Aggregate, Aggregate Root)
聚合定义了一组有内聚关系的对象集合,聚合根是对聚合引用的唯一元素
修改聚合必须在事务级别
70%的聚合只有一个实体, 30%有2到3个实体。
只有一个实体时,实体就是聚合根。多实体时思考哪个对象有独立存在的意义,且可与外部直接交互
工厂(Factory)
工厂模式
仓储(Repository)
持久化到DB,管理对象
只对聚合设计仓储
建模
#
原则
#
简单、容易、清晰
使用不动点
领域专注
聚合内强一致,跨聚合最终一致
内部概念完整一致(unification)
术语不变、不矛盾、不重叠
数据模型(DO)
#
失血
DO和DAO无业务逻辑,纯数据
贫血
持久化逻辑在DAO中
充血
service很薄,持久化的逻辑在DO中,无DAO或与DO双向依赖
肿胀
无service,全部逻辑放DO
分析设计
#
分析模型
#
# 业务领域分析, 不考虑代码
问题
含意不完整,不可图形或文字表达,错误假设
会深入某细节
忽略某细节直到设计或实现, 如持久化、性能
目标
领域模型
架构设计
事件风暴 # 是开发建模,不是用户需求故事
准备
功能确认: 近期milestone
找正确的人: 领域专家, 前后端,架构师
引导者: 准备资料, 排程, 时间, 2/3时间预警
事件风暴
领域事件: 用户可感知状态
分支小组 -> 个人发散 -> 小组一致 -> 整体一致 # 不能一致表示准备不足
逻辑顺序 -> 最终流程
命令风暴 # 为什么, 分色
事件触发原因、方式
用户角色
读模型: 用户前置需求
写模型: 动词
描述
聚合
取名, 分职责
持续探索
领域模型一开始就结合编码设计 # 设计围绕模型, 模型受设计反馈改善
开发时意识到模型变更, 会保持完整性
每个开发在修改前需要了解模型
面向对象更易于建模, 过程化易于流程,如数学
重构
#
要求
设计灵活
使用经过验证的构造
目标
领域理解更深、更清晰
深刻(incisive)、深层(deep)的模型
技术的动机的代码转换
实现
小幅可控
基于测试
突破
新的概念或抽象
隐含的概念被凸显
倾听领域语言
过分复杂是因为关键点被替代
领域文献 # 深层视图
约束 # 表达不变量
过程(process) # 面向对象中的面向过程, 多个过程时用策略
规约 # 测试对象返回布尔值, 重构成对象而非写在application
战略建模
#
# 形成上下文映射图
问题空间
领域 # 与公司组织关联
子域 # 最好对应一个限界上下文
核心域(core domain) # 项目动机, 公司核心竞争力, 尽量小, 最高优先级
通用子域(generic subdomain) # 作用于整个系统的支撑子域
支撑子域 # 重要非核心
集成
合作关系(partnership) # 同时成功失败
共享内核(shared kernel) # 小型内核, 持续集成功能
客户/供应(customer-supplier development) # 上下游
遵从(conformist) # 下游遵从上游
防腐层(anticorruption layer) # 翻译转换领域服务
开放主机服务(open host service) # 公开协议,子系统访问
发布语言(published language) # dsl, 通常与开放主机服务一起
分隔(separate way) # 声明无关联
大泥球(big ball of mud) # 已有纠缠的系统,隔离出来
解决方案空间
通用语言
一个限界上下文一个通用语言
清晰(概念无二义性), 简洁 # 如卖家和买家都叫用户,就是不清晰。如用type标记用户是卖家或买家,就是不简洁。所以直接用两个对象
限界上下文 # 条件的集合
目的
确保术语含义明确
切分规模, 易于保持领域纯洁
设定进化框架而非模块,包含模块
考虑因素
团队组织结构
应用特定部分惯例、物理表现
挑战
团队开发碎片化 # 写重复的代码,由于不知道或怕改错
持续集成
早合并
自动构建测试 # 检测不一致
模块
作用
降低模型规模复杂度
代码高内聚低耦合
设计
通信性内聚(communicational cohesion)
功能性内聚(functional cohesion)
每模块统一接口
名称反映深层理解
灵活性,进化性
上下文映射 # 领域间集成关系
模式
共享内核(shared kernel) # 为减少重复, 共享领域子集,多方测试
客户-供应商(customer-supplier) # 做反馈的需求, 需求测试, 自动化验收
顺从者 # 供应商不做需求, 客户用适配器对接组件
防腐层(anticorruption layer) # 双向领域模型转换器, 保持内部模型纯洁
从前
原始数据(api, db)无模型无语义的处理
实现
对外多门面(facade)
每个门面一个适配器(adapter)
适配器间用转换器(translator)
隔离通道(separate way)
开放主机服务(open host service) # 实现开放服务协议
提炼 # 多次重构后还很大
实现
分离基本概念和普通概念, 提炼核心域和子域
子域
使用第三方服务
外包
修改已有模型
六边形架构
领域模型简洁自治
对外适配器防腐, 保护限界上下文 # 如面向接口
消息, 内存, 数据库
soap, rest
CQRS(command query responsibility segregationg) # 修改只记事件(日志), 查询时计算
查询方式
单数据库/读写分离,查询时计算事件
读写分离, 读库异步计算事件保存冗余, 读库负载均衡
战术建模
#
# 组成限界上下文
领域
实体(entity) # 标识和延续性, 有id, 持续变化。
值对象(value object) # 无id, 只有属性, 最好不可变(可共享)。尽量建模值对象。可包含实体引用或值对象。
生命周期
聚合(aggregate) # 定义对象所有权和边界
简化
关联 # 可导航到的关联
1对1 # 对象引用
1对n # 包含集合
n对n # 删除关联,关系加约束或转换
目的
一致性
强化不变量
实现
聚合根(root) # 聚合根间是最终一致性
是个实体,有id
外部访问的唯一对象
向外传递副本
工厂(factory) # 在领域中没有定义, 但程序需要
目的
并非对象创建对象
对象创建存在自有知识
创建过程原子性
对已有持久化对象重建并修复
问题
外部访问根内对象,需关联不必要的根实体
实现
不用工厂
构造不复杂
不涉及其它对象
客户希望用策略创建
类是具体类型, 无层级
聚合根提供方法
单独工厂 # 违反了封装原则, 但保持了简单
资源库(repository) # 内存假象
目的
不关联根获取对象引用
不暴露细节, 会减少领域专注
防止代码扩散
减少变更修改
维护聚合封装性
容易的基础设施被滥用, 产生除聚合根外导航
实现
封装所有获取对象逻辑
基础设施, 全局可访问
不同对象不同策略访问、存储 # 领域与基础设施解耦
接口是领域模型, 实现像基础设施
参数筛选或规约(specification)筛选(筛选器)
entity
介绍
entity即状态
应用开发即处理entity的表现
主从
主存储(可变) # 关键是选择主存储
多派生一致性好保障
派生表达业务的难易成度
只读派生(representation, 不可变)
多份存储, 一致性
派生, 合并, 转化
类型
东西(可变) # 单据叠加成东西, 东西叠加成东西
单据(可变) # 事件叠加成单据
事件(event, 不可变)
命令(command, 不可变)
视图(view model, 不可变)
子集(subset, 不可变)
视图(aggregation, 不可变)
表单(可变) # 是主存储
物理介质
OLTP(mysql) # 点查询
OLAP(clickHouse) # 范围查询
queue(kafka) # 顺序读, 低延迟
业务服务 # 业务逻辑, 像虚拟的表
分组entity主存储(BC, bounded context)
目的
分解
管理复杂度
系统
组织部门
实现内部一致性
概念, 数据
对主存储进行受控的修改
边界entity # 用于集成,不一定是主存储
形式
授权、binlog、工作流、视图数据、租户作为其它租户user
东西、单据、event
介质
queue, 带权限db, rpc虚拟表
触发
queue, ui, api
触发由worker托管, 输入是queue或rpc socket
粒度
分entity
分步骤
分entity字段
原则
BC尽可能少而大
关系
时间错开
外键关系 # BC挂载到BC, 如后台系统与计费系统的定价, 运营人员与服务系统的配置, 流程节点系统对流程的依赖
rpc, 数据库, 数据复制
报表关系
时效性高
一般做复制 # 所以边界entity是数据变更event
触发关系 # fire and forget
交棒关系
下游给上游command/event, 上游触发
上游实现降级 # 下游不可用时,安慰语
时间同时
accountable/responsible关系 # 负责人与实现人
原则
accountable尽量小
只调度
与responsible的边界entity是rpc虚拟表, 请求command, 返回event
补偿实现一致 # 如超卖
responsible提供自己界面 # accountable不控制
抢资源关系
锁服务
服务(service) # 无法划分对象的动作, 无状态。按功能分组, 多对象的连接点
可在application, domain, infrastructure
最终一致性建模
#
In-Memory
#
聚合根在内存,同步最新状态
事件溯源(Event Sourcing)
#
没有CRUD,只有Append Event。数据不可变
对象最新状态通过事件溯源获得
Actor
#
通过Mailbox取代调用,保证消息线性处理
EDA(Event-driven Architecture)
#
节点只处理逻辑,节点间Event通信
是最终一致性的架构
CQRS
#
介绍
Command Query Responsibility Seperation
CQ接口分离、代码分离,分别设计
概念
项目结构
#
用户接口(user interface)
应用(application) # 尽可能小。数据验证,事务。故事, 表达出操作的事情
application service
unit work
presentation model
领域(domain) # 专注领域。准确定义业务对象
aggregate, entity, value object
domain service, domain event
基础设施(infrastructure) # 辅助层
repository
global support
项目文件
[ui]
mall # 商城api
[saleDomain]
[application]
mall.application # 分模块,讲述故事
CartService
GetCart()
BuyService
Buy()
mall.application.domainEventSubscribers # 订阅domain事件
[domain]
mall.domain # 不大而全,要求刚好满足需求
cartModule
entity
CartItem
aggregate
Cart
valueObject
Product
SellingPriceCart
IDomainServices
IRemoteServices # 访问远程资源接口
IUserService
ISellingPriceService
IRepositories # 仓储接口
ICartRepository
mall.domain.events # 领域事件, 用于实现最终一致性
mall.domainService # 操作domain的无状态方法
ConfirmUserCartExistedDomainService
[sellingPriceDomain] # 与saleDomain合作关系, sale请求sellingPrice定价
[appication]
mall.application.SellingPrice
dto
CalculatedCartDTO
mapper
ValueObjectToDTO
[domain]
[infrastructure]
mall.infrastructure # 通用类库
domainCore # mail.domain base方法
AggregateRoot
Cart
Entity
CartItem
ValueObject
Product
IUnitOfWork # 仓储事务
domainEventCore
DomainEvent
DomainEventBus
DomainEventSubscriber
IDomainEvent
IDomainEventSubscriber
mall.infrastructure.repositories # 仓储
CartSqlServerRepository
mall.infrastructure.translators # 防腐层, 访问远程资源实现
user
UserAdapter # 请求原始结果
UserService
UserTranslator # 转换原始结果
模块结构
api: controller
biz: 特异业务
manager
converter
core: 公用业务
model
entity
bo
service
repository
common
dal
dataobject
do
dao
mapper
service
facade
dto: facade和controller用
service: 服务间api
validate
integration
service
shared
dto: 项目内部公用