DDD实战之三:整体工作框架和全局需求分析(上)

简介: DDD实战之三:整体工作框架和全局需求分析(上)

从今天开始,我就要进入“群买菜”系统真正的 DDD 设计和实现过程了。这里面会涉及到一些 DDD 相关的概念和理论,但更多的是设计和代码实践。


正如第一篇“缘起”中提到的,本专题将尽可能的将理论知识进行浓缩和简化、糅合到实际的设计文档和代码实现中去表达。


DDD 本质上是个“软件设计”方法论,它指导的是“怎么做软件设计”。故在正式开始“群买菜”的软件设计之前,我们先对 DDD 整体方法论(这里主要是张逸老师的 DDDUP)做一个简单的、从我个人角度理解的介绍。


本篇在对 DDD 整体工作框架做了个简要的介绍后,我会在本篇中完成 DDD 工作框架中的第一步——“群买菜”系统的全局分析。


01DDD整体工作框架


在我个人的理解中,DDD 工作框架其实就只有两个部分:对“问题空间”的分析和描述、以及通过一系列方法实现的“解空间”的映射。“问题空间”其实就是在目标软件系统上线前、当下的现实业务世界,“解空间”就是目标软件系统的设计方案和代码实现。


1


“问题空间”分析:全局分析


对“问题空间”进行分析和描述,就是“问题空间”的所有工作内容。它指的是要搞明白我们的软件“到底要解决什么问题、以及满足什么业务需求”——说得更直白一点,就是传统上大家理解的“业务需求分析”。不过,我们在 DDDUP 方法体系里,不叫“需求分析”而叫“全局分析”。


首先,我们需要了解 DDD 中反复提到的“业务领域”和“业务子领域”的概念,并将“业务子域”区分出 3 个类别:核心子域、通用子域、支撑子域。


所谓的“业务领域”,指的就是“业务领域模型”。因为 DDD 是“领域驱动设计”,所以“业务领域模型”是 DDD 的核心概念来源。而”业务领域“就是所开发软件系统需要对应的现实世界的某个“业务范畴”,比如:电商、物流、通信、交通、医疗、教育等等。


在“业务领域”下,我们可以将其业务内容分为多个“子领域”,比如:“电商”业务领域下,有“客户”、“商品”、“库存”等等多个子领域。


需要注意的是:“业务子领域”的划分其实是不考虑将来软件系统会怎么建设的,而更多是考虑现实业务中需要怎么区分这些业务单元。当然,有可能系统实际实现后,系统的模块也会有跟“业务子领域”一样的划分——但这只是“系统同构化映射现实业务”,并不是系统模块划分在影响业务单元划分。也就是说:“业务子领域”划分是“因”、“软件模块”划分是果。甚至有可能根据实际项目的现实条件,软件系统并没有按照“业务子领域”进行分块。比如:重用一个既有的整体的 SAP ERP 系统来实现“采购”、“库存”、“财务”多个“业务子域”的支撑。


“业务子领域”按照如下的识别规则划分为 3 类:核心子域、通用子域、支撑子域。


“通用子域”就是那些无论在哪个行业、哪个业务领域下都可以通用的“业务子域”,比如:角色权限、组织管理、员工、IM 即时消息等等。这些“通用子域”所对应的软件实现,很多时候是可以直接在市面上采购成熟产品来实现的,不见得非要本项目的软件团队来实现。


“支撑子域”是那些虽然有本行业、本业务领域下特定的业务知识,但并不是本业务领域下最核心的业务单元。比如:“电商”业务领域下的“库存”、“物流”、“财务”就不是核心业务单元,而只是支撑“订单”、“客户”等核心业务单元的、后台人员所使用的与直接客户无关的业务单元。


“核心子域”就是那些体现本“业务领域”最核心业务价值的业务单元。当然,这其实是个“相对概念”,是在特定语境下才有效的说法。比如:在“电商”业务领域中,“物流”就是个“支撑子域”;但在“快递”业务领域中,“物流”就是个“核心子域”。


其次,我们大致了解下 DDDUP 对“全局分析”要求的工作内容(这里您不必全部记住,只是大致浏览即可。我在本节的“群买菜”全局分析中会给您演示怎么做):


价值需求分析——对目标软件系统的价值需求进行调研总结;


核心业务流程分析——对目标软件系统将要覆盖的、体现最为“核心商业价值”的业务主流程进行调研分析,画出流程图;


业务场景识别——识别出按照时间轴的跨度,业务主流程有哪些在时间线上“断裂”的业务场景;


业务流程分析——对每个业务场景下的业务流程进行识别,并用跨角色的泳道图、业务服务蓝图、事件风暴图等工具(如果您不明白这些工具是啥,暂时不管它)表达出各场景下的、各情况的业务流程;


业务用例识别——在泳道图、业务服务蓝图、事件风暴图等工具表达的业务流程基础上,识别出系统将要满足的“业务用例”。没错,就是 UML 的那个“业务用例”;


编制业务用例规格书——就是每个业务用例给一个需求规格书(按照规定的模板格式)描述文档;


2


“解空间”映射:战略设计+战术设计+代码实现


首先,我们同样需要了解 DDD 中提到的关于设计的最重要几个概念:


战略设计层面的 2 个重要概念:限界上下文、上下文映射。


限界上下文就是目标系统内部最粗粒度的模块划分,这个粒度的模块划分往往是后续“微服务”怎么切分的主要依据。


这种限界上下文的划分,最理想的情况就是一个“业务子域”对应一个“限界上下文”(所谓“同构映射”)。但在实际软件系统设计中,可能因为“第三方伴生系统”、“遗留系统”等情况的存在,而不得不出现“错配”的情况,就如下图所示——也就是说:只有所有“业务子域”对应的业务逻辑都全部由目标软件系统“全新实现”才可能出现“最理想情况”。


image.png


限界上下文映射就是搞清楚这些“限界上下文”模块之间是怎样的协作关系(调用关系、事件通知关系等等)。


“限界上下文”的划分,最重要的就是追求的“高内聚、低耦合”。而这其实是个“无止境”的追求,并且不同的架构设计师、其经验和对业务的理解不同,自然设计成果也不会完全相同,所以严格来说这是一种“很艺术化”的设计成果。


但无论怎样,DDD 战略设计中有一系列的方法和原则来帮助达到您尽可能地达到“高内聚、低耦合”的设计效果,我们会在后面的实例演示中逐步看到。


战术设计层面的 4 个重要概念:实体对象、值对象、聚合、领域服务。


实体对象是对象模型中最主要的类,需要数据生命周期管理的、根据 ID 标识而不是属性来判断是否同一个对象的类。如:订单、订单行等。


值对象是不需要数据生命周期管理的(它们往往用来描述实体对象的属性)、只要其对象内的属性取值发生变化该对象就被认为是另一个对象。如:地理区域(含省市区)、家庭住址(含经纬度定位和详细地址)、身份证号(含编码规则)、姓名(含姓+名)等。


聚合和聚合根。在实体对象中,有些实体对象是不需要单独出现的、总是跟着另一个实体对象的出现而出现、消亡而消亡的,如:”订单行”总是随着“订单”的出现而出现、消亡而消亡。往往,我们在完成“对象模型和关系识别”后,列出了很多实体对象,这些对象可按照它们之间的“绑定存亡关系”进行分组,分组后有一个实体对象是唯一的访问入口。这种分组就叫“聚合”,而那个作为唯一的访问入口的实体对象就是“聚合根”——一般情况下,给“聚合”的命名就是“聚合根”对象的名称,如:“订单”聚合可能就包含了“订单”、“子订单”、“订单行”、“商品快照”等多个对象(后面的对象都不需要单独出现)。需要说明的是:值对象是“附庸”在实体对象上出现的。


领域服务。实体对象、值对象都是有行为的(也就是方法逻辑),很多业务逻辑就直接在这两类对象中实现了。那么,有了“聚合”就可以将很多业务逻辑在“聚合”内部的各个实体对象、以及伴随的值对象的方法逻辑中实现。但是,仍然有一些不能明确划分到单个聚合”职责“中去实现的业务逻辑,而这些逻辑就需要在“领域服务”中去实现。比如:“为当前登录用户创建订单”这一业务逻辑里面,既包含要根据当前登录用户的 ID 找到对应的客户资料,还包含根据请求输入信息创建订单记录。那这些逻辑,到底应该是由“客户”聚合中实现呢?还是在“订单”聚合中实现?显然,无论放在“客户”还是“订单”聚合中,都是不合适的,所以就有了“为当前登录用户创建订单”这一领域服务存在的必要性。


其次,我们在了解 DDD 战略、战术设计的几个重要概念后,就可以来看看战略设计、战术设计分别包含什么工作内容:


战略设计主要完成如下 3 方面工作:


系统上下文定义——将目标系统与伴生系统进行职责分工。


限界上下文识别及其对应的上下文映射。


战略层面的技术决策——主要包括:

  • “系统整体”角度的软件分层(区分边缘层、业务价值层、基础层);
  • 系统整体角度的架构性决策:微服务的划分、事务一致性策略(全局一致性 vs 最终一致性)、数据库架构(单体数据库 vs 分布式数据库);
  • 必要的各限界上下文的技术栈选择(开发语言、技术组件、使用规则引擎考量、使用 CQRS/命令总线考量、事件消息机制等);


战术设计主要完成 4 方面工作:


对象模型的概念分析——识别出有多少实体对象、值对象。


聚合设计——将实体对象进行聚合分组,选出每个聚合的“聚合根”。


服务设计——设计出“领域服务”,并在“领域服务”的基础上设计“应用服务”。这两者的区别,以及如何设计在后面的实例中会介绍。


战术层面的技术决策——包括但不限于:

  • 如何在前端界面和后端服务之间传输数据(VO:view object)
  • 微服务远程服务调用时如何传输数据(DTO:data transfer object)
  • 使用开发语言的哪个持久化框架实现数据持久保存(如 JPA/Mybatis 等)
  • 如何实现资源库端口等。


好了,我们已经对 DDD 整体的工作框架做了一个特别简短的介绍,下面完成本篇文章的正题——群买菜全局分析。


02群买菜系统全局分析


1业务背景和价值需求分析


说实在的,我写这款“群买菜”小程序,是发现很多生鲜店老板或团长在使用“群接龙”这款小程序。而实际上“群接龙”并不能很好的满足生鲜行业的一些特定行业特性——如:活鲜类商品的多退少补等。


所以,“群买菜”其实是一款给生鲜社区店或社区团长提供的,允许消费者在线下单、接龙以及店主或团长进行后台店铺、商品、订单等管理的小程序。描述一个软件产品商业价值的最好方式是“商业模式画布”,下图就是“群买菜”小程序的商业模式画布:


image.png


通过该“商业模式画布”,我们能够看出我们开发“群买菜”小程序,其主要的用户就两类:消费者(生鲜客户)、商家(生鲜店老板、或社区团长),主要商业价值是通过满足“消费者”和“商家”之间的“在线交易”来实现的。所以,“群买菜”系统涉及到的利益干系人(stake holder)包括 3 个:

  • 消费者(生鲜客户)
  • 商家(生鲜店老板、或社区团长)
  • 平台运营方(群买菜软件的提供方、运营方)


基于这 3 个利益干系人,我们分析各方的“利益诉求”,得出如下的“价值需求规格书”:


image.png


2产品需求输入和统一语言


首先,我们来看看“群买菜”系统的需求输入——产品原型设计。


在 DDD 社群中,大家争论和激烈的一个话题就是:“产品经理”在 DDD 设计中到底起到什么作用,还需要“产品经理”这个角色存在吗?


这里我分享下我个人的观点:DDD 作为软件设计方法,解决的是“工程性问题”;而产品经理作为产品直接负责人,是要从商业利益、用户心理、公司资源等多个方面进行综合考虑的,他在产品设计中所完成的“原型设计”作为产品设计中最重要的成果之一,是在将关于“非工程性问题”的思考结果表达出来。


所以,我在这里秉承的观点是:产品经理输出的产品设计,作为 DDD 全局分析的输入,而不是用 DDD 全局分析代替产品经理的工作。否则,这在公司“商业职责”的定位上就很不清晰了。


于是,我这里“群买菜”系统的“全局分析”是在“群买菜”产品原型设计的基础上进行的。“群买菜”产品原型的部分设计截图如下:


image.png


由于产品原型界面很多,这里就不一一介绍了,有兴趣的您可以到这里去在线浏览:《群买菜产品原型设计》(https://xie.infoq.cn/link?target=https%3A%2F%2Fwww.stardata.top%2Fstarshop%2520mini-app%2Findex.html

接下来,在开始后面的分析设计之前,我们需要完成 DDD 设计方法论中特别重要的一步工作:制定统一语言(UL)。我个人的理解,所谓“统一语言”其实就是一个“术语表”。在大型项目中这样的“术语表”特别重要,一种比较务实的做法是:不要为所有“业务子域”制定一个“大而全”的术语表,而制定“分业务子域”的术语表(即统一语言)。鉴于“群买菜”并不是一个“真正的大工程”,我这里只为“群买菜”系统给出一个集中的术语表。同样限于文章篇幅,我截图部分术语表如下图:


image.png


如果您对完整的“群买菜统一语言”(即术语表)感兴趣,可访问在线页面《“群买菜”统一语言》https://xie.infoq.cn/link?target=https%3A%2F%2Fwww.stardata.top%2Fqunmaicai-ul.html)。


3核心业务流程和业务场景


有了价值需求分析、产品需求输入、以及“统一语言”定义,我们就可以开始基于业务的核心流程(也就是软件要实现的“最核心价值流程”)对业务场景进行划分、并对每个场景下的业务流程进行细化设计,进而完成两个最重要的工作:业务用例识别(也叫“业务服务”,是后续”应用服务“、“领域服务”设计的最主要基础)、业务子域识别与分类。


根据“群买菜”的价值需求分析,我们知道“群买菜”最核心的业务目的就是:满足“社区生鲜”这一“业务领域”下,“客户”和“商家”的在线交易和接龙活动。为此,我们设计出”群买菜“的核心业务流程如下图:


image.png


在进行后续的业务流程具体分析之前,我们需要将“问题空间”进行“简化”,也就是常用的“化整为零”。一般来说,对业务流程来说“化整为零”的第一步是进行“业务场景切分”——也就是按照时间轴、把时间上可以“断裂”开来的流程进行切分。就我们“群买菜”系统来说,从“时间轴”上可以“断裂”的业务场景分析如下:


商家建立店铺。商家在线销售商品之前,需要先在系统中建立店铺,并设置店铺的相关营业属性(营业时间、是否送货上门等)。这个操作,对于商家来说,可能只需要几分钟的时间。


商家建立和维护商品库。商家建立店铺后,系统并不会要求立即建立商品库,所以建立商品库和店铺是可以在“时间上断裂”的。


客户在线浏览和下单。一般来说,客户只有在商家商品库后,才有可能在店铺浏览商品和下单。但显然,并没有一个业务规则要求“客户浏览和下单”必须和“商家建立店铺、商家建立商品库”在时间上是连续操作的。事实上,商家可能只需要建立一次店铺、建立一次商品库,但客户可以发生无数次在线浏览和下单。


在线接龙活动。按照产品需求设计,系统还需要支持商家发起的在线接龙活动。在线接龙和在线销售的区别是:在线销售是一次建立商品库、但发生无数次销售;而接龙活动是在商家每次建立后、客户只能参加一次接龙。所以,商家建立接龙活动、到客户参加接龙活动在“时间轴”上是连续的。


商家结算营业收入。商家可以定期进行营业收入结算,只要客户在系统上确认了收货(或者系统超时自动收货),则商家就可以将资金提现到自己的账户下。这其中,可能涉及到系统平台运营方与商家之间的手续费结算。这一营业收入结算过程,从时间轴上来说,也是一个独立的业务场景。


商家加盟品牌。在产品原型设计中,我们可以看出产品经理还希望系统能够支持商家店铺之间的“轻加盟”模式,以实现店铺的“轻连锁”或实际的“加盟分销”。这种“轻加盟”模式,就是建立店铺和店铺之间的“品牌关联”关系,使得加盟某店铺的新店铺,可以销售被加盟店铺(称为“品牌店铺”)的所有已上架商品。从时间轴角度来说,显然作为品牌店铺设置加盟政策、以及选择哪些品牌店铺进行加盟,这是另一个”时间断裂“的独立业务场景。


根据如上的分析,再结合上述 6 个业务场景之间的逻辑关系,我们画出如下的主体业务流程图:


image.png


4各场景下的业务流程(泳道图)


上面的 6 个业务场景,我们是可以针对每个场景给出业务流程的。分别设计如下:


建立店铺。业务流程如下图:


image.png


加盟品牌。业务流程如下图:


image.png


相关文章
|
2月前
|
敏捷开发 监控 架构师
【领域驱动设计专题】一文带领你透视DDD领域驱动模型的本质和设计原理分析指南(构建领域知识)
【领域驱动设计专题】一文带领你透视DDD领域驱动模型的本质和设计原理分析指南(构建领域知识)
82 0
|
2月前
|
敏捷开发 架构师 Java
【领域驱动设计专题】一文带领你透视DDD领域驱动模型的本质和设计原理分析指南(基本概念篇)
【领域驱动设计专题】一文带领你透视DDD领域驱动模型的本质和设计原理分析指南(基本概念篇)
66 0
|
供应链 小程序 安全
DDD实战之三:整体工作框架和全局需求分析(下)
DDD实战之三:整体工作框架和全局需求分析(下)
DDD实战之三:整体工作框架和全局需求分析(下)
|
前端开发 小程序 机器人
DDD实战之五:战略设计之上下文映射和系统分层架构(上)
DDD实战之五:战略设计之上下文映射和系统分层架构(上)
DDD实战之五:战略设计之上下文映射和系统分层架构(上)
|
消息中间件 前端开发 小程序
DDD实战之五:战略设计之上下文映射和系统分层架构(下)
DDD实战之五:战略设计之上下文映射和系统分层架构(下)
DDD实战之五:战略设计之上下文映射和系统分层架构(下)
|
敏捷开发 消息中间件 前端开发
DDD实战之七: 战术设计、整体流程与首次冲刺
DDD实战之七: 战术设计、整体流程与首次冲刺
DDD实战之七: 战术设计、整体流程与首次冲刺
|
项目管理
DDD案例(1):从需求分析到领域分析(4)
DDD案例(1):从需求分析到领域分析(4)
431 0
DDD案例(1):从需求分析到领域分析(4)
|
存储 Java 网络性能优化
分布式设计要点 | 学习笔记
快速学习分布式设计要点
104 0
|
供应链 安全 领域建模
DDD案例(1):从需求分析到领域分析(6)
DDD案例(1):从需求分析到领域分析(6)
330 0
DDD案例(1):从需求分析到领域分析(6)
|
项目管理
DDD案例(1):从需求分析到领域分析(2)
DDD案例(1):从需求分析到领域分析(2)
317 0
DDD案例(1):从需求分析到领域分析(2)