24 践行组装式架构设计,低代码平台的三个时态
你好,我是陈旭。
前面我们花了3讲的篇幅详细介绍了低代码平台如何帮助应用做测试,至此,我们的低代码平台的功能已经变得很丰富,涵盖了应用开发、运行、测试等主要功能,基本上覆盖了应用开发的全生命周期,我们的低代码平台也不可避免地变得臃肿起来。所以,是时候来对低代码平台的架构做一下治理,避免其过度臃肿而影响它的长期演进。
和之前各讲类似,本讲的内容也来自我们的低代码平台Awade的经验,请你记住这个名词,本讲的其他内容将直接使用Awade这个称呼。同时,我会使用“低代码平台”这样的名词来泛指包括Awade在内的各种低代码平台/工具。
为啥要拆分?
可组装技术是一种系统设计理念,强调通过组合独立的、可互换的组件来构建复杂系统。其核心原则包括:
- 模块化:系统由独立的模块组成,每个模块有明确的职责和边界。
- 互操作性:模块通过标准化接口进行通信,确保它们可以在不同环境中无缝集成。
- 复用性:模块设计为可以在不同项目和场景中重复使用。
- 灵活性:通过组合不同的模块,能够快速响应变化的业务需求。
云原生并不是一个新技术,从2013年Docker发布,拉开了单体软件架构变革的帷幕,到2019年Service Mesh技术成熟,软件架构微服务化设计也深入人心,践行者获益良多,再到2022年云原生技术逐渐成熟。至今,云原生技术早已深入人心。下面这个图展示了云原生技术的关键里程碑。
云原生方法提供了构建和运行现代应用程序的技术基础,而可组装技术提供了模块化和灵活性的设计原则。两者结合,使得构建、部署和管理复杂系统变得更加高效和灵活。
区区几百字就介绍完了近十年来软件系统架构理念和技术的沧海桑田,而可组装原则正是我们的这一讲对逐渐臃肿的低代码平台做架构治理的重要指导思想,我们希望通过采用可组装原则对我们的架构做治理,使得它更适合于其他采用云原生技术的软件系统组装使用,甚至直接采用云原生技术来改造低代码平台。
这是一个包含海量内容的话题,但咱今天这一讲不说大道理,我直接把我们的架构治理结果和主要方法介绍给你,不会涉及太多理论。
有哪些时态?
和多数陈年软件系统相似,Awade最初的架构也是基于典型的单体软件架构而来,从2018年至今,经过多年的演进,Awade的功能已经涵盖了应用开发、运行、测试等应用开发的全生命周期。对于低代码平台来说,开发和维护应用就是它的业务,而应用开发的生命周期就是这个业务的现成抽象。因此,我们以应用的生命周期作为低代码平台的业务流拆分依据,是再恰当没有的了。下面这个图概要描述了这一点。
根据这根时间轴上的主要节点,很明显的,我们可以将低代码平台拆分为3个部分,分别是开发时、测试时、运行时,我们称之为三时态。接下来我们看一下三时态的主要职能。
开发时
在开发时,开发人员使用低代码平台的可视化开发工具来创建新应用或对现有应用进行迭代演进。这一阶段的重点是通过拖拽组件、配置业务逻辑和数据模型,快速构建出功能齐全的应用。开发人员可以利用平台提供的模板和预置功能,加速开发过程,同时保证代码质量和一致性。这个专栏的前20讲,基本都是在讨论这个时态的各个功能,这是低代码平台的核心功能。
需要注意的是,如果在设计低代码架构时未考虑开发时和设计时分开,等到后期再想拆分就很不容易,甚至几乎是不可能拆分了,我在第3讲、特别是第6讲都有详细介绍设计方法,你可以回去温习温习。
测试时
在测试时,新功能完成后需要进行功能测试,以确保其按预期工作。同时,还需对已有功能进行回归测试,以检查新功能的引入是否对现有系统造成影响。这一阶段利用自动化测试工具和手动测试相结合的方法,确保应用的稳定性和可靠性。这个专栏的 21~23 讲从理论到实践详细讨论了如何实现自动化生产应用自动化测试用例的方法。
运行时
在运行时,新功能经过验收和测试后正式上线运行。这一阶段涉及将经过验证的代码部署到生产环境,并确保系统的平稳过渡。运维人员负责监控应用的运行状态,及时处理突发问题,保障应用的高可用性和性能。运行时还包括对应用的持续优化,通过分析用户反馈和系统性能数据,不断改进应用功能和用户体验,以满足不断变化的业务需求和用户期望。
考虑到本专栏至今没有专门针对低代码平台的运行时如何实现做讲解,这一讲我会简要补充一点这方面的内容。但在这个小节里,我们暂时保持专注在三个时态及其关系上。
很显然,这三个时态是可以组合的,我们可以根据不同的应用场合定义多个套餐包,从而可以用最小资源需求满足多样化的系统部署需要。
- 套餐一:设计时+测试时+运行时,这是全家桶,适用于重度低代码应用场合,Awade的这个套餐一般是在公司内所谓效率工具来部署的场景,也可以将这个套餐作为一个商用选项之一。
- 套餐二:设计时+运行时,Awade的对外商用场景一般都采用这个套餐,对外商用一般只辅助用户通过零代码方式开发一些简单场景,一般不需要测试直接上线,拆掉测试时是为了简化开发流程,提升易用性。这个套餐也可以作为其他低代码平台的基础商用选项之一。
- 套餐三:仅运行时,这是Awade早期最主要的商用场景,截至现在仍有许多这样的商用场景,应用在公司内基于全家桶套餐开发,部署到外场时,外场只需要安装运行时就足够了,运行时最极限时只需要0.1个CPU、50M内存就可以跑起来。
- 套餐四:仅测试时,基本只有DevOps流水线才有这样的需求。应用在设计时里开发出来后,提及到代码仓库,DevOps流水线在代码仓库取出应用代码装入测试时进行自动化测试并输出测试覆盖率等各种报告。
- 套餐五:测试时+运行时,使用场景和套餐四基本一样,差异在于应用的自动化测试需要依赖外部业务系统或数据。
设计时一般无法独立部署,因为开发时的试错需要至少有运行时的辅助。
以上五种套餐实际上是可组装原则(模块化、互操作性、复用性和灵活性)的一种实践,基于模块化原则,以及采用应用开发生命周期对低代码平台的功能抽象,我们将单体Awade拆分成了三个时态,也就是拆分出了三个模块。这些模块之间可以相互操作,从而可以组合成套餐以用于不同的业务场景,提供了从简单到复杂、从内部工具到商用产品的多样化解决方案,这是满足复用性和灵活性原则的体现。
如何拆分?
前面我说过,如果在设计低代码架构时未考虑开发时和设计时分开,等到后期再想拆分就很麻烦了。
简单回顾一下第6讲与这一讲相关的内容。在第6讲中,我把代码生成器与编辑器之间的关系,大致分为如下几个层次:
- Level 1:没有代码生成器的概念,或者极其粗糙。
- Level 2:有相对独立的模块用于生成代码,但该模块与编辑器耦合严重。
- Level 3:代码生成器与编辑器基本相互独立,具有同等地位。
- Level 4:插件系统与生态,编译器必须再次抽象才能实现插件系统。
抽象程度至少要达到Level 3才比较容易对低代码平台做拆分,在Level 1和Level 2抽象层级下,设计器和运行时严重耦合,如果要拆分,就必须先把耦合解开,但我认为这几乎不可能实现,但凡有过大型软件系统开发或维护经验的人都知道,解耦合的过程往往是拔出萝卜带出泥、牵一发而动全身。不仅难度大,更关键是这个过程不直接产生业务价值,难以获得足够的资源(人力/时间)来实施,最终一般都是不了了之。
如何对代码做解耦优化,超过了这讲甚至是整个专栏的范畴了,我就不展开了。
大部分的单体低代码平台的代码关系,可以采用下面这个示意图来描述,三个圈代表着不同的时态功能,他们之间有重叠的部分。
现在要做的第一步,就是将各个时态的硬耦合部分拆开,方法也不复杂,就是把重叠的部分抠出来作为库,这样重叠的两个时态只需要对这些库产生依赖,而时态之间没有直接依赖了。拆开之后的逻辑关系图如下:
实际上,在一些注重架构治理的单体架构软件中,应该已经采用了这样的逻辑关系来组织各个功能和基础库了。因此,截至现在,也实际上才完成了软件架构治理应该做的工作而已。但是在这个逻辑关系基础上,要将大单体拆分开,就很简单了。
现在稍微修改一下软件的编译脚本,将编译制品从一个单体软件拆分为3个单体软件,在编译之后,各个时态的组成则是下图所示。请注意,将单体软件进一步构建为Docker容器,是不在这讲的范围内的,但往往这个动作是必须的。
最后,还有一个工作要做,那就要解除单体模式下各个时态的直接代码调用,从而使得各个时态彻底松耦合。这部分工作我将以运行时为例来说明,请继续学习接下来的运行时的设计和实现部分。
运行时设计和实现
之前之所以没有计划增加专门的一讲来说明运行时的设计和实现,是因为这往往是低代码平台与存量业务运行时强耦合的部分,几乎每个业务运行时都不一样,即使我把Awade与我们的云平台底座对接的方式给出来,我相信对你也没有太多帮助。
但即使是这样,运行时也还是有一些共同部分的,我现在就把这部分共同的部分简要说明一下,顺便也说清楚如何避免单体模式下各个时态所采用的直接代码调用的方式,以最大限度保持各个时态松耦合的效果。
不难理解,直接代码调用是一种硬依赖,这种依赖往往早在软件编译的时候就需要我们来维持。解决的办法也不复杂,那就是改为API调用或者事件驱动的方式,这两种方法都可以实现编译时无感,仅在运行时产生作用,因此这是软依赖。这种关系让软件开发时很容易打桩和模拟,在编译时无感,即先编译哪个后编译哪个无所谓。因此一般来说,软依赖的方式就可以认为是松耦合关系了。
我建议是采用HTTP协议走API调用的方式来实现软依赖,现在基本上所有的基础软件都对HTTP协议提供了良好的支持,同时对浏览器也十分友好。当然,如果你有条件使用到Kafka或者MQ这样的基础设施,那采用事件驱动方式也不失为是一种好的选择。如本小节开头所说的,具体如何选择,与你的低代码软件运行环境有很大的关系。
小结
这一讲本身实际上是对前面20多讲内容的一个阶段性总结,前面20多讲的内容,我们基本实现了一个功能完善的低代码平台,虽然有的点讲解得还不足够详细,但基本可以覆盖应用开发的全生命周期了。
应用开发生命周期中,开发、测试、运行是三个最关键的环节,这三大功能放在一个单体架构下,这使得我们的低代码平台变得臃肿,为了后续更好地迭代,我们不能放任不管。这一讲采用架构治理的思路,基于应用开发生命周期这个业务流,对低代码平台的业务功能做抽象,抽象的结果是,低代码平台的业务能力可以,也应该被拆解为开发、测试、运行三部分,我们称之为三个时态。
通过将单体软件架构拆分为三个相对独立的时态,我们获得了诸多好处,至少有:
- 各个时态内部的依赖关系得到了治理,使得逻辑关系更加清晰。
- 各个时态各司其职,边界清晰,范围明确。
- 新的软件架构符合最先进的组装式设计原则,我们可以为应用提供5种不同的套餐包,多样化的套餐可以按需部署,更加节约CPU和内存资源,从而降低运营成本。
- 更重要的是,我们的治理使得低代码平台软件架构具有了往云原生架构演进的基础,进而可以获得云原生技术架构的各种技术支持。
之所以说这一讲本身也是一个阶段性总结,那是因为从下一讲开始,咱这个专栏的内容将做一些调整,我们不再只专注于低代码技术,而是要开始思考低代码技术如何与大模型技术结合,并实际做一些实用的功能出来。
那么,当低代码技术和以大模型为代表的AI技术相结合,能迸发出啥样的火花来呢?
思考题
软件架构的抽象方式多种多样,这一讲是从低代码的业务价值的角度做了抽象,显然这不是唯一的抽象思路,你还有哪些对低代码平台的软件架构做抽象的思路呢?欢迎将你的思路留言在评论区,也欢迎大家参与讨论。