DDD在大众点评交易系统演进中的应用--图文解析

DDD在大众点评交易系统演进中的应用

--图文解析外加Demo

0 Show me the demo!

绝大多数的DDD文章都是只介绍概念,没有具体的代码实现,这篇文章也是一样。我会用非常简短的时间,先简单介绍一下DDD的具体框架结构与思想。

其实DDD的核心只有两个:1是分治,这个大家都能明白。2是业务和技术分离(将不变的业务逻辑和易变的外部依赖分开),我为大家演示两个demo

四层架构:

参考案例,开源DDD框架:https://gitee.com/jensvn/d3boot9

六边形解析:https://coders.run/理解六边形架构-一个简单的springboot实现/

6边型架构,原图来自https://blog.csdn.net/github_38592071/article/details/133502950

1 大众点评交易业务介绍

三大类

境外出行

提供境外城市的美食、购物、商场、景点、门票、当地玩乐等相关服务金券。

商场团购

在国内购物有相关商场的团购优惠券等服务。

内容商业化

类似于商家找人探店,探店的内容其实就是广告。

2 领域驱动设计概述

2.1 什么是领域驱动设计

一下内容是参照原文的,包含一些我的个人观点的解析内容,如有不妥,大家可以在评论区指出,我只是单纯的想把DDD用最通俗易懂的方式让看到我视频的宝子们理解。

Domain-Driven Design

直接翻译就是领域驱动设计

其中的Driven是驱动的过去分词,其实确切点应该翻译为:被领域驱动的设计,也就是你的架构设计需要通过领域来驱动。

原文曰:"领域"指的是特定的业务范围或问题域。

最通俗易懂的理解方式,就是把领域直接理解为业务范围 ,说的官方一些就是业务域。那么DDD就可以理解为:以业务范围为基础进行设计的软件设计思想。

原文又曰:领域驱动设计是一种设计思想,首先体现了分离的思想,它分离了业务复杂性和技术复杂性,其次体现了分治的思想,它通过领域模型、限界上下文或子域进行分治。

那么DDD的主要作用就是以下两点:

1.把业务代码和技术代码分离。

2.把缠绕在一起的业务代码完全根据业务划分范围,也就是划分领域,进行分治。

上面说的把业务代码和技术代码分离是什么意思呢?

我们曾经用的传统的MVC模式设计的代码结构,通常是把业务和技术混在一起的,比如我们根据业务需求,其中一步需要获取到订单信息,在我们实际开发过程中,订单可能会存放在缓存中,我们取得订单信息先要从缓存中取得,如果缓存没有,则从数据库里取,取得后再往缓存中存一份,代码如下:

java 复制代码
		//其他业务代码...
		//获取订单
		Order order = (Order) redisTemplate.opsForValue().get("order:" + orderId);  
        if (order == null) {  
            order = orderRepository.findById(orderId);  
            redisTemplate.opsForValue().set("order:" + orderId, order, 3600, TimeUnit.SECONDS); // 缓存1小时  
        }  
        //其他业务代码...

假设业务特别复杂,业务代码本来就看着比较难懂,了解业务的老人都不在了,新人看着业务代码比较好的方式可以是这样的:

java 复制代码
		//其他业务代码...
		//获取订单
		Order order = orderRepository.getOrder(orderId);//从订单仓储中根据订单ID获取订单
        //其他业务代码...

简单说就是,业务范围(业务域/领域)中只写业务代码,让代码尽可能的和业务相关,不去关心取得对应的数据到底用的是怎样的技术,而具体的技术使用,也就是上面代码中提到的缓存相关操作,统一丢到一个叫做"基础设施层"的地方去。

另外分治的概念就比较好理解了,就是把一个业务规模巨大,业务复杂度高,业务多变的系统中的业务,拆分成一块一块的业务范围(业务域/领域),并且在对应的领域中只写业务流程代码。

2.2 领域驱动设计核心概念

原文:

领域驱动设计涉及到的核心概念非常多,我们重点强调一下"统一语言"和"限界上下文"。"统一语言"贯穿领域驱动设计从战略设计到战术设计到最后的代码实现全过程,对于需求分析、知识提炼和最后代码的实现,都是非常重要的。

"限界上下文"是连接问题空间和解决方案空间的桥梁,一方面我们在问题空间 分析问题时,它是语言的边界和模型的边界;另一方面,在解决方案空间我们通过限界上下文来确定应用的边界和技术的边界,从而帮助我们确定整个系统及各个限界上下文的解决方案。

问题空间:业务范围内的业务需求、业务流程

解决方案空间:实现业务需求,流程所使用的具体技术。

限界上下文

限界 指的是领域的边界,即某个特定范围内的业务活动。上下文是指语义环境,即在特定情境下的含义。

简单说它就是用来确定划分业务范围(领域边界)的,比如商城系统中的商品,换到仓库系统中叫做货物,因为仓库中存放到货物不止商品,这样就可以根据商品在不同语义环境中的变化来划分边界。本文中是通过业务边界和应用边界两方面来进行划分的。

举个例子:

假设我们正在开发一个在线点餐系统,用户可以通过手机应用点餐并支付。这个系统包含了以下几个子域:

订单子域:

在问题空间中,我们定义了"订单"这个概念,表示用户点餐的行为。

通过限界上下文,我们明确了订单子域的边界。在订单子域内,我们关注的是创建订单、支付、配送等业务逻辑。

在解决方案空间中,我们可以使用特定的技术和架构来实现订单子域,例如数据库表、支付接口、配送系统等。

菜单子域:

同样,在问题空间中,我们定义了"菜单"这个概念,表示可供用户选择的菜品。

通过限界上下文,我们明确了菜单子域的边界。在菜单子域内,我们关注的是菜品的管理、展示、价格等业务。

在解决方案空间中,我们可以设计数据库表来存储菜单信息,使用前端技术展示菜单给用户。

用户认证子域:

这个子域不是核心业务,但在整个系统中起到了重要作用。

在问题空间中,我们定义了"用户认证"这个概念,表示用户登录、注册等操作明确了用户认证子域的边界。在解决方案空间中,我们可以使用身份验证库、会话管理等技术来实现用户认证。

统一语言

统一语言指的是统一业务语言,而不是编程语言。简单说就是整个团队中使用相同的术语和概念来描述业务需求和领域模型,确保大家在交流时理解一致,避免混淆。比如商城中的卖家和商家不能混叫,必须统一成为商家等等。

2.3 领域驱动设计的过程

我把原文通俗化处理了一下:

  1. 共同参与

    首先,业务、产品、研发以及QA(质量保障)团队需要共同参与领域驱动设计。

    他们应该基于对问题领域(业务范围)和业务愿景(最终实现)的理解,进行充分的讨论,以达成统一的认知。

  2. 提炼领域知识和建立统一语言

    在讨论过程中,团队成员会提炼出领域知识,即关于业务领域的专业知识从而统一标准,统一语言。

  3. 进一步提炼子域

    将业务范围细粒度划分成核心业务,通用业务和支撑业务。

    也就是原文中的核心子域、通用子域和支撑子域:

    1. 核心子域(Core Domain)

      业务中最关键、最核心的部分。

    2. 通用子域(Supporting Subdomains)

      通用子域是为多个子域提供共享功能的部分。它包含了通用的业务逻辑,例如用户认证、权限管理、数据字典等。

    3. 支撑子域(Generic Subdomains)

      在本文中支撑子域是为核心子域增强功能的子域,比如商城中的订单域是核心域,而优惠券域则是支撑域,可以让订单功能变得更丰富。

  4. 模型驱动设计

    根据上面提炼的子域,设计领域模型。

  5. 认知迭代

    整个设计过程中,会不断发现新的问题,对领域会有新的认知,需要循环往复多次最终完善设计。

3 大众点评交易系统演进

3.1 简单架构阶段

使用MVC,采用数据驱动设计,简单说就是只要数据库表设计出来了,系统也就设计出来了。其实现在大多数单体架构项目依旧采用这种方式进行设计。

优点:快。

缺点:最简单的例子,订单Service需要用户信息,调用了UserService,用户登录时需要订单信息,调用了OrderService,在简单的系统中,或者是大型系统业务非常稳定的时候不会有问题,但是在业务复杂且多变的系统中,不同的Service交织在一起,你修改了其中一个Service的方法,会影响到其他调用端,不修改而是新建一个方法,又会产生冗余代码。当然,绝大多数的项目业务不会发生特别多的变化,也就是项目正式交付初期会有,改上几次也就稳定了,怕的就是那种业务复杂还一直不断变化的系统,需要不断的调整业务代码,这样确实有问题。

3.2 微服务化阶段

功能变的越来越丰富,规模越来越大,于是通过微服务来进行分治,使用了DDD来进行设计,因为DDD本身就是用来分治和分离业务和技术代码的。

如何进行设计的呢?

1.理解问题域

原文:业务价值分析 有助于评估系统的复杂性,并且可以指导我们识别最为关键的业务领域。业务需求分析是一个关键的知识提炼过程,其中涉及多种方法和工具,我们采用的是相对轻量的用例分析法

业务价值分析:简单理解就是哪些业务是最重要了,没这个业务就不行,用来寻找关键业务领域的,也就是核心域。

用例分析法:简单来说就是分为三部分,1:找出参与者。2:定义用例,用例描述了参与者通过系统可以达到的某个目标。注意,每个用例通常使用动词+名词或者名词+动词的方式实现,可以参考下图中的"创建客户,商家对账"。3:使用时序图等工具来展示参与者与系统之间的互动。

通过用例分法对业务进行提炼,最终识别出了商家、商品、订单、优惠和结算等几个子域。我们发现参与者没有变化,但领域被成功划分出来了。

核心域与支撑域

由于商品域和订单域的业务复杂度过高,所以需要对这两个核心域的限界上下文做进一步划分

简单说就是根据流程的不同阶段,参与方的关注点不同,由细分出来了如下图中的若干边界。

通过上文中的各种知识点的介绍,大家自行读一下剩下的领域划分流程就可以了,没太大难度,设计出了该分层架构:

由于商品域和订单域的业务复杂度过高,所以需要对这两个核心域的限界上下文做进一步划分

简单说就是根据流程的不同阶段,参与方的关注点不同,由细分出来了如下图中的若干边界。

[外链图片转存中...(img-c8g3mM3D-1720769331260)]

通过上文中的各种知识点的介绍,大家自行读一下剩下的领域划分流程就可以了,没太大难度,设计出了该分层架构:

[外链图片转存中...(img-uDGlAF2Z-1720769331261)]

相关推荐
火烧屁屁啦14 分钟前
【JavaEE进阶】初始Spring Web MVC
java·spring·java-ee
w_312345428 分钟前
自定义一个maven骨架 | 最佳实践
java·maven·intellij-idea
岁岁岁平安30 分钟前
spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))
java·学习·spring·依赖注入·集合注入·基本数据类型注入·引用数据类型注入
武昌库里写JAVA33 分钟前
Java成长之路(一)--SpringBoot基础学习--SpringBoot代码测试
java·开发语言·spring boot·学习·课程设计
Q_192849990640 分钟前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
张国荣家的弟弟1 小时前
【Yonghong 企业日常问题 06】上传的文件不在白名单,修改allow.jar.digest属性添加允许上传的文件SH256值?
java·jar·bi
ZSYP-S1 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos1 小时前
C++----------函数的调用机制
java·c++·算法
是小崔啊2 小时前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
黄公子学安全2 小时前
Java的基础概念(一)
java·开发语言·python