前言
我大约从几年前开始就和 LowCode 类的项目打交道, 过去类似 CMS 或者智能运营平台类的项目都可以归纳到 LowCode 的范畴之内, 这几年也曾作为类似项目的主前端架构师, 参与和主导设计项目的落地和演进, 大家熟知的阿里的飞冰也是类似的一种, 还有很多同学公司内部用于开发中后台项目的差不多也是其中的一部分, 就像我前几篇文章讲的, 做一名架构师除了对日常业务的关注和架构经验方法论的总结, 我想最具有成就感的事情应该是参与某一种架构标准的推广和普及
搞开发的应该都了解设计模式, 尤其是 Java 四人帮早年写的经典书籍关于设计模式的介绍, 这些模式非常经典, 我们目前在软件开发中使用的一些编程思路大多数都脱胎于这些模式, 或者是这些模式的变种, 但是设计模式至今已经有很多年了, 而且当下前端开发面对的复杂性和项目本身场景之丰富早已不是几十年前软件工程师能够想象的, 作为从业人员, 我们应该秉持一些单纯的技术理想, 整理和推进更好的架构标准的普及, 帮助后来者在此基础上去创新, 因此我想在这篇文章中对我过去从事 LowCode 相关项目的架构设计做一些尝试, 看能不能总结出一些通用模式, 以帮助各位在从事相关项目的架构设计和开发过程中避免一些重复性的思考, 和采坑.
正文
LowCode 项目的共性
早在我从事编程工作的时候, 前端还尚未被独立出来, 那时候微软主推过一套基于 ASP.NET 的 WebForm 网站可视化开发解决方案, 通过拖拽一些网页组件, 然后绑定数据源就可以非常快速的完成一个具有内容展示, 表单提交的网站开发, 整个过程仅编写少量代码就可以完成, 类似 Window 下的开发体验, 所以你们现在看到的拖拖拽拽很 cool 的这些其实都是 10年前 微软玩剩下的, 当然有时候理念行不行跟时代有关系, 所以即便以微软的技术实力, 也无法在当时那种网络环境下跑通这个模式, 因为前端领域的技术发展实在过于迅猛, WebForm 的这套架构根本跟不上时代的步伐, 最终被淘汰
我在 前端职业规划 - 做前端架构究竟比写代码难在哪? 中提到过, 前端架构的最大挑战是技术更新速度对于架构师维护和设计现有架构的挑战, 我相信 WebForm 的前端部分也是肯定有极其精巧的架构设计的, 如果你看过他生成的 JS 代码就知道从专业前端的角度看虽然执行很低效, 但是开发很高效, 在牺牲网站性能的前提下大大提升了开发效率, 但是打死也没想到 V8 的到来很大程度上解决 JS 执行效率差的问题, 从而一下子将前端开发推到了 JQ Backbone 那样的富客户端开发模式, WebForm 原有的开发效率和执行效率间的平衡被前端技术的发展给打破了, 从而导致了这套方案的没落, 在后续的 MVC 中微软甚至内置了 Backbone 并将前端开发从其整套方案的架构设计中解耦, 交给专业开发者, 不再提供可拖拽的方式来生成 JS 代码. 所以不是微软的架构师不行, 实在是时代发展太快, 鸟枪换炮跟不上.
所以前端架构师在考虑前端架构设计的时候, 要尽量考虑采用实现和设计解耦的方式, 来避免一些依赖技术的更替对架构核心的冲击, 导致架构维护出现后续难以为继的情况
所以回顾历史, LowCode 其实默默发展了许多年, 只不过从某种语言实现变成了某种语言实现, 但其本质并没有发生什么变化, 基本上所有的 LowCode 架构设计都应包含一些共性的组成, 由于 LowCode 的具体应用都是为了尽可能以非代码方式构建出各种各样的应用, 所以我们围绕构建目标应用来划分这些组成
- 用于构建目标应用描述文件的可操作的客户端
- 用于描述构建目标应用的描述文件
- 用于解析描述文件从而构建实际产物的服务
- 用于运行实际产物的服务
来一点LowCode架构的实践
为了书写方便, 让我们试着将上述四个部分做一些定义
- LCClient
- LCSchema
- LCBuildService
- LCRuntime
现在我假设你并没有 LowCode 项目的实际开发经验, 也从没有参与过类似项目的架构经验, 但是我告诉你任何一个 LowCode 项目都可以通过以上四个组成来完成架构设计, 先让我们来看下怎么去理解这些组成
LCClient
如果你知道 B/S C/S 架构, 或者使用过一些拖拖拽拽的可视化操作平台, 或者用 Cli 创建过项目, 那你应该就可以理解这些其实都是 Client, 或者一个能听懂你说话的 AI 他也是个 Client, 最多加上智能二字, 我在上文中提到过, 做架构设计, 尤其是我们想沉淀一些架构设计模式, 最好是实现和设计分离, 所以我这里没有指定 Client 是什么形式, 用什么实现, 因为我相信随着技术发展, 拖拖拽拽也一定不是最终形式, 架构模式需要更具有生命力就必须和实际的实现解耦, LCClient 你可以用任何方式实现, 我们对他的定义是, 最终目标是产生一份目标应用描述文件
目标应用描述文件 LCSchema
什么是目标应用描述文件 ? 可能类似这样
{
"appName": "LowCodeExample",
"appRunTime": "React",
"appRoot": {
"name": "SideBar",
"child": [{
"name": "SideBarItem",
"props": {
"title": "学习强国"
}
}]
}
}
随便写了下, 就是这样一份 JSON 文件, 看内容应该能大致猜到这就是用来生成一个 SideBar 的, 实际的情况肯定会更复杂, 不过本文致力于讨论通用的架构模式, 所以不展开, 让我们继续往下看
LCBuildService
用户或者开发人员, 通过 LCClient 一顿操作猛如虎, 然后吐出一个 LCSchema, 这个时候描述文件有了, 我们需要一个真实产物, 所以 LCBuildService 的任务就是解析 LCSchema, 然后生成实际的产物, 比如代码
LCRuntime
如果 LCBuildService 生成的代码是实际可运行的, 为何还要增加一个 LCRuntime? 增加这个服务, 主要是考虑为了让生成代码可以更好的和实际运行环境解耦, LCBuildService 可以通过 LCSchema 生成一些和运行环境无关的中间代码, 然后等到实际运行环境中再被最终解析出来, 以此达到跨平台跨环境的目的, 实际的 LowCode 项目架构设计, 可以只实现 LCClient LCBuildService LCSchema, 当你需要的时候再实现 LCRuntime 不迟, 这里仅作为一种通用的架构模式的组成部分
根据惯例, 这种模式可以被命名为 CSBR
后话
没接触过类似项目开发的同学可能会觉得有些抽象, 不过没关系, 后续让我们以最常见的表单为例来一个实际 coding 的系列, 基于 CSBR 模式来开发一个用来开发表单的 LowCode 平台.
以上部分是我个人经验的一些总结, 当然远谈不上一种可以被广泛推广的架构设计模式, 相比设计模式而言, 架构设计模式所面的问题域要复杂和广大的多, 仅靠一些概念, 边界的抽象层次还是太高, 得再往下深入一下, 挖一下每个组成的子架构设计, 所以我估计这个系列会很长, 今天还有个朋友约稿关于 ReactHooks 的一些实际如何应用的方法, 估计可能我又会岔开去写那个了, 不过我承诺尽量不太监, 所以挖的每个坑都争取填完😀
本文使用 mdnice 排版