3

多子类型业务架构演进

 2 years ago
source link: https://yanhaijing.com/program/2021/07/22/decoupling-the-large-subtype/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

多子类型业务架构演进 原创 编辑

22 July 2021
号外号外:猿辅导招聘前端,后端,客户端啦!地点:北京!!!快点我查看

最近文章写得少了,都是因为在填耦合架构的坑,人生苦短,填坑不完,o(╥﹏╥)o,对如何治理耦合架构感兴趣,可以看我之前的文章——比耦合架构更好的架构

551.png

好不容易从耦合坑里爬出来,又踩到了多子类型的坑,大坑套小坑,放眼望去,我太难了o(╥﹏╥)o

557.png

本文结合真实项目经验,介绍多子类型的架构演进,将会介绍四种架构的优缺点和适应场景,分别是耦合架构,正交架构,低代码架构,低组件架构

故事是这样的,某一天小颜同学接🤚了一个业务M,小颜同学后来才知道,这个业务在团队内部被称为“屎山”,也就是传说中的多年迭代,多人接手,多种组合,缺少文档的烫手山芋,简称三多一少

M系统是典型的多子类型,M系统管理M资源,M资源有100多个子类型,每个子类型都有自己的逻辑,大家有类似业务同学可以感同身受一下

M系统的现状是100+子类型,2万行代码,但这2万行代码是写在一起的,这个文件内部有2000个分支,OMG,每次打开这文件,编辑器都要卡一会,就给某个子类型加一个逻辑,看了一天还不知道该往哪加,下图是小颜同学下班时的心情

558.jpeg

就这样熬了一周,小颜同学深感写代码就像在扫雷,每天都在想,应该改哪里,改了会不会触雷,我的天啊,代码成功了

559.png

比代码出错了不知道为什么,更可怕的是代码成功了,也不知道为什么;那么问题来了,如果是你面对这种情况该怎么办呢?

  • 相似又不同的类型
  • 快速增长的业务
  • 我的代码该如何写?

像这种把子类型的逻辑都写在一起的模式,我称其为耦合架构,需要注意的是耦合也十分级别的,同样是耦合松耦合和紧耦合也是不同的,如果对于耦合的分类感兴趣,可以看下我的另一篇文章——图解7种耦合关系

PS:我的另一篇那文章也提到了耦合架构,感兴趣的可以看看——比耦合架构更好的架构

一般听到耦合,第一反应就是解耦,但是先思考一个问题,我们的代码是如何失控的?

系统很有可能是这样演进的,在业务开始时

  • PM提了一个子类型需求M1,M1包含标题和音频
  • PM提了一个新的子类型M2,在M1的基础上多了一个视频
  • PM提了一个新的子类型M3,在M1的基础上多了一个图片

写下第一行代码的同学很快就完成了M1的开发,当面对M2和M3时,优先会考虑复用M1的逻辑,极有可能选择在M1中加上M2和M3的逻辑

整个系统就这样慢慢的演进,随着时间的积累,慢慢走向失控

  • PM提了一个子类型M4
  • PM提了一个子类型M50
  • PM提了一个子类型M100

终于有一天变成了现在这样,也许早就有同学发现了系统的问题,但未能及时解决问题,相信不少业务会有这种年久失修的问题

现在整个系统紧紧耦合在一起,巨量代码和逻辑交织在一起,整个系统蕴藏着巨大危机,开发效率低下,几乎无法并行开发,因为没法解决代码冲突啊;维护风险很高,真正的牵一发而动全身

整个系统已经到了岌岌可危的处境,急需一个人挽狂澜于既倒,扶大厦于将倾

552.jpeg

如果想解决问题,首先我们要理清问题,这个系统目前存在如下问题:

  • 新人上手成本(看不懂)
  • PM让我修改旧类型(改不动)
  • PM说这个可以复用另一个(理不清)

但是这些都只是问题导致的结果,导致这些问题的根源是,一个类型的需求,要面对全部类型的逻辑,系统复杂度:O(n^2)

其实前人们也一直在努力解决问题,只是努力的方向是,在现有架构下修修补补,比如总结文档,代码上面进行归类抽象等

正交架构的思想其实非常简单,既然要解耦,那就直接把每个子类型的逻辑分开实现不就好了吗,代码上彻底隔离

正交架构其实就是分治思想,分治思想提倡将大的问题,分离成多个小问题,从而分别解决,在多子类型系统中,子类型就是一个绝佳的分治媒介

正交架构带来的直接好处就是,子类型解耦了,子类型内部逻辑是自治的,相互之间没有关联,从而使每个子类型的复杂度降低了,虽然系统里的逻辑和代码量并没有减少,但系统的整体复杂度降低了,在我们这里例子里面,用正交架构替换耦合架构收益如下:

  • 复杂度 n^2 => n,增加子类型时,系统复杂度线性增长
  • 代码量 2个数量级,修改一个子类型时,代码量由万行级别 => 百行级别
  • 分支数量 3个数量级,系统内部逻辑分支由千级别 => 个位数

既然正交架构整么好,那为何不用正交架构替代耦合架构呢?没错,如果是新系统的话,我建议你从一开始就选择正交架构,但对于存量系统就很麻烦,因为有巨大的历史包袱,在这种情况下,如果时间紧迫,我建议先从新类型切到正交架构,历史包袱先保留不动,正可能需要一点小的代码设计才能实现,但我相信难不倒你的

接下来说说正交架构的问题,通过把子类型的代码分开,带来一个明显的问题,在耦合架构中,类型之间的公共逻辑和组件是复用的,但在正交架构中是重复的,特别是对于复杂的逻辑和复杂的组件,问题尤为明显,一旦这部分功能要统一修改时,那可能要在每个子类型都要修改下

这个问题可以通过将公共组件和公共逻辑抽象出来的方法来解决,一般如果2个子类型重复的部分,就应该提取出来,在业务迭代中,如果你想复用另一个类型的功能时,就是抽象的合适时机,一般PM会提醒你这个事情的,PM可能的对话如下

小颜同学,这个新类型的这块就和之前的一样,我就不用在描述了吧 —— 传说中的一句话需求

关于公共组件化,还有一个问题不得不提,有两种抽象公共组件思路,一种是,组件提供配置,不同子类型使用组件时,配置不同开关;一种是,把子类型作为参数,传递到公共组件,组件内部判断子类型实现不同逻辑;对于前一种,我称为纯组件,对于后一种,我称为非纯组件

我发现不少同学在提取组件时,会写出来非纯组件,其实非纯组件只是把代码物理隔离了,逻辑上并没有隔离,在你的公共组件里其实还是一个小型的耦合架构,所以建议大家选择纯组件

除了上面的非纯组件问题,正交架构还有个最大的问题就是组件化是可选的,这其实给拷贝代码提供了可能,让重复代码有继续存在的温床,提取组件这个事情就是一个最佳实践,属于弱约束的事情,而弱约束一般只能通过代码评审发现……

后面可能发生的事情大家都懂了吧

低代码架构

整个系统在正交架构下跑了一段时间,顶住了业务的压力,但我一直在思考有咩有更好的架构,最近低代码如火如荼,多子类型有咩有转低代码的可能?

说来也巧,刚好业务要对系统进行大的改造重构,在这个过程中,我落地了低代码架构,对于多子类型业务,如果其类型之间存在一些相似或重复部分,那么可以考虑低代码架构

对于单个子类型来说,只需要将正交架构中的代码,全部替换为配置,配置可能会对应一套DSL语言,需要搭配一套渲染器,渲染器读取配置,将每个配置渲染成组件库里的组件,就形成了完整的闭环

我发现低代码刚好解决了正交架构系统可能存在的两个问题,低代码架构强制系统必须全部组件化,这就避免了正交架构可选的组件化可能带来的同样逻辑,实现不同问题;同时低代码架构下,强制要求组件面向配置开发,这恰巧杜绝了非纯组件的问题

低代码这一套铺下来,整个系统的复杂度和子类型数量的解耦,整体收益如下:

  • 复杂度:O(N) => O(常数),N是子类型数量,常数和组件数量相关
  • 开发效率:80% ↑,新类型大概率不需要开发了
  • 强制纯组件化

但低代码也不是没有缺点的,首先整个系统的架构会比正交架构复杂,开发难度更大;低代码系统会增加额外的开发工作,比如配置系统,DSL语言和渲染器的开发等;如果遇到系统不支持的组件,需要额外的开发成本,且开发成本会大于这个组件在正交架构下的开发

低代码架构中有几个关键的技术难点,这里简单提一下

  • 绑定数据能力
  • 组件可扩展能力
  • DSL可扩展能力
  • 校验如何设计
  • 组件设计原则

如果对低代码架构的实现细节感兴趣,可以继续关注我的后续文章,您的回复和打赏,是我继续写下去的动力

低组件架构

整个系统在低代码架构美好的演进了一段时间,但很快遇到了一些困难

首先是业务的多样性,导致了某些组件的配置爆炸式增加,有几个典型的组件有几十个开关,其自身的复杂度也需要关注了

有些子类型其功能不具有复用性,对于这种,我们扩展了类型组件的支持,也就是给这个子类型开发一个组件,其逻辑都在一起,就和正交架构的实现区别不大了

最大的问题,有些类型,其校验逻辑和联动关系非常复杂,通过配置化来实现这些联动和校验时越来越困难,维护的同学表示很难受

关于上面的问题,我思考了许久,提出了低组件架构,这个名词是我发明的,低组件架构其实是融合了正交架构和低代码架构,博取两家之长,避开两家之短

拿表单来举个例子,我们的程序其实是分成两部分的,首先是组件的渲染,也可以理解为静态UI的展示,这一部分其实是比较容易通过DSL来表示的;在UI背后还有逻辑层,包括组件的联动,校验等逻辑,这一部分逻辑存在DSL困境

其实我们写的代码,比如js,html等就是通用的DSL,既然如此不如换个思路?一个子类型包含哪些组件,组件包含哪些参数,通过低代码架构来实现;组件背后的逻辑层,通过正交架构来实现

低组件架构,融合了两种架构,避免了正交架构中的组件发散问题,同时也避免了低代码架构中的DSL爆炸问题

不过这个架构,我只是做了纸面上的推演,并未落地,纸上得来终觉浅,希望能给同学们一起启发,如果有同学有尝试,欢迎交流

综上,我们介绍了多子类型系统的4种架构,其实4中架构都能实现需求,但其思想却有很大不同,下面同不同层面做个对比

首先从子类型数量和系统复杂度方面来对比下,低组件架构其实是低代码架构的一个变种,所以此处不单独列出

570.png

再来对比下,开发人员面对不同架构时的心智模型

571.png

通过上面的介绍和对比,相信同学们对4种架构的定义和区别都有了自己的认识,其实架构没有好坏之分,只有适合不适合,下面从我的认知,给大家总结下不同架构适合的不同业务场景

架构 适应场景 耦合架构 无,除非你想离职了,不过害人终害己,天道好轮回 正交架构 子类型小于30个的场景,子类型之间区别很大时 低代码架构 逻辑不重,子类型较多时,子类型迭代较快 低组件架构 同上,但逻辑较重时

最后,希望本文的内容,可以帮助大家在多子类型业务中找到出路,感谢大家阅读,^_^

560.jpeg

原文网址:http://yanhaijing.com/program/2021/07/22/decoupling-the-large-subtype/

微信公众号:颜海镜关注微信公众号 颜海镜微信支付二维码赞赏支持 微信扫一扫

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK