DDD实战之五:战略设计之上下文映射和系统分层架构(下)

简介: DDD实战之五:战略设计之上下文映射和系统分层架构(下)

该服务序列图展示出,实际上“接龙”、“店铺”这 2 个上下文没有发生关联关系。但这个服务序列图设计,有个“坏味道”的感觉:让群买菜小程序客户端承担了过多业务逻辑,这是不合理的。于是,我们将服务序列图调整为如下:

image.png

该服务序列图会导致如下的 2 个上下文之间的关系:

image.png

确认接龙付款

确认接龙付款从产品界面原型可以看出,确认接龙付款是从“查看接龙详情”界面发起的。客户在该界面上点击相应的商品加入购物车、或从购物车移除,然后点击“我要接龙”按钮进入该用例。

该用例允许用户设置提货方式、提货时间、联系人等信息后,点击“确认付款”按钮完成支付。该按钮点击后,按照产品原型设计,需要完成如下任务:

  • 创建订单;
  • 更新关联商品的销量,以便于后续商品列表和详情页面显示商品销量;
  • 如果下单客户首次购买对应店铺商品,则为该店铺初始化对应客户资料,以便于商家后续维护客户资料;
  • 记录客户参与了该接龙,以便于客户“浏览我的接龙”时,可包含该接龙;

根据上面的逻辑,我们画出服务序列图设计如下:

image.png

该服务序列图展示的相关限界上下文关系如下图:

image.png

这里可以看到上下文之间的调用关系比较多,并且“订单”与“商品”、“订单”与“客户”之间,还存在数据一致性的要求,不利于系统的“松耦合”。为了降低上下文之间的耦合性,我们分析业务需求发现:其实“订单创建”后“增加商品销量”、以及“为指定店铺初始化客户”是可以有一定的数据延迟的,并不需要通过“强”服务调用关系保障严格的数据一致性。为此,我们将服务序列图修改如下:

image.png

根据改进后的服务序列图,可以调整上下文映射关系如下图:

image.png

结算订单收入

结算订单收入分为两步:第一步,在客户确认订单完成、或机器人超时自动确认订单完成时,订单上下文通知商家账户上下文记录待结算的订单 ID(由于订单上下文缺乏“判断什么订单属于待结算状态”这样的领域知识,故只能由商家账户上下文来记录);第二步,系统机器人定时触发商家账户上下文对待结算订单进行收入结算。服务序列图设计如下:

image.png

其中第一步的操作,可以和“发送订单提醒”中的“订单已完成”领域事件合并处理。不过,因为这里是不可丢失的领域事件,所以这就要求采用“可靠事件模式”。基于该服务序列图,识别出“订单”和“商家账户”上下文的关系如图:

image.png

结算佣金收入

结算佣金收入可以和结算订单收入同时进行,也可以分开在两个用例中异步执行。考虑到为了将来支持允许品牌商在加盟政策中设置结算周期,故将其分开在两个业务用例异步执行。为此,设计其服务序列图如下:

image.png

该序列图展示出商家账户和订单的上下文关系如图:

image.png

3限界上下文映射图

     

我们将上面针对各跨上下文业务用例分析后,得到的上下文映射关系进行汇总后最终得出下图:

image.png

图中实线是服务调用关系,属于“强关系”;虚线是消息通知关系,属于“弱关系”。

通过该限界上线文映射关系可以看出:我们总共有 9 个限界上下文,其中有强依赖关系的涉及到 9 个、强依赖关系链有 9 条。分别是:接龙依赖于店铺、商品和订单,店铺依赖于平台集成,订单依赖于商品,商家账户、员工、客户依赖于鉴权,商家账户依赖于订单。9 个上下文,理论上最多有 72 条依赖链条,我们分析汇总后只有 11 条,已经是很好的设计。

基于这样的综合评估,我们认为目前的设计已经到了“可接受程度”,暂时不再做更多的优化和调整了,以避免“过度设计”。

02系统架构和代码框架


1业务子域与上下文的映射

     

完成了限界上下文的关系映射,其实就有了对整体系统架构进行分层设计的基础。系统整体架构设计从分层角度来看,包括:边缘层、业务价值层、基础层。边缘层一般都是各种针对前端 UI 的控制器,业务价值层和基础层包含所有的限界上下文,其中基础层放的是对应到支撑子域、通用子域的限界上下文,而核心子域对应的限界上下文作为业务价值层。

为了区分业务价值层和基础层,我们先将前面得出的业务子域(见第三篇中全局分析内容)、与限界上下文进行如下表所示的关系映射:

image.png

需要说明的是:其中“商家管理”和“店铺管理”虽然是支撑子域,但由于分别被合并到“商家账户”、“店铺”上下文来开发实现,故其中逻辑的代码实现也被划分到了“业务价值层”。

2菱形对称架构简介

在对整个系统进行“分层架构设计”之前,我们还需要先熟悉一个软件架构模式——菱形对称架构(以下简称菱形架构),如下图所示:

image.png

对“菱形架构”的说明如下:

菱形架构的基础,是依赖于“领域层”的界定。这涉及到后面才会进行的 DDD 战术设计内容,这里不做展开,您只需要知道:“领域层”放的是业务领域的关键业务知识,包括“聚合(内含实体对象、值对象)”和“领域服务”两类内容。事实上,我们所追求的“高内聚”主要体现的“领域层”,因为业务需求变化而引起的变化,我也希望主要通过“领域层”的“聚合”和“领域服务”的变化来实现。所以说,“领域层”是 DDD 的“核心代码所在地”。

所有非“领域逻辑”层的代码,我们都希望封装到“北向网关”或“南向网关”中去。具体说明如下:

“北向网关”其实就是上下文向外提供服务、或接受消息通知的入口处。如果上下文只需要向外提供同一个进程内部的应用服务调用接口、或接受消息通知(通过消息总线),则需要“本地”服务;如果上下文需要作为独立进程(这时候一般是云原生的独立“微服务”)向外输出服务、或接受消息通知(通过消息中间件),则需要将“本地服务”包装为“远程”服务。

也就是说:北向网关中的“本地服务”和“远程服务”是一一对应的,“远程服务”只负责将远程调用的出入参做格式转换并传给“本地服务”,而“本地服务”负责调用领域层的“聚合”或“领域服务”来完成具体的业务逻辑(关于“聚合”和“领域服务”的内容,我会在本专题后面的章节中演示)。

“南向网关”其实就是上下文用来将这 3 类技术细节从业务逻辑中“剥离”出来的主要手段(图中只画了第一种):访问底层数据库、调用其它上下文服务、向其它上下文发布消息通知。这 3 个技术细节的封装,在 DDD 战术设计中分别叫“资源库”、“客户端(也可以叫防腐层——ACL)”、“消息发布者”。

从图中可以看出,“南向网关”有“端口”和“适配器”两种角色。简单点来说,“端口”是抽象接口(以 java 为例就是 interface),而“适配器”则是“接口实现”(以 java 为例就是实现了对应 interface 的类)。并且,一般来说“适配器”都是通过依赖注入(DI,控制反转 IoC 的一种)的方式集成到限界上下文的代码中(java spring 中一般是 bean 注入)。

“南向网关”区分“端口”和“适配器”两个角色的好处:一方面是可以让限界上下文内的任何代码都不直接依赖于具体的底层技术细节,如:采用哪种数据库(oracle 还是 mysql、甚至 nosql 数据库)、怎么调用其它上下文服务(本地调用还是远程 RPC)、怎么发布消息通知(本地消息总线、还是消息中间件);另一方面的好处是允许我们随时将构建在一个“微服务”甚至“单体应用”中的多个限界上下文进行拆分到多个进程(即多个“微服务”中),而并不会引起“领域层”、以及“北向网关”的任何代码修改(只要替换并重新打包被依赖注入的“适配器”类即可)。

很明显可以看出,“菱形架构”其实是限界上下文内部所采用的一个“软件架构模式”。而在整个系统范围内,因为包含多个限界上下文,DDD 设计理念并没有要求所有的上下文都严格遵循“菱形架构”——而完全可以根据实际需要(尤其是“基础层”的上下文),视情况而采用其它架构模式(如 MVC 三层架构、大数据计算架构等)。

3系统分层架构图

     

有了前面的将上下文分为“基础层”和“业务价值层”、以及菱形架构概念的基础,再考虑到“群买菜”系统前端界面针对 3 类用户:商家、客户、平台运营,前两者使用微信小程序,最后一个使用 PC 端,以及相应“伴生系统”的协作边界。我们最后画出系统架构分层设计图如下:

image.png

对于上图,需要特别说明的是:

其中腾讯地图、短信系统、微信公众号因为不涉及到业务流程,只是一个功能点,故前面的业务子域、上下文、业务用例都没有展现。

有的上下文有“事件订阅”。这是根据我们前面“限界上下文映射图”中的描述,需要进行消息订阅的上下文才有。

其中比较特殊的一点:“平台集成上下文”没有“领域层”,这是因为它主要完成对微信公众号接口、短信接口、微信开放平台接口的封装,并不存在 DDD 战术设计所需要的“实体”对象这一“领域层”必须的基本要素。

4代码框架结构    

基于前面的系统分层架构设计,结合菱形架构模式,我们给出目标系统的如下代码框架结构(采用 java spring 框架开发):

image.png

对上图中各目录的划分和命名说明如下:

foundation 目录存放的是“基础层”限界上下文,valueadded 目录存放的是“业务价值层”限界上下文,edge 目录存放的是“边缘层”限界上下文;

本质上,每个限界上下文都可以作为独立的 java 工程存在。但因为本项目由我一个人开发,所以就没有划分工程了;

每个限界上下文,采用“上下文名称+context”命名风格。如:“订单上下文”命名为 ordercontext;

每个限界上下文中,其内部目录结构说明如下:

  • north 目录存放的是“北向网关”的内容,包括 local 和 remote 子目录,分别对应北向网关的“本地服务”和“远程服务”。有的上下文的 north 目录下有 subsrciber 子目录,是为了存放消息订阅者代码的。
  • south 目录存放的是“南向网关”的内容,包括 port 和 adapter 子目录,分别对应南向网关的“端口”和“适配器”。
  • domain 目录存放的是“领域层”内容,也就是核心的业务逻辑所在。
  • pl 目录存放的是“发布语言”,其实就是北向网关的“本地服务”向外提供服务时,允许外部调用服务所使用的出入参对象类型。之所以将出入参对象类专门设定一个目录来管理,是因为这些出入参其实是和 domain 目录下的“实体类”是不同的,我们不希望将“领域层”内部的业务逻辑暴露出去(也就是不将“实体类”的业务逻辑暴露出去)。
  • 业务价值层有个 sharedcontext,这是因为考虑到代码复用、可能会出现某些“值对象”、“发布语言(即服务接口出入参)”类被多个上下文共用,具体细节我在后面的战术设计中会讲到。

对于“群买菜”系统来说,由于小程序前端界面已经存在,本次是服务端做 DDD 改造,故不打算对前后端交互接口进行调整。为此,我为其设计了“边缘层”,也就是 BFF 层。这就是 edge 目录存放的代码内容。

好了,本节作为“群买菜”系统 DDD 战略设计的第二篇,就先到这儿。下节我们将完成战略设计的最后一步:相关战略技术决策。

相关文章
|
17天前
|
安全 数据管理 中间件
云LIS系统源码JavaScript+B/S架构MVC+SQLSugar医院版检验科云LIS系统源码 可提供演示
检验科云LIS系统源码是医疗机构信息化发展的重要趋势。通过云计算技术实现数据的集中管理和共享可以提高数据利用效率和安全性;通过高效灵活的系统设计和可扩展性可以满足不同医疗机构的需求;通过移动性和智能化可以提高医疗服务的精准度和效率;通过集成性可以实现医疗服务的协同性和效率。因此,多医院版检验科云LIS系统源码将成为未来医疗机构信息化发展的重要方向之一。
26 2
|
5天前
|
前端开发 Java 关系型数据库
Java医院绩效考核系统源码B/S架构+springboot三级公立医院绩效考核系统源码 医院综合绩效核算系统源码
作为医院用综合绩效核算系统,系统需要和his系统进行对接,按照设定周期,从his系统获取医院科室和医生、护士、其他人员工作量,对没有录入信息化系统的工作量,绩效考核系统设有手工录入功能(可以批量导入),对获取的数据系统按照设定的公式进行汇算,且设置审核机制,可以退回修正,系统功能强大,完全模拟医院实际绩效核算过程,且每步核算都可以进行调整和参数设置,能适应医院多种绩效核算方式。
26 2
|
14天前
|
API 开发者 UED
构建高效微服务架构:后端开发的新趋势移动应用与系统:开发与优化的艺术
【4月更文挑战第30天】 随着现代软件系统对可伸缩性、灵活性和敏捷性的日益需求,传统的单体应用架构正逐渐向微服务架构转变。本文将探讨微服务架构的核心概念,分析其优势,并着重讨论如何利用最新的后端技术栈实现一个高效的微服务系统。我们将涵盖设计模式、服务划分、数据一致性、服务发现与注册、API网关以及容器化等关键技术点,为后端开发者提供一份实操指南。 【4月更文挑战第30天】 在数字化时代的浪潮中,移动应用和操作系统的紧密交织已成为日常生活和商业活动的基石。本文将深入探讨移动应用开发的关键技术、跨平台开发工具的选择以及移动操作系统的架构和性能优化策略。通过分析当前移动应用开发的挑战与机遇,我们将
|
15天前
|
安全 Java 数据安全/隐私保护
Spring Boot优雅实现多租户架构:概念与实战
【4月更文挑战第29天】在多租户系统中,一个应用实例服务于多个租户,每个租户享有独立的数据视图,而应用的基础设施被共享。这样的架构不仅优化了资源使用,还能降低维护和运营成本。本文将详细介绍如何在Spring Boot中实现多租户架构,并提供具体的实战案例。
40 2
|
17天前
|
消息中间件 监控 中间件
探索微服务架构下的系统弹性设计
【4月更文挑战第26天】 在当今快速迭代和持续部署的软件发展环境中,系统的弹性设计成为维护高可用性和稳定性的关键因素。本文将深入探讨在微服务架构下如何实现系统弹性,包括识别潜在的故障点、设计容错机制、以及通过实践案例分析提升系统整体的韧性。我们将讨论一系列策略,如服务降级、超时管理、重试策略、断路器模式等,旨在为开发者提供一套实用的系统弹性设计方案。
|
22天前
|
缓存 监控 算法
Python性能优化面试:代码级、架构级与系统级优化
【4月更文挑战第19天】本文探讨了Python性能优化面试的重点,包括代码级、架构级和系统级优化。代码级优化涉及时间复杂度、空间复杂度分析,使用内置数据结构和性能分析工具。易错点包括过度优化和滥用全局变量。架构级优化关注异步编程、缓存策略和分布式系统,强调合理利用异步和缓存。系统级优化则涵盖操作系统原理、Python虚拟机优化和服务器调优,需注意监控系统资源和使用编译器加速。面试者应全面理解这些层面,以提高程序性能和面试竞争力。
18 1
Python性能优化面试:代码级、架构级与系统级优化
|
22天前
|
运维 安全 定位技术
云HIS系统采用B/S架构云端SaaS服务的方式提供,使用用户通过浏览器即能访问
云HIS系统采用B/S架构云端SaaS服务的方式提供,使用用户通过浏览器即能访问
26 2
|
4天前
|
存储 监控 API
构建高效微服务架构:后端开发的现代实践
【5月更文挑战第9天】 在本文中,我们将深入探讨如何在后端开发中构建一个高效的微服务架构。通过分析不同的设计模式和最佳实践,我们将展示如何提升系统的可扩展性、弹性和维护性。我们还将讨论微服务架构在处理复杂业务逻辑和高并发场景下的优势。最后,我们将分享一些实用的工具和技术,以帮助开发者实现这一目标。
|
1天前
|
负载均衡 JavaScript Java
构建高效微服务架构:后端开发的新视角
【5月更文挑战第13天】在现代软件开发中,微服务架构已经成为一种流行趋势。它通过将应用程序拆分为一组小型、独立的服务来提高可扩展性、弹性和可维护性。本文将探讨如何构建一个高效的微服务架构,包括选择合适的技术栈、设计良好的服务接口、确保数据一致性以及实现有效的服务发现和负载均衡。
|
1天前
|
监控 Java 开发者
构建高效微服务架构:后端开发的新趋势
【5月更文挑战第13天】随着现代应用的复杂性日益增加,传统的单体应用架构已不足以满足快速迭代和可扩展性的需求。本文将探讨如何通过微服务架构来提升后端开发的效率和系统的可靠性,涵盖微服务设计原则、技术栈选择、部署策略以及维护实践。我们将分析微服务的优势与挑战,并提供一系列实施建议,帮助开发者在构建和维护分布式系统时做出明智决策。