DDD 领域驱动

DDD 领域驱动

一、开篇

范钢:架构设计,软件重构,微服务,大数据

Get了思想确没有按照它的方法实践用起来:因为工作要求快速迭代
DDD是软件核心复杂性的应对之道

案例
案例

根源:软件的业务由简单向复杂转变的必然结果

解决:重构,解耦,拆分,再增加新需求=》但是,1,2次的需求还能看到,10次以后呢?设计迷失方向 怎么办?

1. 阻止软件退化的钥匙就是DDD

当业务越来越复杂的时候
运用DDD将对业务的理解绘制成领域模型,根据业务变更导致领域模型变更,指导软件开发

微服务:小(解耦内聚)而专(DDD)
小团队一人专负责一个小微服务

用DDD需要一个支持DDD和微服务的技术中台,这样研发团队可以专心放在业务理解上。

领域模型关心分层
领域模型关心分层

1-1. 单Controller,通用仓库,通用工厂,支持DDD+微服务的技术中台架构设计。

研发和架构痛点
研发和架构痛点

本课综合了重构,高质量软件设计和微服务,讲DDD的实践落实到指导开发团队从现有状态逐步转型为领域驱动设计和微服务架构。

本课5大模块
本课5大模块

模块5用两个实战案例演练,在微服务+人工智能,嵌入式+物联网的新项目中,如何运用DDD进行业务建模,系统规划与设计实践过程。

二、DDD杜绝软件退化

软件设计质量最高就是第一次设计的版本,该版本上线后后学各种需求变更打乱原有设计,导致软件一步步退化,哪有能力做新的设计。

退化的案例
退化的案例

软件要做成什么样?由客观世界决定而非用户,软件即世界的映射。

软件的业务逻辑越来越接近真实世界,软件就越来越专业;同时随着业务越来越复杂,软件规模越来越大。

如果软件解耦而非在原有payof上塞代码就可以持续从简单需求变更到复杂而不退化
如果软件解耦而非在原有payof上塞代码就可以持续从简单需求变更到复杂而不退化

保证开放封闭原则:两顶帽子:在不添加新功能的前提下,重构代码,调整原有程序结构,以适应新功能实现新功能

改造案例
改造案例

每一种“灵活设计”只能应对一种需求变更,不能真正解决未来变更的问题,被称为“”过度设计"。

关键:每次需求变更的设计,只有保证每次需求变更时做出正确的设计,才能保证软件以一种良性循环的方式不断维护下去。

比较困难是第一步:不增功能,如何设计重构代码,以适应新功能;一次适应,两次适应还好,单第十次能?就会又回到以前?

真实世界的映射,将变更还原到真实世界根据世界变更转成软件变更。

世界和软件映射领域模型
世界和软件映射领域模型

三、以电商支付功能演练DDD

领域模型图
领域模型图

1. 第一次变更-折扣

第一次需求变更折扣
第一次需求变更折扣

领域模型思考付款和折扣的关系?单一职责原则

一个职责就是软件变化的一个原因!!

怎样才能在每次变更的时候只修改一个模块就能实现新需求呢?

  • 需要平时不断的整理代码,将因同一个原因而变更的代码放在一起,不同原因而变更的代码放在不同的模块,不同的类中。

1-1. 折扣和付款的需求变更是否是同一个原因

  • 当付款变更时,折扣是不是一定要变?
  • 当折扣变更时,付款是不是一定要变?
  • 当限时折扣发生变更时,限量折扣是不是一定要变
  • 当限量折扣发生变更时,某类商品的折扣是不是一定要变?

1-1-1. 综上领域模型变更为

新增折扣需求的领域模型
新增折扣需求的领域模型

1-1-1-1. 基于领域模型+设计模式的类图设计

领域模型和设计模式
领域模型和设计模式

2. 第二次变更-vip会员

第二次变更需求功能
第二次变更需求功能

  • 用户变更,vip会员是否要变:用户要做的是注册,变更,注销

  • vip会员变更时,用户是否要变:会员要做的是会员折扣,会员福利,会员特权

  • 付款与会员关系--在付款的过程中去调用会员折扣,福利和特权。

vip会员变更领域模型
vip会员变更领域模型

3. 第三次变更-增加支付方式

支付方式变更领域模型
支付方式变更领域模型

为了减少接入第三方系统,第三方变更对我们的影响:加入适配器模式

支付宝出问题不影响微信
支付宝出问题不影响微信

4. 总结

  • 演练了如何运用DDD进行软件设计及变更
  • 设计变更过程中如何思考,评估代码,实现高质量

四、如何落地数据库设计

早期设计流程
早期设计流程

数据库设计-只能描述数据结构,而不能描述系统对这些数据结构的处理。

面向对象设计流程
面向对象设计流程

领域模型转换关系库和非关系库表设计的方案
领域模型转换关系库和非关系库表设计的方案

1. 总结

从DDD落实到数据库设计过程:

  • 传统的4中关系可以直接转换
  • 继承关系有3中设计方案
  • 转换成NoSql数据库是完全不同的思路。

五、如何指导程序设计

应当怎样映射,让领域模型指导程序设计呢?

  • 服务
  • 实体
  • 值对象

领域对象转程序的两种设计
领域对象转程序的两种设计

用到充血模型的封装特性的4中场景
用到充血模型的封装特性的4中场景

1. 总结

领域模型的最终落地三种类型:服务,实体,与值对象
设计思路:贫血,充血模型。

六、聚合,仓库,工厂分不清楚?

聚合关系和仓库
聚合关系和仓库

装载-通过主键ID去查询某条记录。

1. 仓库的聚合设计实现过程

  1. 订单仓库在查询订单时,只是简单地查询订单表
  2. 查询到该订单后,将其封装在订单对象中,通过查询补填用户对象,订单明细对象
  3. 通过补填后,会得到一个用户对象,多个订单明细对象需要将他们装配到订单对象中。

这事装配的工作交给另外一个组件实现DDD工厂

在设计模式中,将被调用方设计成一个接口下的多个实现,将这些实现放入工厂中,工厂负责通过key值找到对应的实现类,创建出来,返回给调用方,从而降低了调用方与被调防的耦合度。

DDD的工厂共同点是都要创建对象,DDD工厂是通过装配创建领域对象,是领域对象的起点。

装配工厂说明
装配工厂说明

七、限界上下文:冲破威武福设计困局

订单模型只是整个系统的一部分思考:

  • 怎么对整个电商网站进行领域驱动设计?
  • 怎么通过领域驱动来设计整个系统?
  • 怎么绘制领域模型呢?

1. 问题域和限界上下文

将整个系统划分成许多相对独立的业务场景,在一个个业务场景中进行领域分析与建模,这样的业务场景称为子域。
领域驱动的核心思想--将对软件的分析与设计还原到真实世界中,真实世界的业务和问题叫做“问题域”业务规则和知识叫“业务领域知识”。

1-1. 问题域

  • 电商网站的问题域---人们如何进行在线购物,购物的流程怎么样的?
  • 在线订单系统的问题域--人们如何在线订餐,饭店如何在线接单,系统如何派送骑士配送?

1-1-1. 子域

  • 电商网站-用户选购,下单,支付,物流等多个子域
  • 在线订餐--用户下单,饭店接单,骑士配送等子域

1-2. 限界上下文

一个复杂系统的领域驱动设计,就是以子域为中心进行领域建模,绘制出一张一张的领域模型设计,称之为“限界上下文(context bouds)”

单一职责:每个限界上下文中实现的软件变化都是同一原因的业务。

用户下单交给下单限界文;而用户信息读取应交给用心信息管理限界文。

限界上下文之间的相互关系--上下文地图

限界上下文的高内聚:就是限界文内实现的功能都是软件变化的同一个原因的代码。

1-3. 限界文与微服务

限界文很好的将每次的需求变更快速落到某个微服务中变更,实现低成本维护和快速交付,快速适应市场变化,提升企业竞争力。

限界文的低耦合:就是指上下文地图相互调用时,通过接口调用,可以通过微服务来实现关系的低耦合。

传统-领域-微服务划分
传统-领域-微服务划分

1-4. 微服务拆分的困局

各微服务不应该直接操作数据表而应该通过根据关系调其他系统接口
各微服务不应该直接操作数据表而应该通过根据关系调其他系统接口

  1. 从DDD开始需求分析,领域建模,逐步建立起多个问题子域
  2. 讲问题子域落实到限界上下文,他们之间的关联形成上下文地图
  3. 各子域落实到微服务中贫血模型或充血模型的设计,从而再微服务之间依据上下文地图形成接口。

微服务小而专
微服务小而专

2. 总结

微服务设计困局是拆分
拆分的核心是小而专,高内聚
破解微服务困局的关键是DDD。

八、在线订餐场景如何开事件风暴会议

解决微服务拆分,实现微服务的高内聚与单一职责问题:领域驱动设计。如何进行?

  • 订餐业务流程:

用户选餐->用户下单-》饭店接单-》餐食就绪-》骑士配送-》订单送达

如何分析理解需求,通过领域驱动拆分微服务,把系统拆分出来?

1. 统一语言建模

目前需求对接的问题
目前需求对接的问题

软件开发伤:客户不能理解技术实现方案能力,技术不能理解客户的领域知识;从而沟通中客户以为技术做到了,技术以为理解了客户需求。

  1. 我主动学习你的语言,了解你的业务领域知识,并用你的语言与你沟通

    • 如何了解业务?
      从客户那里去学习,注意捕获客户在描述业务过程中那些专业术语,努力学会用这些专用数据与客户探讨业务。
      比如:面向中医的数据建模型业务术语
      症状-(西医)--表象(中)
      疾病(西)---症候(中)
  2. 我主动让你谅解我的语言,了解我的业务领域知识,用我的语言与你沟通。

2. 事件风暴(同一语言建模的实践)

一种基于工作法的DDD实践方法,可以快速发现业务领域中正在发生的事件,知道领域建模及程序开发。

事件即事实:即在业务领域中那些已经发生的事件就是事实(fact)

事件风暴会议图
事件风暴会议图

2-1. 事件风暴步骤

  1. 产经经理引导下,与业务专家梳理当前的业务中有哪些领域事件,因为已经发生的应该在命名时采用过去时态。
    - 用户选餐是不是领域事件:不能算领域事件,领域事件是指已经发生需要保存保存的领域事实用橙色贴纸展现出来;ddd用于系统增删改业务场景,其他查的场景用其他方式。
  2. 针对每一个领域事件,项目组成员围绕它进行业务分析,增加各种命令与事件,进而思考与之相关的资源,外部系统与时间。

如下图已下单领域事件命令使用蓝色便签任何事使用黄色便签
如下图已下单领域事件命令使用蓝色便签任何事使用黄色便签

  1. 识别模型中可能涉及的聚合及聚合根

是否聚合关系看业务含义
是否聚合关系看业务含义

订餐系统领域事件分析
订餐系统领域事件分析

3. 总结

统一语言建模-指导思想;事件风暴--实践方法。

九、ddd如何解决微服务拆分

微服务低耦合,就是执行过程中不是自己的职责,应当交给其他微服务实现,只需要调用其他接口即可。

通过DDD进行建模可以基于限界上下文实现系统设计在限界文内高内聚限界文间低耦合。

子域与限界上下文
子域与限界上下文

用户下单是主题域,而用户注册和饭店管理是支撑域。

已接单已就绪子域和限界文
已接单已就绪子域和限界文

领域事件如何通知?

事件通知
事件通知

  1. 通过事件风暴进行领域建模,
  2. 基于另有建模进行限界上下文设计

1. 设计步骤

  1. 按照限界上下文进行微服务的拆分,按照上下文地图定义各位服务之间的接口与调用关系
  2. 讲领域模型划分到多个问题子域,每个子域都有一个领域模型的设计
  3. 基于充血模型与贫血模型的设计各个微服务的业务领域层,即各自的Service,Entity与value object
  4. 按照领域模型设计各个微服务的数据库。

最终设计领域图
最终设计领域图

2. 总结

本节讲了拆解微服务的难题和解决思路,更加完美的解决方案--微服务的技术中台统一去解决。

十、DDD如何落地微服务设计实现

1. 深入理解业务与适时重构

DDD的真谛
DDD的真谛

小步快跑
小步快跑

上节讲了几个领域主题及支撑域:
下图举例用户下单建模

用户下单建模会冗余菜品名称等信息提高查询效率虽然菜品名称不在用户下单上下文中
用户下单建模会冗余菜品名称等信息提高查询效率虽然菜品名称不在用户下单上下文中

1-1. 用户下单有取消订单领域事件

取消订单需求
取消订单需求

因此用户下单上下文需要增加一个取消时间,当用户下单上下文对订单对象更新之后,饭店接单与骑士配送上下文是否也需要跟着更新呢。

取消时间对饭店接单有意义需要变更:需要用户下单领域通知事件给饭店节点。
而对骑士配送没有任何意义,不需要变更;满足了更新范围更小。

restful接口使各个服务之间的数据结构解耦,使用json数据传输即使结构不同类型不同也互不影响。

订单状态的事件与跟踪
订单状态的事件与跟踪

2. 总结

DDD真谛是领域建模,即深入理解业务。

十一、微服务落地技术实践

核心架构师的能力:

  1. 将业务转换为技术(业务理解DDD)
  2. 合理利用技术支撑业务(广知识开视野)。

多个团队提API接口如何提供接口?

  • 对接口进行规划,通过复用尽可能少的接口满足他们的需求。
  • 调用方需要接口变更时:变更现有接口应向前兼容,即接口名称和参数不变,只在内部增加新的功能。
  • 调用双方传递的值对象不用完全一致(因为restful接口json数据)!
  • 调用:同步调用OR异步调用。

1. 去中心化数据管理

根据数据量与数据访问特点,选用不同的数据的数据存储方案存储数据。

不同业务域存储管理
不同业务域存储管理

关联查询难题的涉及解决
关联查询难题的涉及解决

  • 在查询订单时,如果要通过用户姓名,联系电话进行过滤,然后再查询时,又该如何设计呢?
    提升海量数据的查询性能,适当增加冗余,即在订单表增加用户姓名,联系电话等字段。
  • 当系统要在某些查询模块进行订单查询时,可能对各个字段都需要进行过滤,利用NoSql特性,采用宽表设计。

2. 总结

基于DDD的微服务设计既强调对业务的分析理解,又强调对业务的技术落地。

  • 微服务间要通过feign接口互相调用,数据 要通过补填相关联查询
  • 还有聚合的实现,仓库和工厂的设计。

十二、解决技术改造困局:整洁架构

在DDD实现过程中,技术中台应当能够封装那些繁琐的聚合操作,仓库与工厂的世界,以及相关的各种技术。

  • 为什么技术升级那么费劲?
  • 系统设计开发时,大量的业务代码依赖于底层的技术框架,形成了耦合,比如:

dao层跟框架耦合了
dao层跟框架耦合了

解决方案加入一层接口稳定
解决方案加入一层接口稳定

bus业务service-entity-value中实现业务于基础平台解耦
bus业务service-entity-value中实现业务于基础平台解耦

整洁架构图4层说明
整洁架构图4层说明

整洁架构细化图
整洁架构细化图

在平台建设过程中,除了通过技术选型将2各种技术整合到系统中外,还应通过封装,在其上层建立接口层。

  • 支持DDD与微服务的技术中台:
    基于整洁架构的思想,将DDD底层的聚合操作,仓库和工厂的设计微服务的技术框架,以及整洁架构中的适配器,统统封装在技术中台中。

十三、如何设计支持快速交付的技术中台战略

以往建设的系统都分为:前台和后台
前台是与用户交互的UI界面,后台是服务端完成的业务逻辑操作--不准确

前后台有些共用的部分:共用的组件既包含前台的界面也包含后台的逻辑被称为中台。

中台:将以往业务系统中可以服用的前端和后端代码剥离个性,提取共性,形成的公共组件。

业务中台技术中台数据中台概念
业务中台技术中台数据中台概念

解决规模化团队与软件快速交付的矛盾。

1. 导致交付慢的两个关键问题:

  • 烟囱式开发

烟囱式开发图解
烟囱式开发图解

  • 统一发布制约

统一发布需要依赖串行制约
统一发布需要依赖串行制约

2. 解决方案

  • 特性团队开发

特性开发
特性开发

  • 特性团队大前端+技术中台发布

大前端+技术中台交付方式
大前端+技术中台交付方式

组件一个特性团队:成本高昂,每个人就要懂业务,又要懂前端,后端,大数据。运维。

解决方案:组件底层架构团队

  • 通过技术选型,构建技术中台,讲软件开发中入住UI,应用,数据库甚至大数据等诸多技术进行了封装。
  • 然后以API借口的形式开放给上层业务。

大前端:是一种职能的转变,业务开发人员更加关注业务,深刻理解业务,并快速应对市场应对业务需求的变化。

架构团队从业务开发角度进行提炼,提炼共性,保留个性,将这些共性沉淀到技术中台中,通过将DDD与微服务涉及的各个技术组件封装到技术中台中,封装各个技术细节。

3. 技术中台特性

  • 简单易用,快速便捷的技术中台
  • 易于技术架构演化:整洁架构,右边型架构。。
  • 支持领域驱动与微服务的技术架构

越少的代码bug越少,所以花更多的时间分析设计,让软件精简到极致,花更少的时间编码。

传统分层设计存在代码问题
传统分层设计存在代码问题

领域模型设计的代码处理
领域模型设计的代码处理

4. 总结

采用DDD为了理解业务,做出用户满意的产品,还需要快速交付产品,以应对瞬息变化的时长。

十四、如何实现支持快速交付的技术中台设计(普通中台)

大前端+技术中台:既可以降低业务开发工作量,提高开发速度,又可以降低技术门槛。

命令与查询分离(CQRS)模式:讲系统按照职责划分为命令(即增删改)与查询操作两个部分。

命令:用领域驱动设计
查询:用事务脚本SQL语句。

增删改的技术中台架构设计
增删改的技术中台架构设计

这样spring的controller的bean不知道是哪个service的bean;保障了安全性,实现了前后端分离,讲前端代码与后端解耦。

1. 增删改

1-1. 如何实现单Controller

前端访问同一个controller请求
前端访问同一个controller请求

{bean}是spring中的bean.id;{method}bean中需要调用到店方法,注意不支持重写,否则会调用最后一个。

  • 如果要调用的方法有值对象,必须将值对象放在方法的第一个参数上
  • 如果要调用的方法既有值对象,又有其他参数,则值对象中的属性与其他参数都放在该JSON对象中。
  • 在实际项目中需要在OrmController(本身不支持权限校验)前进行权限校验,来规范前端可以调用的方法,建议使用服务网管或filter进行校验。

1-1-1. OrmController的流程

  1. 根据前端参数bean从spring中获取service
  2. 根据前端参数method通过反射获得调用方法
  3. 通过反射获得调用方法的第一个参数作为值对象
  4. 根据反射活动的值对象的所有树型,从前端JSON中获得对应属性的值,写入值对象。
  5. 根据前端JSON获得其他参数
  6. 将值对象与其他参数,使用反射调用service中的method方法。

1-2. 如何实现单Dao

vo.xml文件
vo.xml文件

值对象中可以有很多的属性变量,但只有最终作持久化的属性变量才需要配置。

每个Service在spring中都是统一注入BasicDao

  • 如果要使用DDD的功能支持注入通用仓库Repository
  • 如果要使用Redis缓存,注入RepositoryWithCache

basicdao配置
basicdao配置

1-2-1. 特别说明

基于本框架进行的业务开发,包括spring的配置,mybatis配置,vobj的配置建议都采用xml文件的形式,而不是采用注解。

注解:让业务代码对技术框架依赖,违背了技术中台设计的初衷,这样后续换其他框架依旧可移植。

1-2-2. 流程

  1. 单Dao调用VObjFatory.getVObj(class)获得配置信息VObj
  2. 根据VObj.getTable()获得对应的表名
  3. for(Property prop:VObj.getPreperties()){
  • 通过prop.getColumn获得值对象对应的字段
    -运用反射从值对象中获得所有属性及其对应的值
  • 通过以上参数形成SQL语句。
    }
  1. 通过SQL语句进行数据库操作。

2. 查询

查询的架构设计图
查询的架构设计图

前端请求案例
前端请求案例

{bean}就是spring的service

2-1. 查询流程

  1. 从前端获得bean,page,size,count,以及查询参数
  2. 根据bean从spring中获得相应的service
  3. 从前端获得查询参数JSon,将其转换成MAP
  4. 执行service.query(MAP)
  5. 执行查询完后,以不同形式返回给前端。

2-2. 单service的设计

mybatis的mapper文件
mybatis的mapper文件

所有的查询都是同一个queryservice查询,但是注入的是不同的Dao就可以配置不同的bean,完成各自不同的查询。
为了更加简化:可以通过mybatis框架注入同一个dao,单配置不同的mapper就可以完成不同的查询。如下图:

单service
单service

2-2-1. 单service流程设计

  1. 将查询参数map,page,size传递给dao,执行查询dao.query(map)
  2. 在查询的前后增加空方法beforeQuery(),afterQuery()作为hook,当某业务需要在查询前后进行处理时,通过重载子类去实现
  3. 判断前端是否传递count,如果有则不再求和,否则调用dao.count()求和计算“第x页,共y页”
  4. 将数据打包成ResultSet对象返回。

在某些业务中,需要个性地在查询前进行某些处理,现在的设计中只有一个service,如何实现查询前后的这些处理呢?

如果继承重写afterquery不就增加了ctroller?
如果继承重写afterquery不就增加了ctroller?

如果该查询在前端展现时,需要在表格的最下方对某些字段进行汇总

并且这个汇总是对整个查询结果的汇总,则将该字段作为key值写入aggregage中。

设计一个普通的查询,只需要制作一个Mybatis的查询语句配置,在spring中制作一个bean,对于进行查询结果集的补填,可以使用通用程序AutofillQueryServiceImpl

3. 总结

  • 增删改操作时:只需要编写前端界面,service和值对象
  • 大多数情况下只需要编写Mybatis与spring的配置以完成查询功能的编写。

十五、如何设计支持DDD的技术中台

1. 领域设计的仓库-装配

DDD落地的坑就是支持DDD的技术架构设计=》即程序设计业务领域层的设计

  • 不能准确掌握DDD的分层架构
  • 程序写的非常乱,频繁在各种TDO,DO,PO进行数据转换。

一个技术中台简化程序的实现,从而减少开发的问题。

传统领域设计即当前领域设计
传统领域设计即当前领域设计

2. 领域设计的-内置聚合功能

聚合代表在真实世界中整体和部分的关系,在设计支持领域驱动的技术中台时2,硬蛋简化聚合的设计与实现。
例如:订单和订单明细存在聚合关系
在VObj.xml中建模时,通过join标签关联他们,并置join标签的isAggregation=true.

3. 总结

通过一个支持DDD的技术中台,将许多DDD繁杂的设计实现做成通用的仓库与工厂,封装在了底层的技术中台中。

查询订单不像加载明细,而加载了影响了性能,未来富客户端把更多数据放到前端,减少交互次数。

十六、如何设计支持微服务的技术中台

1. 支持DDD与微服务的技术中台的能力

  • 解决当前微服务架构的技术不确定性
  • 更加容易地将领域驱动设计应用到微服务架构中
  • 解决领域对象在仓库与工厂进行装配时,如何将本地查询替换为远程接口调用,实现微服务间数据装配的问题。

目前spring cloud框架各种组件不确定不排除以后微服务都要切换到service mesh的可能。

解决技术不确定性问题
解决技术不确定性问题

  1. 聚合层微服务在调用原子微服务时,调用自己本地的接口,再由这个解耦通过加载Feign注解,去实现远程调用
  2. 原子服务层的微服务在对外开放接口时,由统一的controller开放接口,再2由它去调用内部的service.这样各个service都是纯洁的不与spring耦合,而只有controller与spring耦合。这样以后迁移时只需要自由迁移切换service即可。

2. 支持微服务远程调用的架构设计

拆分要提现单一职责-微服务间低耦合,服务内高内聚。=》基于领域设计建业务名,然后基于限界上下文进行微服务拆分。

2-1. 垮库的操作

经过微服务拆分,订单有订单数据库,用户有用户数据库,当查询订单,需要补填 其对应表的用户信息时,调用“用户”微服务的元辰共接口,在用户数据库中查询,然后返回给“订单”微服务。

  • 通用DDD仓库在执行查询或者装载操作时,现在订单微服务的本地编写一个用户service的feign接口,通过Feign接口实现对用户微服务的远程调用。

2-1-1. 采用Feign接口实现远程调用

以往我们对微服务间的调用采用Ribbon的方式,在程序中任意一个未知注入一个restTemplate,就可以进行远程调用。

代码过于随意,会越来越难以阅读与变更维护。

解决方案:采用Feign的远程调用方式

Ribbon和Feign对比
Ribbon和Feign对比

Feign远程调用代码图
Feign远程调用代码图

3. 总结

支持DDD+微服务架构的技术中台设计思想
即实现了业务代码与技术框架解耦的整洁架构思想,又实现了领域模型在微服务间的数据装配。

十七、基于DDD的代码演示(含DDD的技术中台设计)

代码演示模块说明
代码演示模块说明

1. 对于核心里单个controller的设计实现

  1. OrmController:用于增删改操作,以及基于key值的load,get操作,通常基于DDD进行设计

service编写所有方法
service编写所有方法

还要求前端的JSON与领域对象中的属性一致

  1. QueryController:用于基于SQL语句形成的查询分析报表,查询结果会形成领域对象,并基于DDD进行数据补填;
    QueryController接受注入query/{bean}的请求,该方法既可以接受Get方法,也可以接收POST方法,并用各自的方式传递查询所需的参数;如果该查询需要分页,在传递查询参数以外,还要传递page,size,第一次查询时除了分页还会计算count并返回前端。
  2. 其他Controller用于如ExeclController等特殊的操作是继承以上两个类的功能扩展。

2. 单Dao的设计实现

以往系统设计的硬伤在于一头一尾:Controller与Dao

  1. 注解会带来代码与技术框架依赖
  2. 注解使用与1:1,n:1的场景而1:n,n:的场景往往非常麻烦。

因此使用原始xml可以避免这些问题

xml对dao的配置说明
xml对dao的配置说明

entity与VObj.xml
entity与VObj.xml

VObj.xml就是mybatis语法底层就是它。

十八、基于DDD的微服务设计演示(含支持微服务的DDD技术中台设计)

1. 单Service实现数据查询

用单service注入不同的dao实现各种不同的查询;
大数据相关产品:运用大数据技术对海量的数据进行分析处理,并且最终的结果是通过各种报表来查询并且展现。

每个报表在Service中断代码基本相同,从前端获取查询参数,然后调用Dao执行查询,最多再做一些翻页的操作。

  • 以往每个Dao都要写一个自己的接口,然后配置一个Mapper,但Dao接口都长的一模一样,只是接口名与Mapper不同
  • 过去的设计,每个Service都对应一个Dao,现在一个Service要对应很多个Dao.

技术中台xml配置关系
技术中台xml配置关系

补填就配置AutofillQueryServiceImpl,不补填就配置QueryServiceImpl.

如果配置AutofillQueryServiceImpl,除了配置queryDao,还要配置basicDao=》这样对数据不拆分可以,如果是微服务拆分了数据库,就需要这个配置远程也可以。

当product微服务要补填Supplier时,只能远程调用Supplier微服务的响应接口,因为微服务对数据库也做拆分了,数据补填必须要技术中台提供对微服务的响应支持

技术中台补填远程为服务接口查询支持把原来的supplier改成ref
技术中台补填远程为服务接口查询支持把原来的supplier改成ref

1-1. Supplier微服务提供2个查询接口

  • Supplier loadSupplier(id) 通过id进行查找
  • List loadSupplier(List ids) 通过多个ID进行批量查找

就可以实现通过fegin微服务的远程调用,补填相关数据。这样product的vobj就不用配置supplier而只需要配置ref

2. 通用仓库和工厂的设计

DDD架构设计增加了仓库和工厂,除了读写数据库以外,还要实现对领域对象的映射与装配。

传统的DDD设计每个模块都有自己的仓库和工厂,创建后放到仓库的缓存,供上层应用访问,当领域对象经过一系列操作后,通过仓库完成持久化:普通领域对象就是存单表,如果存在聚会关系的领域对象,持久化要到多库多张表并放在同一事务上

聚合关系会存在垮库操作吗?即存在聚合关系的多个领域对象会被拆分成多个微服务吗?不可能!因为聚合是强相关的,如果拆分了,要么微服务拆分有问题,要么不是聚合关系,因此仓库不能实现垮库处理。

2-1. 所以DDD设计仓库和工厂

  1. 完成仓库和工厂对Dao的替换,减少Dao层仓库和工厂数量
  2. 扩展许多其他功能实现聚合关系的微服务拆分能力:比如数据的补填,领域对象的映射和装配,聚合的处理。

仓库和工厂改造类图
仓库和工厂改造类图

3. 总结

一个可以落地的技术中台:它摒弃了一些非常繁琐的TDO,PO,仓库与工厂的设计,而是将其封装在了底层技术框架中。

十九、基于事件溯源的设计开发

1. 回顾

基于DDD的设计研发思路:

  1. 如何实现聚合
  2. 如何设计仓库
  3. 如何将领域对象与数据库映射
  4. 基于DDD与微服务的技术架构

事件即事实:过去式,一个信息化管理系统的作用就是存储这些事实,对这些事实进行管理和跟踪,进而起到提高工作效率的作用。

2. 事件风暴的精髓

准确地抓住业务进行过程中需要存储的关键事实,并围绕着这些事实进行分析设计,领域建模。

如案例事件溯源的设计思路就是事件通知
如案例事件溯源的设计思路就是事件通知

3. 事件溯源

将过去耦合在一起的业务流程有效的解耦,让复杂的业务系统能松耦合的拆分为一个个独立的组件,实现组件式设计开发与插拔式的业务变更。

领域事件的发布和订阅关系
领域事件的发布和订阅关系

事件风暴中定义的每个事件,是不是都需要发布领域事件呢?

最好的做法是将每个领域事件都予以发布;未来变更也不怕。

业务系统的发布者只负责事件的发布,订阅者只负责事件的后续操作。

事件的发布方再发布事件的同时,需要在数据库中予以记录。

SpringCloudStream 是springcloud技术框架中一个实现消息驱动的技术框架,底层支持RMQ,Kafka等主流消息队列。

消息发布订阅代码案例
消息发布订阅代码案例

4. 总结

事件溯源解耦了领域事件的上下游。
事件上又有负责执行publish发布事件,事件的下游负责各自去定义各自的apply()方法。

二十、远程指挥医疗平台(微服务+人工智能)设计过程

DDD如何从战略层面规划一个智能系统的建设。

DDD与传统的OOA/D有什么区别?
DDD是领域建模的思想,将业务分析与技术实现予以分离,分而治之。
DDD在复杂业务和技术下分为:

  • 战略设计:
    1.通过限界上下文从全局的角度规划整个系统的业务模块
    2. 逐步细化,对每个模块开展事件风暴会议,进行领域建模
    3. 逐步落实到每个模块的数据库设计与微服务设计以及需要涉及2的分布式技术与云端部署。
  • 战术设计

1. 首要的需求

建设一个只会诊疗数据模型,通过人工智能与数据建模,入住医生进行诊断治疗。

基于业务的战略规划
基于业务的战略规划

首要的服务对象是各地的医院和诊所。

  • 在诊所接诊的患者在诊所中缴费取药
  • 在App接诊的患者通过健康购物网站远程进行配送

2. 系统拆分

系统拆分
系统拆分

3. 限界上下文领域建模

  1. 独立性:按照业务独立性将系统划分为不同模块,分配给多个团队独立开发
  2. 协作:单一职责,各自负责各自的,通过接口并行前进。

共同制定计划:事件风暴&PI计划会议

每个团队独立召开事件风暴会议,可以和敏捷开发相结合。

  • 通过建模,分析清楚自己的领域模型
  • 通过主题域和支撑域的分析,清楚需要哪些团队提供什么样的接口支持。

有项目经理拉起大家把各团队分析结果组织在一起,各团队会根据你提的接口需求,跟你制定接口规范,并纳入到他们的story里。

把领域驱动和敏捷开发结合在一起,使得各团队保持独立性还能相互协作组成一个有机整体;
有了前面一系列规划协调,整个系统分为哪些模块,交给哪些团队,相互之间如何解耦以及各自的迭代计划都制定出来了,这样每个团队就可以独立的开展各自的工作。

4. 基于领域的战术设计

智慧医疗数据模型团队:通过统一语言建模,用更专业的中医词汇来表述业务需求,分别形成用例模型,用例描述与领域模型。

  1. 用例模型
    问诊业务模块的用例模型

领域建模有很多方法:事件风暴,四色建模,人文分析
2. 领域建模
诊断业务四色建模

  • 粉红色:时间原型,某个时间发生的某件重要的事件是领域建模的核心
  • 绿色:围绕着时间有很多相关的参与者-地点-物品原型(简称PPT Party/place/thing)
  • 黄色:在此次事件中参与的角色原型
  • 蓝色:是对PPT原型的补充说明

针对场景中的所有领域对象按照限界上下文划分为诊断模型:主题域;表象模型,症候模型都是支撑域。
3. 技术实现
最后把以上领域模型(主题,支撑)落实到技术实现和编码

技术实现分解
技术实现分解

技术实现拆分后各部分实现方案
技术实现拆分后各部分实现方案

5. 总结

DDD设计核心是分而治之

  1. 通过业务建模将业务和技术分离,设计变得清晰而做出正确设计
  2. 通过限界上下文划分,是的系统复杂性下降,风险变得可控
  3. 通过与规模化敏捷的结合使得团队协作变得顺畅。

二十一、智能温控系统(嵌入式+物联网)的DDD设计过程

DDD在嵌入式研发的领域,嵌入式系统向物联网领域发展,DDD扮演的作用

1. DDD的实践:

  1. 实践风暴法的领域事件,四色建模法的时间原型共同特点都是以带有时间属性的活动作为中心。

  2. 很多软件更多的是描述真实世界中对象与对象的关系(而不是时间属性的活动),可以采用需求讨论中建模与原型分析法

需求讨论中建模:是我们与业务专家在探讨业务需求过程中,一边理解业务需求,一边绘制的草图。

沟通需求过程建模虽然客户没说控制器,还是要有把传感器,显示器,加热降温等串联起来
沟通需求过程建模虽然客户没说控制器,还是要有把传感器,显示器,加热降温等串联起来

基于需求更细化的系统类图不同的HMI客户端不同的传感器类型
基于需求更细化的系统类图不同的HMI客户端不同的传感器类型

在一个标准接口的基础上有各种各样的不同实现

对领域模型进行限界上下文分析
对领域模型进行限界上下文分析

加热上下文主题域支撑域分析
加热上下文主题域支撑域分析

  1. 原文分析法建模
    在用例建模之后,在对每个用例进行描述的基础上,将用例描述的事件流按照名词与动词进行逐一分析的领域建模实践方法。

用例图
用例图

用例描述图
用例描述图

  • 原文分析法与需求建模法结合使用:
    先用原文分析法形成一个初步的模型,然后与客户去探讨需求,客户看着这个模型,给我们提供更多的细节,让需求的探讨更加深入。

1-1. 目前的设计还不能提现智能温度控制:客户提出新需求

  • 当房间内没人时:温控器停止工作降低能耗
  • 检查到房间有人时,自动启动温度控制。

拿到新需求后如何进行设计:

  1. 首先在用例模型,用例描述中进行变更

用例描述变更
用例描述变更

  1. 在用例模型基础上进行领域模型变更

领域模型变更
领域模型变更

2. 向物联网的转型

在以上功能设计基础上,用户还希望提供更多 智能的设计。
单个房间的温控器已经不能满足客户需求,需要将各个房间的温控管理进行联网。

物联网的发展将冲击嵌入式产业,所有的电子产品需要连接到云端实现互联互通。

温控系统向云端转型,但不能规划太远,不能脱离现实。

随着变化越来越快,已经无法预测未来几年的变化,最好采用意图架构:
根据近期需求变化的意图,规划近期的技术架构,使其刚刚满足近期的需求,未来,当需求变化时,再根据未来的变更意图,去规划未来一段时间的技术架构。

  1. 以建筑为单位制订集中式管理的技术方案
    1. 宏观地展现个房间的总体能耗
    2. 发现那些需要频繁加热与制冷的房间,是否增加保暖措施以降低能耗
  2. 将温控系统的数据与其他更多的数据进行比对,做更多的职能应用:比如在主人回家的时候提前进行温控,让主人到家更加舒适
  3. 从更大的范围去规划温控系统的云端部署提供更多的智能应用。

DDD不仅应用在信息化管理中,还可以用在其他软件开发中,在不同的系统中有不同的实践方法。

3. 总结

  1. 事件风暴法
  2. 四色建模法
  3. 需求讨论建模
  4. 原文分析法
    DDD让我们再技术变化的发展中更准确的把握市场,用户需求,在竞争中把握先机。

二十二、结束语

1. DDD的难是落地

  • 本文从战略战术的不同层面讲解了很多案例,以及DDD如何开展。
  • 从实战的角度,讲解如何通过DDD+微服务架构解决软件发展规模化问题
  • 通过DDD进行规模化的业务分析,通过微服务进行规模化的设计开发,通过规模化的敏捷进行团队组织。

2. 研发团队需要改变进行DDD落地实现:

业务领域建模,统一语言建模
业务需求是研发的最大风险,风险核心是研发人员不理解业务。

研发与业务的敏捷减少交付期限
研发与业务的敏捷减少交付期限

统一语言建模+4大实践方法能够让研发更快深刻理解业务。

准确理解DDD中的概念:DDD应对的是软件复杂性,通过领域建模去抽象复杂业务,让复杂业务得到简化,从而简化软件的设计。

3. 正确的设计:理解一下概念进行DDD合理的设计

  1. 聚合,仓库,工厂,领域事件
  2. 在DD的设计实践中,也应当与微服务,分布式,大数据等当今主流技术相结合,将DDD的设计理念落实到技术上。
  3. 将DDD融入敏捷开发中:敏捷开发的落地对开发团队的设计能力,设计质量,提出了每2~3周的敏捷周期,都是对上一个版本的更新,如果设计质量跟不上,一直迭代,后续敏捷就起不来了。

团队在敏捷过程中应将DDD融入其中,在每次迭代前进行业务建模,基于领域模型设计变更,快速迭代

3-1. DDD的架构

基于业务领域建模的设计思想如何落地到软件的架构设计上呢?
不好地方:很多团队落地DDD不知怎么架构?好的地方:但提供了广阔的发挥空间,在DDD思想下提出来各种架构设计思路。包括整洁架构,右边型架构,作者的单Controller,单Dao,通用工厂和仓库的设计。

posted @ 2022-07-01 12:31  编程未来  阅读(388)  评论(0编辑  收藏  举报