1. DDD设计篇:运用事件风暴法进行业务领域建模、统一语言建模
1.1 如何成为优秀架构师?
架构师 = 技术大牛?
架构师不仅需要懂技术,还要懂业务。
只有将业务落地到技术,开发出对用户有价值的产品,技术才是有价值的。
什么是业务架构师?
掌握了业务领域知识,掌握了业务痛点,然后用技术方案,解决业务痛点,才能为公司创造价值。
架构师
-
要能够将业务转换为技术
-
能合理运用技术支撑业务
客户买单,不是买技术,而是买你用技术解决我业务痛点的技术方案。
所以上面一步步分析后,业务痛点是关键
1.2.1 对业务及其痛点,有深刻的理解与思考
不仅要理解业务,还要挖掘出业务痛点,才才可以用对应的技术来解决问题
-
分析业务流程
-
理解业务规则
-
挖掘业务痛点
1.2.2 能够将技术落地,产生业务价值
- 规划高远却落不了地
- 快速研发产生业务价值
关键难题:
- 如何快速有效地学习业务领域知识
- 如何深入地理解与挖掘业务痛点
- 如何通过技术的手段落地业务
系统没那么复杂时,需要领域驱动吗?
系统不复杂时,领域驱动有点大材小用,以前互联网时代没到来,没有发生数据和需求井喷,业务也不复杂,所以不会流行起来。
现在基本都需要,特别是互联网业务。
1.2 领域驱动设计在什么时候起作用?
1.2.1 在新项目开发中起作用?
-
深刻理解业务,进行领域建模
-
然后把业务转换为领域模型,
-
再用领域模型指导数据库设计和程序设计
-
按照贫血模型或者充血模型转换为程序设计
这个时候,一开始需要花大量时间进行领域建模。
但是新项目有一些特点
1、复杂度没那么高
2、要快速交付
怎么办?一开始没办法,用领域驱动,是考虑长远,在日后,项目需求变更、维护。
1.2.2 在老项目维护中起作用?
面对老项目越来越复杂时,领域驱动会更好的发挥作用
快速交付?
通过领域驱动设计,提高维护的质量,使得可以在业务不断迭代、市场激烈竞争、技术快速更新、系统越来越庞大时,依然能快速交付高质量的产品。
我们采用整洁架构,将业务代码和技术框架,通过一个中间层进行解耦。
今后,我们的业务代码随着业务不断迭代,技术框架也可以不断迭代调整,互不影响。
1.2.3 在技术架构演化中起作用?
1.3 领域驱动的解决之道
首先,业务负责人(或产品经理),以故事的形式,将功能分发给多个敏捷团队,每个敏捷团队负责一个功能模块。
每个敏捷团队,在开发的过程中,结合领域驱动设计和微服务架构理念,设计功能。
我们按照业务去拆
微服务如何拆分?
接口怎么划定?
怎么将每次的需求只落到一个微服务里?
让每个微服务,都是软件变化的一个原因,
今后因为这个原因而发生的变更,都只发生在这个微服务。微服务的优势才真正发挥出来了。
每次需求变更时,基于领域驱动设计,进行业务建模,
最后把对业务建模的变更,落实到对微服务的变更
我们的系统才能高质量的低成本维护下去。
在我们结合领域驱动设计和微服务架构理念去做设计时,都是有一定的成本的,从而提高了系统设计的复杂度。
复杂度的增加,进一步降低了交付速度。又和我们的初衷产生矛盾。
我们的初衷,通过领域驱动设计和微服务,简化设计,降低维护成本,提高交付速度
而使用它们的成本,又会降低交付速度
这个矛盾怎么解决?
架构团队,架构一个支持微服务,支持领域驱动的架构,把我们在领域驱动中的一些复杂的设计,比如聚合、仓库、工厂下沉,落地到技术框架里,
一边基于业务领域模型,建立业务领域层,
一边将各个技术框架,通过适配器进行解耦。
这就是整洁架构。
1.3.1 为什么需要领域驱动设计指导微服务的划分?
首先看下,传统的烟囱式的数据库设计
这样的数据库设计,使用的时候,比如获取商品信息这个数据的时候,各个微服务都要去读取商品表,
一旦商品表发生变更,各个模块都要变更代码。
这样维护成本非常高。
怎么办?
那我们希望某一次变更,只需要改某个微服务的代码,缩小需求变更的影响范围
1.3.2小而专的微服务设计
小:拆分微服务
专:单一职责原则(容易被忽略)
怎么做?
要求每个表只能有一个微服务直接操作数据库,其他微服务想要操作是不行的,只能调用对应微服务的接口。
这样设计后,商品表变更,只会影响到商品维护这个微服务。
只要对外接口不变,其他微服务也不需要改代码,影响很小
对数据表进行规划,哪个微服务操作哪些表。
提高内聚,从业务角度分析整理,划分清楚边界。
业界现在最有效的做这个事情的方法,就是领域驱动设计,
通过对业务的梳理,逐步建立起限界上下文。
然后基于限界上下文设计和开发系统
1.3.3 跨库关联查询解决方案
方案:按照领域模型建模,建立好关联关系,底层架构自动帮我补填数据。
比如我下单,我只关心查订单数据,我建立领域模型时,建立了订单和其他模块的业务关系,配置好业务关系,底层架构自动帮我补填数据。
1.4 本课程要解决的问题
领域驱动设计的作用与意义
真正的目的不在于开发新项目,新项目没有那么复杂,而在于,日后的维护,系统越来越复杂时,体现出来。
一开始用领域驱动的意义,在于,日后的维护,系统复杂的时候,提高代码质量,降低交付时间。
怎样正确地进行业务领域建模
今天的内容
事件风暴
需求变更
为什么越来越复杂?
这是软件发展的必然规律
传统的设计方式,随着新需求的到来,不断地往payoff()方法里塞代码。
这样会导致代码质量不断下降,维护成本不断提高。
软件退化的根源,不是需求变更
当我们需求变更的时候,我们适时地调整代码。进行解耦和功能扩展,再去实现需求,就能够实现高质量代码
那么我们怎样适时调整代码,解耦和扩展呢?
当发生10次、50次、100次变更后,可能就迷失方向了
我们需要一个方法,无论多少次变更,都能够产生高质量代码
当我们需求变更时,把变更还原到真实世界里,真实世界是什么样子,我们软件就怎么变更。
这样,无论发生多少次变更,我们也能找到方向。
在领域模型里变更,思考设计
面对变更,传统设计方式如下图,直接塞代码。
而领域驱动的设计方式,我们先将新需求,还原到领域模型上,进行分析
我们要增加折扣,而且有多种不同方式的折扣。
我们需要思考,付款和折扣有什么关系?
折扣的功能,怎么添加到现有功能里?
传统思考方式:折扣是在付款的时候折扣,就写在付款的逻辑里
那么难道不写在付款里?为什么不该写在付款里?是基于什么原则去思考的吗?
有,单一职责原则
抽象出限界上下文,每个限界上下文就是一个微服务
每个微服务是软件变化的一个原因,有需求变更,只需要改某个微服务
怎样将模型转换为程序设计
明天分享
支持领域驱动设计的架构设计
第三天的分享内容
1.5 单一职责原则(SRP)
软件系统中的每个元素只完成自己职责内的事,将其他的事交给别人去做。
“职责”通常理解为一个事情,与该事情相关的事都是它的职责。
一个所谓职责,其实是软件变化的一个原因。
软件质量如何衡量?高还是低?
关键:维护成本
一个需求来了,我要改3个模块的代码
一个需求来了,我要改1个模块的代码
要求我们
平时,就要整理代码。
把同一个原因的代码放一起。
不同原因的代码分开放
现在要增加折扣,问自己两个问题:
付款变了,要不要变折扣?不要
折扣发生变更,付款要不要变?要
折扣是付款的另一个变化原因,不应该把折扣放进付款里
1.6 领域驱动设计与传统软件开发有什么不同?
项目不变更,不需要添加新功能,不会越来越复杂,就不需要使用DDD
新项目里用领域驱动设计,是为了日后需求变更,维护起来更容易。
按照领域驱动设计,每次变更的时候,不是直接coding,而是把新需求,先放到领域模型里,在领域模型里设计,还原到真实世界。
领域驱动设计更适合老项目
是不是增加了工作量呢?
需要有一个支持领域驱动的底层架构
如何设计这样一个底层架构?
在第三天的课里
1.7 案例:远程智慧医疗系统
对前面的思想进行实战
1.7.1 期初:传统的诊所管理系统
护士接待患者,指引患者挂号,
然后由医生去看
医生建议患者去检查,做B超、验血
患者去交钱,验血
医生看报告,开药
患者交钱去药房拿药
1.7.2 统一语言与领域建模
用客户的语言,理解业务
1.7.3 事件风暴
迭代会议之前,开一个事件风暴会议
已确诊和已开药是否是同一个事件?
查找医生算不算一个事件?
只是个查询,不需要记录,预约了才要记录
领域事件分析全了么?还有已排班,已注册
分析和事件相关联人和事
预约是一个命令
触发者 是患者
相关的还有预约时间
出诊计划,哪个医生、哪个科室,什么时间出诊
梳理人和事的关系,
人和事是不是聚合
药品明细和处方是一个部分和整体的关系,有聚合关系,贴一个紫色便签
是不是聚合关系?
如果是聚合关系,部分的生命周期一定是在整体里
比如,处方产生了,才有药品明细,当处方删除,药品明细才会删除
1.7.4 领域建模
2 DDD实践篇:通过领域模型落地系统设计:数据库、聚合、工厂与仓库
2.1 将领域模型转为数据库设计
表、表之间的关系
2.2 将领域模型转为程序设计
实体、值对象(不要太纠结)
实体:每个同学,有一个编号学号,
值对象:同学、老师
贫血模型的设计
充血模型的设计
充血模型的service,把订单作为一个整体,订单对象里有依赖的其他对象的,定义了他们的关系。订单service不需要处理订单和订单明细的依赖关系。
订单service只需要保存订单,也不需要关心保存到两个表
订单和订单明细是聚合关系,订单仓库来做保存到具体的表的事情。
查询的时候,订单工厂,会补查用户、用户地址等信息
好处:
service不需要关注底层,只需要关注业务逻辑判断
是不是要为每个领域对象设计一个仓库和工厂?
框架
2.3 聚合
对象和对象之间有整体和不分关系的时候,只操作整体
订单和订单明细是整体与部分的关系。聚合关系
查询和保存操作都是对订单,订单明细的查询和保存操作在订单里完成
订单就是聚合根
2.4 工厂(Factory)/仓库(Repository)
2.5 领域驱动设计的分层架构
dao变成了仓库,做了更具体的事情
2.6 互联网转型:远程接诊平台
2.7 互联网转型带来的阵痛
2.8 系统规划与架构设计
互联网转型后的领域建模设计
CQRS命令查询指责分离
2.9 四色建模法
通过适配器,解耦业务代码和技术架构
总结
-
领域驱动设计适合大型复杂项目,不适合初始项目
-
领域驱动设计适合频繁需求变更的项目
-
通过领域建模,划分限界上下文,根据限界上下文拆分微服务
-
有新需求或者需求变更,先将需求放在领域模型内讨论设计,再将领域模型转为数据库设计和程序设计
-
领域驱动设计时,可以采取事件风暴会议形式,和领域专家一起,使用统一语言讨论,一起设计领域对象和关系
-
事件风暴会议需要确定一些事情:分析有哪些领域事件、确定有哪些和领域事件相关的人和事、确定触发事件的命令、确定领域事件关联的人和事有没有聚合关系
-
将领域模型分配到各个限界上下文中,构建上下文地图
-
新需求的代码写在哪个模块里,采用单一职责原则作为指导
-
单一职责中的职责是指引起软件变化的一个原因
-
平时,就要整理代码,把同一个原因的代码放一起,不同原因的代码分开放
-
软件退化的根源不是需求变更,而是在需求变更时,没有及时调整代码,没有正确的调整代码
-
贫血模型设计的代码,领域对象只有状态,service层包含各种行为,service层很重
-
充血模型设计的代码,更符合面向对象,领域对象不仅封装了状态,还有各种修改状态的行为,service层更轻,service层只处理业务逻辑的封装、事务、权限