从4万行代码降到1.8万,腾讯视频竟然用DDD做架构重构?

5e6c29941c343e95eaa39e97715a21a7.gif

DDD 的指导思想很多时候较为晦涩,与实际业务中的架构设计场景往往较难结合理解。本文通过引入架构映射等方式将二者结合,试图给出一套量化评估方法并通过腾讯视频一起看系统的实践案例说明如何应用。

作者 | 刘啸南      责编 | 夏萌

出品 | 腾讯云开发者

本文将通过腾讯视频一起看系统的架构重构实践,给出一套可供参考的领域建模、架构设计与量化评估准则。

ca778864a4d1acb01905f9b969ba3559.png

领域驱动

1.1 DDD 简介

视频会员部门目前正在进行领域驱动项目,希望借助 DDD 的一些方法论,对会员的整个技术体系做一个梳理。内容作为其中的一个子领域,也希望借助 DDD 的一些方法进行整个体系的建设:

▶︎ 复杂度:既有业务复杂度(涉及播放、购买、活动、内容展示、内容互动等全场景),也有技术复杂度(涉及业务规则、模块众多、请求量大、信息安全等),需要拉通考虑

▶︎ 跨部门合作:目前的会员内容体系,至少涉及会员、直播中台、腾讯云、安平审核等部门,是一个跨部门协作项目。

扫描二维码关注公众号,回复: 17368251 查看本文章

▶︎ 体系梳理:会员业务目前涉及内容展示、内容互动、内容合作与内容创新。

▶︎ 领域模型:本文重点是借助一些领域图,把整个会员内容体系以一个直观的方式呈现出来。

DDD 的核心方法,总结起来是以下四点:

f7fd93fd9a80be7d60a9f3901d7ce3d5.png

1.2 领域建模

在理解产品需求的基础上,从中提取出核心概念,然后建立起核心概念的逻辑结构,概念的逻辑结构即领域模型,通常可以用领域图或 ER 图来表示。领域模型有助于我们以一种抽象的视角来理解复杂业务。

对于复杂大系统,DDD 给出的基本操作方法就是<大分形>,即大而化之、分而治之、形而上之。将这个方法应用在内容体系上,可以做一个初步的领域建模。

27303de695d16b1916247a37d6bfda22.jpeg

 1.3 领域建模-例1

将上面的方法应用在视频会员内容体系上,可以做一个初步的领域建模。针对会员内容体系业务梳理,以内容为核心,共分为4块:

93a1c61042a82a6171a7b1ab2ee8a7ad.png

01ed4082814dabfa8bb64dd093452d43.png

1.4 领域建模-例2

f51e92bbba357f3300b712aea257b193.jpeg

d929739c0e4f51799f0e042b19ee2360.png

软件架构

2.1 定义架构

软件架构,其实没有一个明确定义,但可以通过【结构、架构特征、架构决策和设计原则】来描述。

af1945557e1775b1133c8633a14984de.jpeg

8d1503535d28d1df93b8ce22487f73bb.png

对比而言,架构决策的约束力要比设计原则更强。

59db5a84d7b26f3e0af8f223ace26d22.jpeg

 2.2 架构特征

常见的架构特征有两大类,运营性架构与结构性架构,其定义如下:

c0ec9e92a72e723800e74d7d83627bbb.jpeg

 2.3 架构特征

除了上面定义的那些运营性与结构性架构特征外,我们过往的系统开发实践中,似乎一直忽略了很重要的架构特征,即“模块化”,这一架构特征可以看做是隐式的架构特征。

下面一段话引用自《软件架构》,很好地解释了模块化的概念:

关于软件架构的用语中有95%以上都在称颂“模块化”,而关于如何实现“模块化”的用语却少之又少。-Glenford J.Myers(1978年)

不同的平台提供了不同的代码重用机制,但是所有平台都支持以某种方式将相关代码分组到模块中。虽然模块的概念在软件架构中是通用的,但一直没有统一的定义。从 Myers 的引言中可以看出,这不是一个新问题。

在开发平台中了解模块化及其多种形式,对于架构师来说至关重要。分析架构的许多工具(例如度量指标、适应度函数和可视化)都依赖于这些模块化概念。模块化是一种组织原则。如果架构师在设计系统时没有注意各个部分是如何连接在一起的,那么最终创建的系统必将存在众多问题。从物理学的角度来看,软件系统是对趋于熵(或无序)的复杂系统进行建模,必须将能量添加到物理系统中以保持秩序。软件系统也是如此:架构师必须不断消耗精力以确保良好的结构稳定性。

保持良好的模块化体现了我们对一种隐式的架构特征的定义:几乎没有项目要求架构师去确保良好的模块化划分并以模块化为主题与项目成员沟通,而可持续的代码则需要秩序和一致性。

我们使用模块化来描述代码的逻辑分组,该逻辑分组可以是面向对象语言中的一组类也可以是结构化语言和函数式语言中的函数。大多数语言都提供了模块化的机制(Java 中的包(package),NET 中的命名空间(namespace),等等。开发人员通常使用模块作为将相关代码分组在一起的一种方式。

模块化对架构师非常重要,研究人员创建了各种跨语言的度量标准来帮助架构师理解块化。我们重点聚焦三个关键概念:内聚性、耦合和共生性。

6816ad1ab84ca87e1e0417c2aed7b35a.jpeg

内聚性及其度量:

72fd5e0540a9986a488f0e7339ba593c.jpeg

耦合性及其度量:

46d88f886a1d0460bf3f5face9971e0f.jpeg

共生性及其度量:

9beddf8a95d029bbe5c8ecde1e9120b0.jpeg

统一耦合性与共生性:

20ef8d358700117b218fdb061442a52a.jpeg

 2.4 架构风格及度量

平时的实际工作中,常见的是以下六种架构:

92e594fa50b5b83404c4b9bfe0b5b5c9.png

7fa1911c3a2b700aa978ae62f2d1cc42.jpeg

我们可以通过以下特征来对各个架构进行评级:

f2d92ca2aa14e77dad63c61462d30c95.jpeg

f385b35aa9382ff782a534500e7fefed.jpeg

3d3b5c17aa602bfaf6a61e453ba81a73.png

架构映射

3.1 DDD 过程模型

作为开发,我们工作的本质就是把一个产品需求转化成一个可以运行的系统,中间涉及产品设计、领域建模、架构设计、详细设计、代码编写、测试等步骤。

如果不考虑编码过程,就 DDD 的基本过程进一步展开来说,大体是以下三点:

bac758b1bac108aba7849902e83e5652.png

为便于理解,以下是我自己梳理的一个 DDD 过程模型:

05cb13abe52b4c9c2957d09d44843594.jpeg

3.2 架构映射

过往工作,大部分时候,我们一上来就开始系统架构设计,并没有领域建模这一步,究其原因主要是:

a38b42477da7997bc00101a2dfd6b6ce.png

只有对于一些特别复杂的业务,在理解产品需求的基础上,从中提取出核心概念构成领域模型,然后把领域模型中的概念分解到系统架构中的各层和各模块中去。架构映射即是把领域模型映射到系统架构。

架构映射,以视频一起为例:

f7b755fb7d654d890e5d83ebae3f8f7d.jpeg

bbd1788fd542fc4e1f3ed7319efb450a.png

架构映射

4.1 DDD 与重构

领域驱动设计的重点在系统设计阶段,但领域驱动设计同样将重构作为重要内容。某种程度而言,软件工程师仍然是手工业者,软件开发仍然没有银弹,重构仍然是软件在生长过程中不可或缺的调校手段。

因此,我们也不用迷信什么银弹,也不必忌讳什么过度设计与设计不足,通过多次重构迭代,让正确的设计逐步显现。

b273316c45c7d68be92e9b01fefd9cd2.jpeg

4.2 一起看的技术债

视频一起看,经过几年不断的功能开发,已经堆砌了比较多的模块,而且是由两个跨部门的团队一起开发的,整体上缺乏统一设计,技术债务积累较多,亟需来一次整体重构。

6ce768f7c2dbd35e91288641f420483f.jpeg

4.3 发起重构

整个重构规划如下:

5cdcdf984eab7bceb039c3143e8c26d8.jpeg

4.4 架构重构

8a598245c7ba7658a4c7af53a5e880df.png

老系统架构图:room_adapter 是结构瓶颈。

8d3e02a48ba21fc4fa346d45dd89533f.jpeg

具体的解决办法分为两点:

▶︎ 架构分层明确,模块间解除了不必要的耦合;

严格遵守分层架构,上层服务可以调用下层服务,下层服务禁止调用上层服务,下层服务不关心业务逻辑。如果上下层服务需要发生交互,通过逻辑解耦的方式进行,如消息队列/中转。

▶︎ 重新划分领域,合理分布功能,避免模块太大或太小;

通过重新调整领域划分,使得功能分布上更为合理,各个模块专注于专属领域相关能力,更利于后续开发和维护。

067980e8ee5ece1cd91ed8abe1485bae.jpeg

重构前后架构对比:

d2695bffc705b04220fa125cc2153b42.jpeg

4.5 逻辑重构

这部分涉及较多业务逻辑,方法上主要是以下两点:

▶︎ 剥离非核心逻辑子域,保障业务主流程的可读性。

▶︎ 提取公共组件,去除代码拷贝,提供复用性。

下面是一个房间重构的例子:

以 room_adapter 为例,因为属于业务适配层,掺杂了太多的特殊业务逻辑,导致代码可读性,可维护性很差;

梳理服务流程,将非核心的业务逻辑抽离为业务子域,封装为 trpc 拦截器,保障业务主流程的可维护性。

c0a5dea09836001ee7c50108f2c5fd03.jpeg

在进一步分析系统重构完之后的效果时,我们先补充一点架构方面的知识点,这可以更方便地以一种量化的方式来评估重构效果。

3be139b213826381b941ffe712cbd623.png

效果评估

这部分通过介绍视频会员今年做的一个系统重构项目,结合上面的知识点,对这个项目做一整体量化分析。

5.1 问题与目标

一起看房间系统,这是一个重构项目,如果说明这个项目的意义,通常的结构就是“问题-目标-方案-效果”。

bc374495fe7acfdef095120603407675.jpeg

5.2 效果的定性说明

在系统架构层面,重构前后的架构对比:

1b9f5e5bea652375305bad3c6c0eb484.jpeg

重构前后,到底有什么效果,我们可以定性来说,比如:

▶︎ 重构前:单体架构,难维护难扩展;

▶︎ 重构后:微服务架构,内核简单,易于扩展。

▶︎ 重构前:功能堆砌,没有整体规划;

▶︎ 重构后:架构清晰,产品功能有序扩展。

对于非技术同学(产品、运营)来说,定性说明可能更容易理解,但定性说明本身比较随意,任何一个重构都可以讲出以上这些好处,所以没有区分度。

ea8dbbdcfbc39786075807076aa5be7b.jpeg

5.3 效果的量化分析

在定性说明的基础上,有必要做进一步的量化分析,这样效果就更有说服力。

  • 在架构层面,重构前后的改进

我们可以利用上一节的架构风格及特征参数,从而能够做出量化分析,这样得出的结论就更有说服力。

▶︎ 重构前系统:可以看做是微内核模式;

▶︎ 重构后系统:可以看做是微服务模式。

这样我们就可以对比重构前系统与重构后系统的区别,可以看到重构后的系统在可测试性、扩展性及模块化方面都有明显改进。

44158d55d0352b5af24e5917dc45d8cd.jpeg

  • 在模块层面,重构前后的改进

▶︎ 重构前:room adapter 模块包含了太多低耦合逻辑,除房间业务逻辑外,还有定时任务、房间回调、进房审批鉴权、消息发送。

▶︎ 重构后:room_adapter 模块只保留房间业务逻辑,其他逻辑按高内聚低耦合拆分为独立小模块。

07790e42787b97aef9358ff49f497b2f.jpeg

  • 在代码层面,重构前后在四个维度(代码规范、千行问题、圈复杂度、千行超标复杂度)上的改进:

8847ab497bd78870f753eb4d3495f833.jpeg

  • 代码量方面,重构前代码量约 4w 行,重构后 1.8w 行,降幅55%

2e5425fdac79e151fa614f566c141916.jpeg

  • 性能方面,重构后 Top4 接口耗时都有明显优化,平均耗时降低45%左右

d05883ff503335d6663923e2774bac81.jpeg

  • 成本方面,通过下图趋势线可以看出,Top4 成本均呈下降趋势,其中 PCG-123 和日志服务 CLS 成本降幅明显,达到40%左右

2a12a68bb6391a59428a41bd49cffc92.jpeg

5.4 设计关键点

这里不准备长篇大论,通过以下几个关键设计点,从整体上了解一下【视频一起看房间系统】:

  • 系统架构

e67570df38d8d870093aa9326fa8eb91.jpeg

  • 核心数据结构

5fe1ad62212c89a9ede09a8ef33990bf.jpeg

a9703e9335945aaae364a9a69fb3f432.jpeg

  • 技术与业务指标

20f2947928b3a459f03e054bf39eb7fd.jpeg

关键技术指标:请求量 QPS 峰值:约3万/QPS,Redis 存储占用量:11.16GB;

关键业务指标:线上房间总数:约6k+,其中用户房约3K,系统房3k,运营房0.25k。

29cdc830bb65e496d441f8d7c5078d1f.png

几点说明

做开发多年,一直有一些问题困扰着自己,本文主要想对以下几个问题做出自己的理解和探索:

▶︎ 我们在描述架构时,通常会采用高性能、扩展性、可观察性等几个点,缺乏一个整合的量化分析框架,本文试图解决这一问题,让不同的架构风格具有可对比性;

▶︎ 领域建模和架构设计之间的关系是什么,本文提出“架构映射”的概念,试图建立起两者之间的转换关系;

▶︎ 架构理论与架构设计的实践相结合,试图给出一套可操作、能落地、能量化对比的设计方法;

▶︎ 做过很多重构,重构前后系统效果的说明,通常会采用定性描述,或者列举单点说明,比如性能、扩展性等,如何以一种可量化的整合的方式说明重构的效果。

本文经授权转载「腾讯云开发者」,点击「阅读原文」直达原文。‍‍‍‍‍‍

22060a5613143e9fe3c88acdc4427dd5.gif

猜你喜欢

转载自blog.csdn.net/dQCFKyQDXYm3F8rB0/article/details/135163309
今日推荐