DDD和功能解耦
如果无法正常显示,请先停止浏览器的去广告插件。
1. DDD和功能解耦
京东 7FRESH 阎华
2. 1.
2.
3.
4.
5.
为什么选择DDD
DDD和系统解耦
DDD和统一语言
总结
3. 我们为什么选择DDD
Y轴扩展性成为瓶颈
领域模型不合理
这意味着Y轴的扩展要灵活
低内聚高耦合
影响Y轴扩展的两个因素
4. 我们为什么选择DDD
问题一:领域模型不合理
错误的
数据结
构
错误的
业务约
束
产品经理关注上层的业务流程和交互
缺乏业
务语义
的模型
没有发
现隐式
的概念
程序员关注技术实现
5. 我们为什么选择DDD
数据格式由
B来定义
问题二:系统功能间的各种耦合
A
B不需要
B需要
B
B
A的数据
耦合的类型
A
B
Data Coupling
Stamp Coupling
A
B需要
Content Coupling
A
B
全局数据
Common Coupling
6. 但是DDD落地很难
7. 让整个团队弄清楚复杂而抽象的方法论,太难了
8. 那怎么办?
简化,小步,例子多
9. 而是面向对象分析和设计
最先要普及的不是DDD
10. | 二十多年前人们就推崇用值对象了
| 那时DDD还没有诞生
丰富的表
达力
比如电话号码的
校验
https://riehle.org/computer-
science/research/1998/ubilab-tr-1998-10-1.html
不可变性
带来的线
程安全
Primitive Obsession
| 基本类型偏执
| 2000年
这种坏味道是指用基本类型
来表示领域概念,比如用字
符型来表示电话号码、邮政
编码等
解决办法是定义明确
ValueObject 。
集中的合
法性校验
和易测试
性
后面会有实际的
例子
封装带来
的设计柔
性
降低认知负担提升
可读性
11. | 实体和聚合早就被广泛认可了
富血模型
| Since
|1997年
贫血模型
12. | 很多原则和模式在系统级别依旧有效
设计原则
•依赖倒置
•开放封闭
•单一职责
•信息隐藏
•……
设计模式
•工厂
•适配器
•策略
•状态
•……
架构模式
•分层架构
•整洁架构/洋葱模型
•端口适配器架构
•CQRS
•……
13. | 面向对象富血模型没落史
| 背后的驱动力
专业的单机软件向以数据为中心的网络软件的演变
| 1980s,Smalltalk/C++ 带领下面向对象广泛流行
| 1991,Visual Basic 出现属性和属性列表功能
| 1992-1995,可视化工具和IDE遍地开花
| 1996,JDK1.0发布
| 1997,JavaBean规范发布
| 1998 -,Java和.NET平台出现大量的通过反射
| 机制处理对象属性的工具和框架
FROM 《实现领域驱动设计》
14. 理论上,使用DDD并不一定要通过面向对象,
但实战中,使用面向对象进行分析设计是最务实的。
那么,DDD在面向对象之外还带来了哪些新的东西?
15. 首先要理解的是
限界上下文
该了解些DDD
最核心的概念了
16. | 案例分析1:概念不能穿透上下文
1
3
2
除了更好的表意性外
有更好的扩展性
17. | 案例分析1:变化控制在局部
代码不用变
RPC
API
Controller
企业客户适配器
领域层
业务逻辑
数据库
用户适配器
RPC
API适配器
企业客户适配器
u 只有适配器部分的代码需要变化,包含业务逻辑的领域层代码不需要变化
u 通过编译器的静态类型检查,适配器的变化点很容易识别
u 适配器充当防腐层(ACL),起到了概念隔离和功能解耦的作用
18. | 案例分析2:上下文关系和Common Coupling
读取订单上的标记
WMS
TMS
交易服务
订单
订单生产
控制中心
餐饮
“订单标记委员会”
分布式大泥团系统的守护神
按各系统的需要
生产订单时
在订单上打标记
读取订单上的标记
读取订单上的标记
读取订单上的标记
购物卡
标记太多位,含义二义性,
新增需求改动的系统太多,
影响范围难以界定,沟通成
本非常高……
19. | 案例分析2:上下文关系和Common Coupling
自己领域的
守护神
U
交易服务
WMS
D
D
U
TMS
U
订单
U
D
订单生产控制
D
中心
U
餐饮
D
U
自己领域的
守护神
自己领域的
守护神
自己领域的
守护神
U
购物卡
D,下游; U,上游
自己领域的
守护神
清晰的上下文映射关系,
下游理解上游的领域概念,
上游不理解下游的领域概念,
上游制定标准,
下游适配标准。
* 在 DDD 的上下文映射中,我们优先使
用这种 “ 客户 - 供应商 ” 的模式。
20. | 案例分析2:进一步要避免的问题
上游“老好人”,边界没守住,造成Stamp Coupling
下游要保持独立性,需要使用适配器/防腐层
数据
TMS
不需要
D
WMS
TMS
需要
D
U
TMS
WMS
TMS产品研发
WMS产品研发
U
TMS
TMS产品研发
WMS产品研发
21. | 案例分析1&2:系统间的依赖倒置/ACL保证Data Coupling
下游定义的接口
(D)下游
(U)上游
适配上下游接口的实现
领域层
业务逻辑
数据库
API
Controller
RPC
用户适配器
API适配器
领域层
业务逻辑
22. | 案例分析3:上下文级贫血和Control Coupling
1.获取订单数据
做逻辑判断
外部调用或消息
订单生产控制
中心
外部调用或消息
订单
订单生产控制
中心
2.设置订单状态
3.操作其它系统
1.给订单状态机
输入可枚举
的事件
2.监听得到订单
状态的变化
3.操作其它系统
订单
23. | 案例分析4:聚合根的作用
聚合根是一组实体和值对象的一致性边界。
在这个边界之内维护不变条件,即业务规则。
这不是DDD引入的新原则,聚合就该如此。
DDD的新原则包括:
n 通过唯一标识引用其他聚合
n 在边界之外使用最终一致性(比如通过领
域事件),一个事务只能修改一个聚合
聚合的识别很难,领域事件、领域服务和
CQRS的正确应用可以减少技术因素对聚合
根识别的干扰。
这样做的好处是能更好地支持架构的演进
24. 其次要理解的是
统一语言
该了解些DDD
最核心的一些概念了
25. | 物理世界在不同上下文代表着
| 不同的概念
| 领域太复杂,只有在分割的上下文内
| 才可能形成统一的语言
店
仓
拣货打包车间
厨房
26. | 使用事件风暴形成统一语言
隐形概念的挖掘,聚合根的识别等都非常难且难以达成一
致,仅仅在方法论上达成一致还不够,我们需要一种容易
执行的工具
•
•
•
•
让开发者做回正常人
控制节奏,慢下来很难但很重要
学会问问题,挖掘隐形概念
命名很难,那就随便给个名字先达成一致
统一语言的形成
27. | 代码和分析模型的低表示差异
业务模型和业务逻辑都在这里
值对象
聚合根
领域事件
普通实体
Repository接口
和技术相关的实现
Repository实现
应用服务
应用服务应该很“薄”
28. | 没有一个完美的ORM可以用
JPA/Spring-Data-JDBC 富血,但性能不可控
JDBC/MyBatis 性能可控,贫血
RoR/Domain注入Repo(AspectJ ),貌似解决以
上问题,但污染领域模型
| 用JPA来实现,好处/坏处:
•
•
•
富血模型 ✔
聚合根的原则 ✔
但带来了糟糕的或不可控的性能 ✘
29. | 权衡
应用层
的方法
聚合根助手
的方法
聚合根的方
法
• 应用层尽可能薄,不包含领域层的业务逻辑
• 每个聚合根配一个无状态的助手,和聚合根一起体现
所有的领域业务逻辑
• 如果开始分不清代码放在哪一层,可以先实现然后通
过重构渐进下沉
30. 界定上下文并明确其间的关系
挖掘领域知识,识别隐型概念,
定义聚合根,形成团队理解一致的统一语言
让代码尽可能和分析模型保持一致
未必能尽善尽美,但可以不断趋近
31. THANKS