DDD - 微服务落地的技术实践
文章目录
- Pre
- 概述
- 如何发挥微服务的优势
- 怎样提供微服务接口
- 原则
- 微服务的拆分与防腐层的设计
- 去中心化的数据管理
- 数据关联查询的难题
- Case 1
- Case 2
- Case 3
- 总结

Pre
DDD - 软件退化原因及案例分析
DDD - 如何运用 DDD 进行软件设计
DDD - 如何运用 DDD 进行数据库设计
DDD - 服务、实体与值对象的两种设计思路:贫血模型与充血模型
DDD - 聚合、聚合根、仓库与工厂
DDD - 微服务设计与领域驱动设计实战(上)_统一建模语言及事件风暴会议
DDD - 微服务设计与领域驱动设计实战(中)_ 解决微服务拆分难题
DDD - 微服务设计与领域驱动设计实战(下)_落地微服务设计实现
概述
如今,做一个优秀的程序员越来越难。激烈的市场竞争、互联网快速的迭代、软件系统规模化发展,无疑都大大增加了软件设计的难度。因此,对于架构师的能力要求也越来越高 :
作为顶级架构师应当具备这样两个核心能力:
- (1)能够将业务转换为技术;
- (2)能合理利用技术支撑业务。
能够将业务转换为技术,意味着需要将更多的精力放到对业务的理解中。技术本身并不能产生价值,必须具备超强的业务落地能力,能够将用户的业务需求落地到技术方案,开发出用户乐于使用的产品和功能,用户才能为之买单,企业才能挣钱。具备这样的能力,才能够强力地帮助企业产生效益,才能体现价值。学习 DDD 就能让你掌握快速学习业务领域知识的能力。
能合理利用技术支撑业务,意味着必须具备广博的知识与开阔的视野,能将用户的业务痛点,快速落地形成合理的,甚至是最优的技术方案。做出用户需要的功能,让用户为之买单,从而为企业产生效益。然而,如今是一个技术快速更迭的时代,各种高新技术层出不穷。每次新产品的开发不是将原有的技术拿来炒冷饭,而是运用更多的新技术解决新问题,让产品更有竞争力与生命力。因此,必须有广博的技术知识与超强的技术落地能力。
DDD - 微服务设计与领域驱动设计实战(上)_统一建模语言及事件风暴会议
DDD - 微服务设计与领域驱动设计实战(中)_ 解决微服务拆分难题
DDD - 微服务设计与领域驱动设计实战(下)_落地微服务设计实现谈到 DDD 落地微服务的分析设计过程,然后将这些设计最终落实到每个微服务的设计开发中。微服务的落地其实并没有那么简单,需要解决诸多设计与实现的技术难题,我们就来探讨一下吧。
如何发挥微服务的优势
微服务也不是银弹,它有很多的“坑”。 当我们将一个庞大的业务系统拆分为一个个简单的微服务时,就是希望通过合理的微服务设计,尽量让每次的需求变更都交给某个小团队独立完成,让需求变更落到某个微服务上进行变更。唯有这样,每次变更只需独立地修改这个微服务,独立打包、独立升级,新需求就实现啦,才能发挥微服务的优势。

然而,过去很多系统都是这样设计的(如上图所示),多个模块都需要读取商品信息表,因此都通过 JDBC 直接读取。现在要转型微服务了,起初采用数据共享的微服务设计,就是数据库不变,然后简单粗暴地直接按照功能模块进行微服务拆分。这时,多个微服务都需要读取商品信息表,都通过 SQL 直接访问。这样的设计,一旦商品信息表发生变更,那么多个微服务都需要变更。这样的设计就使得微服务的变更与发布变得复杂,微服务的优势无法发挥。

通过 DDD 的指导,是希望做“小而专”的微服务设计。按照这样的思路设计微服务,对商品信息表的读写只有“商品维护”微服务。当其他微服务需要读写商品信息时,就不能直接读取商品信息表,而是通过 API 接口去调用“商品维护”微服务。这样,日后因商品信息变更而修改的代码就只限于“商品维护”微服务。只要“商品维护”微服务对外的 API 接口不变,这个变更则与其他微服务无关。只有这样的设计,才能真正发挥微服务的优势。
为了规范“小而专”的微服务设计,在微服务转型之初,先按照 DDD 对数据库表按照用户权限进行划分。每个微服务只能通过自己的账号访问自己的表。当需要访问其他的表时,只能通过接口访问相应的微服务。这样的划分,就为日后真正的数据库拆分做好了准备,微服务转型将更加平稳。
怎样提供微服务接口
因此,微服务的设计彼此之间不是孤立的,它们需要相互调用接口实现高内聚。然而,当一个微服务团队向另一个微服务团队提出接口调用需求时,另一个微服务团队该如何设计呢?
原则
首先第一个问题,当多个团队都在向你提出 API 接口时,怎么提供接口。如果每个团队给你提需求,就必须要做一个新接口,那么你的微服务将变得非常不稳定。因此,
- 当多个团队向你提需求时,必须要对这些接口进行规划,通过复用用尽可能少的接口满足他们的需求;
- 当有新的接口提出时,要尽量通过现有接口解决问题。这样做,就能用更低的维护成本,更好地维护自己的微服务。
接着,当调用方需要接口变更时怎么办?
-
变更现有接口应当尽可能向前兼容,即接口的名称与参数都不变,只是在内部增加新的功能。这样做是为了不影响其他微服务的调用。
-
如果确实需要更改现有的接口怎么办?宁愿增加一个新的接口也最好不要去变更原有的接口。
-
调用双方传递的值对象需要完全一致吗?当然不用。当被调方因为某些变更对值对象增加了字段,而这些字段调用方不使用时,那么调用方不需要跟着变更值对象。因为微服务间的调用是采用RESTful 接口,以 JSON 的形成传递数据,是一种松耦合的调用。因此调用双方的值对象可以不一致,从而降低了需求变更的微服务更新范围。
-
最后,调用方 如何调用接口呢?这里分为 同步调用与异步调用。
“用户接单 Service”在完成下单以后,用消息队列通知“饭店接单 Service”,就是异步调用。
“用户接单Service”常常要查找用户表信息,但前面说了,它没有查询用户表权限,因为用户表在“用户注册”微服务中。这时,“用户接单 Service”通过同步调用“用户注册 Service”的相关接口。
微服务的拆分与防腐层的设计
具体设计实现上,就是在“用户接单”微服务的本地,增加一个“用户注册 Service”的 feign 接口。这样,“用户接单 Service”就像本地调用一样调用“用户注册 Service”,再通过这个 feign 接口实现远程调用。这样的设计叫作“防腐层”的设计。如下图所示:

微服务的拆分与防腐层的设计图
譬如,大家想象这样一个场景。过去,“用户注册 Service”是在“用户下单”微服务中的。后来,随着微服务设计的不断深入,需要将“用户注册 Service”拆分到另外一个微服务中。这时,“用户下单Service”与“取消订单 Service”,以及其他对“用户注册 Service”的调用都会报错,都需要修改,维护成本就很高。这时,在微服务的本地放一个“用户注册 Service”的 feign 接口,那么其他的 Service 都不需要修改了,维护成本将得以降低。这就是**“防腐层”的作用,即接口变更时降低维护成本**。
去中心化的数据管理
按照前面 DDD 的设计,已经将数据库按照微服务划分为用户库、下单库、接单库、派送库与饭店库。这时候,如何来落地这些数据库的设计呢?微服务系统最大的设计难题就是要面对互联网的高并发与大数据。因此,可以按照“去中心化数据管理”的思想,根据数据量与用户访问特点,选用不同的数据存储方案存储数据:
-
微服务“用户注册”与“饭店管理”分别对应的用户库与饭店库,它们的共同特点是数据量小但频繁读取,可以选用小型的 MySQL 数据库并在前面架设 Redis 来提高查询性能;
-
微服务“用户下单”“饭店接单”“骑士派送”分别对应的下单库、接单库、派送库,其特点是数据量大并且高并发写,选用一个数据库显然扛不住这样的压力,因此可以选用了 TiDB 这样的 NewSQL 数据库进行分布式存储,将数据压力分散到多个数据节点中,从而解决 I/O 瓶颈;
-
微服务“经营分析”与“订单查询”这样的查询分析业务,则选用 NoSQL 数据库或大数据平台,通过读写分离将生产库上的数据同步过来进行分布式存储,然后经过一系列的预处理,就能应对海量历史数据的决策分析与秒级查询。
基于以上这些设计,就能完美地应对互联网应用的高并发与大数据,有效提高系统性能。设计如下图所示:

在线订餐系统的去中心化数据管理图
数据关联查询的难题
Case 1
此外,各个微服务在业务进行过程需要进行的各种查询,由于数据库的拆分,就不能像以前那样进行 join 操作了,而是通过接口调用的方式进行数据补填。比如“用户下单”“饭店接单”“骑士派送”等微服务,由于数据库的拆分,它们已经没有访问用户表与饭店表的权限,就不能像以往那样进行 join 操作了。这时,需要重构查询的过程。
如下图所示:

查询的过程分为 2 个步骤。
-
查询订单数据,但不执行 join 操作。这样的查询结果可能有 1 万条,但通过翻页,返回给微服务的只是那一页的 20 条数据。
-
再通过调用“用户注册”与“饭店管理”微服务的相关接口,实现对用户与饭店数据的补填。
这种方式,既解决了跨库关联查询的问题,又提高了海量数据下的查询效率。
注意,传统的数据库设计之所以在数据量越来越大时,查询速度越来越慢,就是因为存在 join 操作。因而,在面对海量数据的查询时,干掉 join 操作,改为分页后的数据补填,就能有效地提高查询性能。
Case 2
然而,在查询订单时,如果要通过用户姓名、联系电话进行过滤,然后再查询时,又该如何设计呢?这里千万不能先过滤用户数据,再去查询订单,这是一个非常糟糕的设计。我们过去的数据库设计采用的都是3NF(第 3 范式),它能够帮助我们减少数据冗余,然而却带来了频繁的 join 操作,降低了查询性能。因此,为了提升海量数据的查询性能,适当增加冗余,即在订单表中增加用户姓名、联系电话等字段。这样,在查询时直接过滤订单表就好了,查询性能就得到了提高。
Case 3
最后,当系统要在某些查询模块进行订单查询时,可能对各个字段都需要进行过滤查询。这时就不再采用数据补填的方式,而是利用 NoSQL 的特性,采用“宽表”的设计。按照这种设计思路,当系统通过读写分离从生产库批量导入查询库时,提前进行 join 操作,然后将 join 以后的数据,直接写入查询库的一个表中。由于这个表比一般的表字段更多,因此被称为“宽表”。
由于 NoSQL 独有的特性,为空的字段是不占用空间的,因此字段再多都不影响查询性能。这样,在日后的查询时,就不再需要 join 操作,而是直接在这个单表中进行各种过滤、各种查询,从而在海量历史数据中实现秒级查询。因此,“订单查询”微服务在数据库设计时,就可以通过NoSQL 数据库建立宽表,从而实现高效的数据查询。
总结
基于 DDD 的微服务设计,既强调对业务的分析理解,又强调对业务的技术落地。 在这个过程中,微服务间要通过 feign 接口相互调用,数据要通过补填关联查询。此外,还有聚合的实现、仓库和工厂的设计。所有这些内容都需要在 DDD 设计思想的基础上,落地实现。
然而,如果每个模块都要反复地写代码去实现这些功能,DDD 的设计将显得异常烦琐,因此迫切需要有一个既支持 DDD,又支持微服务的技术中台,封装这些代码,简化微服务的设计。

相关文章:
DDD - 微服务落地的技术实践
文章目录 Pre概述如何发挥微服务的优势怎样提供微服务接口原则微服务的拆分与防腐层的设计 去中心化的数据管理数据关联查询的难题Case 1Case 2Case 3 总结 Pre DDD - 软件退化原因及案例分析 DDD - 如何运用 DDD 进行软件设计 DDD - 如何运用 DDD 进行数据库设计 DDD - 服…...
fgets、scanf存字符串应用
题目1 夺旗(英语:Capture the flag,简称 CTF)在计算机安全中是一种活动,当中会将“旗子”秘密地埋藏于有目的的易受攻击的程序或网站。参赛者从其他参赛者或主办方偷去旗子。 非常崇拜探姬的小学妹最近迷上了 CTF&am…...
鸿蒙动态路由实现方案
背景 随着CSDN 鸿蒙APP 业务功能的增加,以及为了与iOS、Android 端统一页面跳转路由,以及动态下发路由链接,路由重定向等功能。鸿蒙动态路由方案的实现迫在眉睫。 实现方案 鸿蒙版本动态路由的实现原理,类似于 iOS与Android的实…...
Spring-boot3.4最新版整合swagger和Mybatis-plus
好家伙,今天终于开始用spring-boot3开始写项目了,以后要彻底告别1.x和2.x了,同样的jdk也来到了最低17的要求了,废话不多说直接开始 这是官方文档的要求jdk最低是17 maven最低是3.6 一. 构建工程,这一步就不需要给大家解释了吧 二. 整合Knife4j 1.大于…...
基于Java的高校实习管理平台
基于Java的高校实习管理平台是一个专为高校设计的信息化管理工具,旨在通过信息化手段简化实习管理流程,提高管理效率,增强学校、企业与学生之间的沟通与协作。: 一、系统背景与意义 随着教育体系的不断完善和就业市场的日益竞争…...
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之一维数组(应用技巧)
二、一维数组应用技巧2:打标记 实战训练1—开关灯 问题描述: 有 M个从1到M依次编号的人参加一项游戏。将K 盏从1到K依次编号的灯(K和M均为正整数,M≤K≤5000)进行一系列的熄灭与打开的操作,游戏开始时均处于亮灯的状态…...
【2024年华为OD机试】 (B卷,100分)- 路灯照明问题(Java JS PythonC/C++)
一、问题描述 路灯照明问题 题目描述 在一条笔直的公路上安装了 ( N ) 个路灯,从位置 0 开始安装,路灯之间间距固定为 100 米。每个路灯都有自己的照明半径。请计算第一个路灯和最后一个路灯之间,无法照明的区间的长度和。 输入描述 第一…...
SVGAPlayer error 处理
提示错误 Call to undeclared function OSAtomicCompareAndSwapPtrBarrier; ISO C99 and later do not support implicit function declarations Conflicting types for OSAtomicCompareAndSwapPtrBarrier Declaration of OSAtomicCompareAndSwapPtrBarrier must be imported …...
2024年12月电子学会青少年机器人技术等级考试(二级)实际操作试卷
2024.12 青少年机器人技术等级考试(二级)实际操作试卷 一、多选题 第 1 题 关于后轮驱动车说法正确的有哪些?( ) A.起步加速表现比前轮驱动好 B.容易转向过度 C.车身重量比前轮驱动更均衡 D.造价比前轮驱动车更高…...
Swift 专题二 语法速查
一 、变量 let, var 变量是可变的,使用 var 修饰,常量是不可变的,使用 let 修饰。类、结构体和枚举里的变量是属性。 var v1:String "hi" // 标注类型 var v2 "类型推导" let l1 "标题" // 常量class a {…...
Api网关Zuul
网关分类与开放API 开放API (OpenAPI) 企业需要将自身数据、能力等作为开发平台向外开放,通常会以REST的方式向外提供,最好的例子就是淘宝开放平台、腾讯公司的QQ开发平台、微信开放平台。开放API平台必然涉及到客户应用的接入、API权限的管理、调用次数…...
01设计模式(D3_设计模式类型 - D3_行为型模式)
目录 一、模版方法模式 1. 基本介绍 2. 应用案例一:豆浆制作问题 需求 代码实现 模板方法模式的钩子方法 3. View的draw(Android) Android中View的draw方法就是使用了模板方法模式 模板方法模式在 Spring 框架应用的源码分析 知识小…...
python编程-OpenCV(图像读写-图像处理-图像滤波-角点检测-边缘检测)角点检测
角点检测(Corner Detection)是计算机视觉和图像处理中重要的步骤,主要用于提取图像中的关键特征,以便进行后续的任务,比如图像匹配、物体识别、运动跟踪等。下面介绍几种常用的角点检测方法及其应用。 1. Harris角点检…...
费解的开关
费解的开关 你玩过“拉灯”游戏吗? 25 盏灯排成一个 55 的方形。 每一个灯都有一个开关,游戏者可以改变它的状态。 每一步,游戏者可以改变某一个灯的状态。 游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也…...
【机器学习】机器学习引领数学难题攻克:迈向未知数学领域的新突破
我的个人主页 我的领域:人工智能篇,希望能帮助到大家!!!👍点赞 收藏❤ 一、引言 在数学的浩瀚领域中,存在着诸多长期未解的难题,这些难题犹如高耸的山峰,吸引着无数数…...
Qt之QDjango-db的简单使用
QDjango是一款由C编写、依托于Qt库的Web开发框架,其设计理念受到了广受欢迎的Python框架Django的影响。这个项目旨在提供一个高效、灵活且易于使用的工具集,帮助开发者构建高质量的Web应用。其项目地址: https://gitcode.com/gh_mirrors/qd/qdjango&…...
缓存、数据库双写一致性解决方案
双写一致性问题的核心是确保数据库和缓存之间的数据同步,以避免缓存与数据库数据不同步的问题,尤其是在高并发和异步环境下。本文将探讨双写一致性面临的主要问题和解决方案,重点关注最终一致性。 本文讨论的是最终一致性问题 双写一致性面…...
SUnet: A multi-organ segmentation network based on multiple attention【医学图像分割】
一、论文信息 1.1、中文名称 名称:SUnet:基于多重注意力的多器官分割网络 1.2、论文关键词 医学图像分割、Transformer、注意力机制、高效特征融合模块 1.3、核心概述 本文提出了一种新颖有效的医学图像分割方法 SUnet,用于腹部和胸部的多…...
uniapp实现“到这儿去”、拨打电话功能
"到这儿去" 在 UniApp 中实现“到这儿去”的功能,即调起地图导航至指定位置,对于不同的平台(小程序、H5、App)有不同的处理方式。下面将简单介绍如何在这些平台上实现该功能,并讨论位置信息的获取。后面需求会用到,先来找一些相关资料,并不一定很准确,但也来…...
2025年入职/转行网络安全,该如何规划?网络安全职业规划
网络安全是一个日益增长的行业,对于打算进入或转行进入该领域的人来说,制定一个清晰且系统的职业规划非常重要。2025年,网络安全领域将继续发展并面临新的挑战,包括不断变化的技术、法规要求以及日益复杂的威胁环境。以下是一个关…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
算法打卡第18天
从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。 示例 1: 输入:inorder [9,3,15,20,7…...
