Loading

DDD领域驱动设计:业务分析神器

1. DDD设计篇:运用事件风暴法进行业务领域建模、统一语言建模

1.1 如何成为优秀架构师?

架构师 = 技术大牛?

架构师不仅需要懂技术,还要懂业务。

只有将业务落地到技术,开发出对用户有价值的产品,技术才是有价值的。

什么是业务架构师?

掌握了业务领域知识,掌握了业务痛点,然后用技术方案,解决业务痛点,才能为公司创造价值。

架构师

  • 要能够将业务转换为技术

  • 能合理运用技术支撑业务

客户买单,不是买技术,而是买你用技术解决我业务痛点的技术方案。

所以上面一步步分析后,业务痛点是关键

1.2.1 对业务及其痛点,有深刻的理解与思考

不仅要理解业务,还要挖掘出业务痛点,才才可以用对应的技术来解决问题

  • 分析业务流程

  • 理解业务规则

  • 挖掘业务痛点

1.2.2 能够将技术落地,产生业务价值

  • 规划高远却落不了地
  • 快速研发产生业务价值

关键难题:

  • 如何快速有效地学习业务领域知识
  • 如何深入地理解与挖掘业务痛点
  • 如何通过技术的手段落地业务

image-20211006193444872

系统没那么复杂时,需要领域驱动吗?

系统不复杂时,领域驱动有点大材小用,以前互联网时代没到来,没有发生数据和需求井喷,业务也不复杂,所以不会流行起来。

现在基本都需要,特别是互联网业务。

1.2 领域驱动设计在什么时候起作用?

1.2.1 在新项目开发中起作用?

image-20211006193826980

  1. 深刻理解业务,进行领域建模

  2. 然后把业务转换为领域模型,

  3. 再用领域模型指导数据库设计和程序设计

  4. 按照贫血模型或者充血模型转换为程序设计

这个时候,一开始需要花大量时间进行领域建模。

但是新项目有一些特点

1、复杂度没那么高

2、要快速交付

怎么办?一开始没办法,用领域驱动,是考虑长远,在日后,项目需求变更、维护。

1.2.2 在老项目维护中起作用?

image-20211006194413194

image-20211006194424835

面对老项目越来越复杂时,领域驱动会更好的发挥作用

image-20211006194714773

快速交付?

通过领域驱动设计,提高维护的质量,使得可以在业务不断迭代、市场激烈竞争、技术快速更新、系统越来越庞大时,依然能快速交付高质量的产品。

我们采用整洁架构,将业务代码和技术框架,通过一个中间层进行解耦。

今后,我们的业务代码随着业务不断迭代,技术框架也可以不断迭代调整,互不影响。

1.2.3 在技术架构演化中起作用?

1.3 领域驱动的解决之道

image-20211006195220682

首先,业务负责人(或产品经理),以故事的形式,将功能分发给多个敏捷团队,每个敏捷团队负责一个功能模块。

每个敏捷团队,在开发的过程中,结合领域驱动设计和微服务架构理念,设计功能。

我们按照业务去拆

微服务如何拆分?

接口怎么划定?

怎么将每次的需求只落到一个微服务里?

让每个微服务,都是软件变化的一个原因,

今后因为这个原因而发生的变更,都只发生在这个微服务。微服务的优势才真正发挥出来了。

每次需求变更时,基于领域驱动设计,进行业务建模,

最后把对业务建模的变更,落实到对微服务的变更

我们的系统才能高质量的低成本维护下去。

在我们结合领域驱动设计和微服务架构理念去做设计时,都是有一定的成本的,从而提高了系统设计的复杂度。

复杂度的增加,进一步降低了交付速度。又和我们的初衷产生矛盾。

我们的初衷,通过领域驱动设计和微服务,简化设计,降低维护成本,提高交付速度

而使用它们的成本,又会降低交付速度

这个矛盾怎么解决?

架构团队,架构一个支持微服务,支持领域驱动的架构,把我们在领域驱动中的一些复杂的设计,比如聚合、仓库、工厂下沉,落地到技术框架里,

一边基于业务领域模型,建立业务领域层,

一边将各个技术框架,通过适配器进行解耦。

这就是整洁架构。

image-20211006201106413

1.3.1 为什么需要领域驱动设计指导微服务的划分?

首先看下,传统的烟囱式的数据库设计

image-20211006195535129

这样的数据库设计,使用的时候,比如获取商品信息这个数据的时候,各个微服务都要去读取商品表,

一旦商品表发生变更,各个模块都要变更代码。

这样维护成本非常高。

怎么办?

那我们希望某一次变更,只需要改某个微服务的代码,缩小需求变更的影响范围

1.3.2小而专的微服务设计

小:拆分微服务

专:单一职责原则(容易被忽略)

怎么做?

要求每个表只能有一个微服务直接操作数据库,其他微服务想要操作是不行的,只能调用对应微服务的接口。

这样设计后,商品表变更,只会影响到商品维护这个微服务。

只要对外接口不变,其他微服务也不需要改代码,影响很小

image-20211006200056410

对数据表进行规划,哪个微服务操作哪些表。

提高内聚,从业务角度分析整理,划分清楚边界。

业界现在最有效的做这个事情的方法,就是领域驱动设计,

通过对业务的梳理,逐步建立起限界上下文。

然后基于限界上下文设计和开发系统

1.3.3 跨库关联查询解决方案

image-20211006202338414

方案:按照领域模型建模,建立好关联关系,底层架构自动帮我补填数据。

比如我下单,我只关心查订单数据,我建立领域模型时,建立了订单和其他模块的业务关系,配置好业务关系,底层架构自动帮我补填数据。

1.4 本课程要解决的问题

领域驱动设计的作用与意义

真正的目的不在于开发新项目,新项目没有那么复杂,而在于,日后的维护,系统越来越复杂时,体现出来。

一开始用领域驱动的意义,在于,日后的维护,系统复杂的时候,提高代码质量,降低交付时间。

怎样正确地进行业务领域建模

今天的内容

事件风暴

image-20211006203725526

image-20211006203738605

image-20211006203906407

image-20211006203928922

image-20211006203953635

需求变更

image-20211006204039799

image-20211006204124648

为什么越来越复杂?

这是软件发展的必然规律

image-20211006204204655

image-20211006204232919

传统的设计方式,随着新需求的到来,不断地往payoff()方法里塞代码。

这样会导致代码质量不断下降,维护成本不断提高。

软件退化的根源,不是需求变更

当我们需求变更的时候,我们适时地调整代码。进行解耦和功能扩展,再去实现需求,就能够实现高质量代码

image-20211006204552356

那么我们怎样适时调整代码,解耦和扩展呢?

当发生10次、50次、100次变更后,可能就迷失方向了

我们需要一个方法,无论多少次变更,都能够产生高质量代码

当我们需求变更时,把变更还原到真实世界里,真实世界是什么样子,我们软件就怎么变更。

这样,无论发生多少次变更,我们也能找到方向。

image-20211006204846712

在领域模型里变更,思考设计

image-20211006205024005

面对变更,传统设计方式如下图,直接塞代码。

image-20211006205055562

而领域驱动的设计方式,我们先将新需求,还原到领域模型上,进行分析

image-20211006205239226

image-20211006205252884

我们要增加折扣,而且有多种不同方式的折扣。

我们需要思考,付款和折扣有什么关系?

折扣的功能,怎么添加到现有功能里?

传统思考方式:折扣是在付款的时候折扣,就写在付款的逻辑里

那么难道不写在付款里?为什么不该写在付款里?是基于什么原则去思考的吗?

有,单一职责原则

image-20211006210027762

image-20211006210129424

image-20211006210145290

image-20211006210218981

抽象出限界上下文,每个限界上下文就是一个微服务

每个微服务是软件变化的一个原因,有需求变更,只需要改某个微服务

怎样将模型转换为程序设计

明天分享

支持领域驱动设计的架构设计

第三天的分享内容

1.5 单一职责原则(SRP)

软件系统中的每个元素只完成自己职责内的事,将其他的事交给别人去做。

“职责”通常理解为一个事情,与该事情相关的事都是它的职责。

一个所谓职责,其实是软件变化的一个原因。

软件质量如何衡量?高还是低?

关键:维护成本

一个需求来了,我要改3个模块的代码

一个需求来了,我要改1个模块的代码

要求我们

平时,就要整理代码。

把同一个原因的代码放一起。

不同原因的代码分开放

image-20211006205845163

现在要增加折扣,问自己两个问题:

付款变了,要不要变折扣?不要

折扣发生变更,付款要不要变?要

折扣是付款的另一个变化原因,不应该把折扣放进付款里

1.6 领域驱动设计与传统软件开发有什么不同?

项目不变更,不需要添加新功能,不会越来越复杂,就不需要使用DDD

新项目里用领域驱动设计,是为了日后需求变更,维护起来更容易。

按照领域驱动设计,每次变更的时候,不是直接coding,而是把新需求,先放到领域模型里,在领域模型里设计,还原到真实世界。

领域驱动设计更适合老项目

是不是增加了工作量呢?

需要有一个支持领域驱动的底层架构

如何设计这样一个底层架构?

在第三天的课里

1.7 案例:远程智慧医疗系统

对前面的思想进行实战

image-20211006210951136

1.7.1 期初:传统的诊所管理系统

image-20211006211029507

护士接待患者,指引患者挂号,

然后由医生去看

医生建议患者去检查,做B超、验血

患者去交钱,验血

医生看报告,开药

患者交钱去药房拿药

1.7.2 统一语言与领域建模

image-20211006212048308

用客户的语言,理解业务

1.7.3 事件风暴

image-20211006212510847

image-20211006212558102

迭代会议之前,开一个事件风暴会议

image-20211006212811086

image-20211006212938651

已确诊和已开药是否是同一个事件?

image-20211006213010632

查找医生算不算一个事件?

只是个查询,不需要记录,预约了才要记录

领域事件分析全了么?还有已排班,已注册

image-20211006213213808

分析和事件相关联人和事

image-20211006213349061

预约是一个命令

触发者 是患者

相关的还有预约时间

出诊计划,哪个医生、哪个科室,什么时间出诊

梳理人和事的关系,

人和事是不是聚合

image-20211006213556589

image-20211006213612230

药品明细和处方是一个部分和整体的关系,有聚合关系,贴一个紫色便签

是不是聚合关系?

如果是聚合关系,部分的生命周期一定是在整体里

比如,处方产生了,才有药品明细,当处方删除,药品明细才会删除

1.7.4 领域建模

image-20211006214033246

image-20211006214313502

image-20211006214215846

image-20211006214413014

2 DDD实践篇:通过领域模型落地系统设计:数据库、聚合、工厂与仓库

image-20211006220303418

2.1 将领域模型转为数据库设计

表、表之间的关系

2.2 将领域模型转为程序设计

image-20211006220527857

实体、值对象(不要太纠结)

实体:每个同学,有一个编号学号,

值对象:同学、老师

image-20211006220756643

贫血模型的设计

image-20211006221030021

充血模型的设计

image-20211006221117782

充血模型的service,把订单作为一个整体,订单对象里有依赖的其他对象的,定义了他们的关系。订单service不需要处理订单和订单明细的依赖关系。

订单service只需要保存订单,也不需要关心保存到两个表

image-20211006221417059

订单和订单明细是聚合关系,订单仓库来做保存到具体的表的事情。

查询的时候,订单工厂,会补查用户、用户地址等信息

好处:

service不需要关注底层,只需要关注业务逻辑判断

是不是要为每个领域对象设计一个仓库和工厂?

框架

2.3 聚合

image-20211006222050553

对象和对象之间有整体和不分关系的时候,只操作整体

订单和订单明细是整体与部分的关系。聚合关系

查询和保存操作都是对订单,订单明细的查询和保存操作在订单里完成

订单就是聚合根

2.4 工厂(Factory)/仓库(Repository)

image-20211006222527714

2.5 领域驱动设计的分层架构

image-20211006222620189

dao变成了仓库,做了更具体的事情

2.6 互联网转型:远程接诊平台

image-20211006222804928

2.7 互联网转型带来的阵痛

image-20211006222846608

2.8 系统规划与架构设计

image-20211006223009698

互联网转型后的领域建模设计

image-20211006223053780

image-20211006223316190

image-20211006223443525

image-20211006223636714

image-20211006223831853

image-20211006223847585

image-20211006224114736

image-20211006224957236

CQRS命令查询指责分离

image-20211006225216710

image-20211006225420095

image-20211006225500612

image-20211006225808784

2.9 四色建模法

image-20211006225922971

image-20211006230043182

image-20211006230218464

image-20211006230338555

image-20211006230442460

image-20211006232007375

image-20211006232037931

通过适配器,解耦业务代码和技术架构

image-20211006234343393

image-20211006234704540

image-20211006234814257

image-20211006234839997

image-20211006234925225

image-20211006235207792

image-20211006235254381

image-20211006235423299

image-20211006235458621

image-20211006235701389

image-20211006235740810

image-20211007000014674

image-20211007000038638

image-20211007000054822

image-20211007000203707

image-20211007000237235

image-20211007000247415

image-20211007003812957

image-20211007005122362

image-20211007005237941

image-20211007005416460

image-20211007005508960

image-20211007005718782

image-20211007010113105

image-20211007010249353

总结

  • 领域驱动设计适合大型复杂项目,不适合初始项目

  • 领域驱动设计适合频繁需求变更的项目

  • 通过领域建模,划分限界上下文,根据限界上下文拆分微服务

  • 有新需求或者需求变更,先将需求放在领域模型内讨论设计,再将领域模型转为数据库设计和程序设计

  • 领域驱动设计时,可以采取事件风暴会议形式,和领域专家一起,使用统一语言讨论,一起设计领域对象和关系

  • 事件风暴会议需要确定一些事情:分析有哪些领域事件、确定有哪些和领域事件相关的人和事、确定触发事件的命令、确定领域事件关联的人和事有没有聚合关系

  • 将领域模型分配到各个限界上下文中,构建上下文地图

  • 新需求的代码写在哪个模块里,采用单一职责原则作为指导

  • 单一职责中的职责是指引起软件变化的一个原因

  • 平时,就要整理代码,把同一个原因的代码放一起,不同原因的代码分开放

  • 软件退化的根源不是需求变更,而是在需求变更时,没有及时调整代码,没有正确的调整代码

  • 贫血模型设计的代码,领域对象只有状态,service层包含各种行为,service层很重

  • 充血模型设计的代码,更符合面向对象,领域对象不仅封装了状态,还有各种修改状态的行为,service层更轻,service层只处理业务逻辑的封装、事务、权限

posted @ 2021-10-09 10:23  元宝爸爸  阅读(770)  评论(0编辑  收藏  举报