gitbook/李智慧 · 高并发架构实战课/docs/502205.md
2022-09-03 22:05:03 +08:00

96 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 21 | 网约车系统重构:如何用 DDD 重构网约车系统设计?
你好,我是李智慧。
软件开发是一个过程这个过程中相关方对软件系统的认知会不断改变。当系统现状和大家的认知有严重冲突的时候不重构系统就难以继续开发下去。此外在持续的需求迭代过程中代码本身会逐渐腐坏变得僵硬、脆弱、难以维护需求开发周期越来越长bug却越来越多系统也必须要进行重构。
我们在前一篇讨论的Udi网约车系统经过了几年的快速发展随着业务越来越复杂功能模块越来越多开发团队越来越庞大整个系统也越来越笨拙、难以维护。以前两三天就能开发完成的新功能现在要几个星期开发人员多了工作效率却下降了。
Udi使用微服务架构开始的时候业务比较简单几个微服务就可以搞定。后面随着功能越来越多微服务也越来越多微服务之间的依赖关系也变得越来越复杂常常要开发一个小功能却需要在好几个微服务中进行修改。后来开发人员为了避免这种复杂性倾向于把所有功能都写在一个微服务里结果整个系统架构又开始退回到单体架构。
基于以上原因我们准备对Udi进行一次重构核心就是要解决微服务设计的混乱梳理、重构出更加清晰的微服务边界和微服务之间的依赖关系。我们准备使用DDD即领域驱动设计的方法进行这次重构。
那么领域驱动设计的核心思想是什么设计的一般方法是什么如何将这些方法应用到Udi的重构过程中这些就是我们今天要解决的主要问题。
## DDD的一般方法
领域是一个组织所做的事情以及其包含的一切通俗地说就是组织的业务范围和做事方式也是软件开发的目标范围。比如对于淘宝这样一个以电子商务为主要业务的组织C2C电子商务就是它的领域。**领域驱动设计就是从领域出发,分析领域内模型及其关系,进而设计软件系统的方法。**
但是如果我们说要对C2C电子商务这个领域进行建模设计那么这个范围就太大了不知道该如何下手。所以通常的做法是把整个领域拆分成多个**子域**,比如用户、商品、订单、库存、物流、发票等。强相关的多个子域组成一个**限界上下文**,它是对业务领域范围的描述,对于系统实现而言,限界上下文相当于是一个子系统或者一个模块。限界上下文和子域共同组成组织的领域,如下:
![图片](https://static001.geekbang.org/resource/image/29/fb/297fa4e7e64e9fcdf91e600f28d56cfb.jpg?wh=1280x917)
不同的限界上下文也就是不同的子系统或者模块之间会有各种的交互合作。如何设计这些交互合作呢DDD使用**上下文映射图**来完成,如下:
![图片](https://static001.geekbang.org/resource/image/b6/a6/b661e8bfabe968794ae80d01b81f73a6.jpg?wh=1920x593)
在DDD中领域模型对象也被称为**实体**。先通过业务分析,识别出实体对象,然后通过相关的业务逻辑,设计实体的属性和方法。而限界上下文和上下文映射图则是微服务设计的关键,通常在实践中,限界上下文被设计为微服务,而上下文映射图就是微服务之间的依赖关系。具体设计过程如下图:
![图片](https://static001.geekbang.org/resource/image/cc/31/cc4aac65f24bfcfd2f965973070f3331.jpg?wh=1920x1279)
首先领域专家和团队一起讨论分析业务领域确认业务期望将业务分解成若干个业务场景。然后针对每个场景画UML活动图活动图中包含泳道通过高内聚原则对功能逻辑不断调整使功能和泳道之间的归属关系变得更加清晰合理。**这些泳道最终就是限界上下文,泳道内的功能就是将来微服务的功能边界,泳道之间的调用流程关系,就是将来微服务之间的依赖关系,即上下文映射图。**
但是,这个状态的泳道还不能直接转化成限界上下文。有些限界上下文可能会很大,有些依赖关系可能会比较强。而一个限界上下文不应该超过一个团队的职责范围,因为根据康威定律:组织**架构决定系统架构**,两个团队维护一个微服务,必然会将这个微服务搞成事实上的两个微服务。所以,我们还需要根据团队特性、过往的工作职责、技能经验,重新对泳道图进行调整,使其符合团队的职责划分,这时候才得到限界上下文。
在这个限界上下文基础上,考虑技术框架、非功能需求、服务重用性等因素,进一步进行调整,就得到最终的限界上下文设计,形成我们的微服务架构设计。
我们将遵循上述DDD方法过程对Udi微服务进行重新分析设计并进行系统重构。
## Udi DDD 重构设计
首先分析我们的业务领域,通过头脑/事件风暴的形式,收集领域内的所有事件/命令,并识别事件/命令的发起方即对应的实体。最后识别出来的实体以及相关活动如下表:
![图片](https://static001.geekbang.org/resource/image/09/29/09e53a303016b07a49da69aec0145729.jpg?wh=1920x1412)
基于核心实体模型,绘制实体关系图,如下:
![图片](https://static001.geekbang.org/resource/image/4f/81/4fc8ebe70deae09e043194b27e15ed81.png?wh=768x493)
在实体间关系明确且完整的前提下,我们就可以针对各个业务场景,绘制场景活动图。活动图比较多,这里仅用拼车场景作为示例,如下:
![图片](https://static001.geekbang.org/resource/image/86/1b/8611bb0513340f6a0a46946ba34a8f1b.png?wh=1254x2248)
依据各种重要场景的活动图,参考团队职责范围,结合微服务重用性考虑及非功能需求,产生限界上下文如下表:
![图片](https://static001.geekbang.org/resource/image/59/93/597f2ccf6576d1acc4868600b66eea93.jpg?wh=1920x1709)
针对每个限界上下文进一步设计其内部的聚合、聚合根、实体、值对象、功能边界。以订单限界上下文为例:
![图片](https://static001.geekbang.org/resource/image/99/e9/99452cc01307c21866a662ae1fe37ce9.jpg?wh=1920x1591)
上述订单实体的属性和功能如下表:
![图片](https://static001.geekbang.org/resource/image/ec/29/ecd11ebbec719a3b1beed7d7f8103029.jpg?wh=1920x954)
最后,在实现层面,设计对应的微服务架构如下图:
![图片](https://static001.geekbang.org/resource/image/e3/41/e3efb97e6376f10774d99dd8d5cd8b41.png?wh=1188x998)
这是一个基于领域模型的分层架构,最下层为聚合根对象,组合实体与值对象,完成核心业务逻辑处理。上面一层为领域服务层,主要调用聚合根对象完成订单子域的业务,根据业务情况,也会在这一层和其他微服务通信,完成更复杂的、超出当前实体职责的业务,所以这一层也是一个聚合层。
再上面一层是应用服务层,将实体的功能封装成各种服务,供各种应用场景调用。而最上面是一个接口层,提供微服务调用接口。
## 小结
领域驱动设计很多时候雷声大雨点小说起来各种术语满天飞真正开发实践的时候又无从下手。这节的案例来自一个真实落地的DDD重构设计文档你可以参考这个文档按图索骥应用到自己的开发实践中。
既然说到“按图索骥”那我认为也有必要在这一节的最后帮你画一个更有概括性的DDD重构路线图我们把使用DDD进行系统重构的过程分为以下六步
1. 讨论当前系统存在的问题发现问题背后的根源。比如架构与代码混乱需求迭代困难部署麻烦bug率逐渐升高微服务边界不清晰调用依赖关系复杂团队职责混乱。
2. 针对问题分析具体原因。比如:微服务 A 太庞大微服务B和C职责不清团队内业务理解不一致内部代码设计不良硬编码和耦合太多。
3. 重新梳理业务流程明确业务术语进行DDD战略设计具体又可以分为三步。
a. 进行头脑风暴,分析业务现状和期望,构建领域语言;
b. 画泳道活动图、结合团队特性设计限界上下文;
c. 根据架构方案和非功能需求确定微服务设计。
4. 针对当前系统实现和DDD设计不匹配的地方设计微服务重构方案。比如哪些微服务需要重新开发哪些微服务的功能需要从A调整到B哪些微服务需要分拆。
5. DDD技术验证。针对比较重要、问题比较多的微服务进行重构打样设计聚合根、实体、值对象重构关键代码验证设计是否合理以及团队能否驾驭DDD。
6. 任务分解与持续重构。在尽量不影响业务迭代的前提下,按照重构方案,将重构开发和业务迭代有机融合。
## 思考题
你认为DDD最大的价值是什么如何才能成功落地DDD
欢迎在评论区分享你的思考,我们共同进步。
> 【编辑温馨提示】4月10日12点前提交[期中测试](http://https://time.geekbang.org/column/article/495175)作业,有机会获得老师精心准备的奖励哦~