背景
低代码应用是希望提供一种编排手段(GUI、自定义脚本等),可以类似搭积木的方式搭建编排业务的开发方式来快速实现业务功能,编排后的业务脚本由低代码平台执行变现业务能力。
低代码本质是把业务模型中通用规则提炼出来,形成可以编排的组件和接口,然后把上层业务逻辑的编排权交给最精通业务的人(客户或尽量靠近客户的人),把业务的定制便利和app的编排灵活交给广义的用户,编排后app交给低代码平台执行变现。用户可以快速编排从而构建自己的业务,而不需要从头开发整个系统。
随着这几年低代码概念的大流行,目前市面上低代码平台如雨后春笋层出不穷,而且很多平台都可以试用或低成本获取,这也导致低代码大行其道,大家一窝蜂一哄而上。
是不是上了低代码平台,开发速度就如开挂了呢?
其实,看一项技术推广要看其冰山以下的结构,都是由三部分组成:
1、核心特性
这项技术的最有价值部分,值得深入研究和探索。
2、底层逻辑
该项技术背后的底层逻辑,包括的适合场景和使用前需要完成不可偏离项,这部分往往是应用新技术最最重要的前提,也是最容易被忽略的部分。
3、广告
有部分夸张吹牛皮的部分
从新技术推广的进程看,大家一上来都会被上述1部分吸引,往往忽略最重要的2部分。就像当年设计模式刚刚推出来的时候,也是广受推崇,大家学习和使用都激情很高甚至有些狂热,几乎每周都有新的设计模式被提出来,导致设计模式满天飞。
开发人员写代码时,更是无模式不欢,不管什么场景,一段代码不用几种设计模式,都不好意思跟别人打招呼。
但是任何设计都是有成本的,对设计模式不恰到的使用往往导致代码维护成本激增,很多时候为了模式而模式,往往造成模式和应用场景不匹配,代码可理解性和可维护性大幅降低,这时如果引入模式的成本大于模式带来的收益,模式给项目带来的就是负价值。
反过来看设计模式本身是个好东西,但是用好的前提是做好2,本质其是设计原则(SOLID)。
只有理解了设计原则,找到原则的适用场景,并在该场景下选择合适的满足原则的实现,然后该实现恰好暗合某个模式,才是模式的最合理应用,而不是显式机械的套用模式。
从很多项目实践可以看出,低代码也碰到设计模式诞生时碰到的困境。
现在低代码平台应用很普遍,很多时候如果低代码的应用方向和业务方向不正交,会导致某些场景下低代码平台二次开发维护的成本激增,我甚至见过部分场景需要复杂递归的方式编写低代码,实现该场景低代码的行数竟然超过百行,甚至部分场景比直接开发代码实现业务工作量还大,低代码编排出的业务app可理解性和可维护性都骤降,这种情况比比皆是,导致很多业务人员吐槽低代码为高代码,画虎不成反类犬。
分析
用好低代码的关键是对业务模型的抽象和组织,其底层逻辑是DSL的一种应用。忽视对业务模型的抽象和组织往往产生严重的后果,演绎如下:
模型抽象不合理-》与特定某种低代码平台存在Gap-》某些功能两者之间存在复杂映射(见上图);
模型抽象合理-》与选择的低代码平台不匹配-》存着复杂映射。
一旦出现了复杂映射,跟全人工写代码比,会出现“简单功能挺简单,复杂功能更复杂”的尴尬,或者需要低代码平台大量编码定制化,产生严重额外的二次开发维护负担和预算,造成用了低代码反而事倍功半。
小结,上线低代码平台是有成本的(不光是采购成本,还包括二次开发维护成本,这块往往占比最高)。
如果本身业务比较庞大复杂,再加上业务语义抽象不合理,引发语义和低代码平台间网状违规映射(反向,循环)过多,往往会造成低代码维护成本激增,到时候低代码就会陷入食之无味,弃之可惜的困境。
方案
底层逻辑
要用好低代码,必须理解好它的底层逻辑DSL,关键是做好业务模型的抽象和表达。
软件架构设计是沿着上述Roadmap发展的,每一个阶段都是对前一个阶段补充和完善,价值也是逐渐增加,DSL处于一个相对高阶的阶段,价值也较高。
设计一门通用编程语言,满足绝大多数通用场景需要语义十分完备,难度比较大;但对于某个专业方向,只需要定义实现该方向的narrow的语义,则相对比较容易,且对该领域内问题表达非常简练灵活有效,投入产出比很高,是研发提效的利器。
我们先看下DSL的核心要素:
传统的软件系统都是使用通用开发语言(c/c++,java,python,go,rust,js/ts等)直接解决业务问题,小系统或简单业务场景问题不大,但对于大型或复杂系统来说,这种解决方式存在业务域语义层次降解太快的致命问题,很多时候会造成业务模型模糊,系统层次不清,业务和通用编程实现强耦合,语义层次的代码显性或隐形重复度高,往往形成大泥球,造成系统复用性差,难以轻量级编排,不易于理解和维护等突出问题。
DSL的核心解决思路是复杂度分层降解(见上图):
1、业务降解
a、把问题/需求提炼成业务模型形成业务语义,业务语义由原子(action+relation)构成。
b、使用载体如内外DSL/API/XML/YML/DataType等把业务业务中的原子表达出来形成语法/词法,载体也可以进一步通过GUI方式表达,以提升易用性。
c、 二次开发通过语法/词法表达后的relation把原子组合成组合子(relation把原子(relation+action)组合后生产的部件),组合后的组合子还具有原子性,还可以通过relation把组合子或原子继续组合,通过递归组合,最终形成app,并最终通过语法/词法组合表达出业务逻辑。
2、实现降解
通过interpreter或compiler/vm把app降解为通用编程语言跑起来,其中:
a、interpreter
解释器:解释执行,实现简单,但性能有可能会有瓶颈。
b、compiler/vm
编译器/虚拟机:把app转换成宿主语言或者其他高性能语言编译后执行,性能相对较高;也可以把app转换成自定义业务流,通过自定义vm执行,实现灵活且易于性能优化。
3、对应的团队也按语义层次和个人能力自然形成分层:
a、框架团队
负责设计开发业务降解(业务模型-》业务语义-》语法/词法)+实现降解(自定义解释器、编译器/虚拟机)
b、业务团队
应用语法/词法进行二次开发编写业务app,并进行调试运行和后续持续维护,业务开发可以由开发团队中资历稍浅的员工承担,也可以委托给客户或最靠近客户的产品团队编写。
DSL把系统功能按语义进行内聚形成分层,对应的团队也按照语义分层的映射进行了划,自然而然顺应了康威定律。
解决方案
对于如何做好低代码的应用,关键提前是做好业务降解,业务降解的关键是业务模型的抽象和组织,即原子的提炼。
这一步做完后才能选型低代码平台或适配已有低代码平台,才能用好低代码平台。
如何提炼好原子呢?
具体包括提取语义和提炼语法/词法两部分。
先看提取语义,语义由原子组成,原子又分为action和relation,其中:
1、action
实现原子粒度的垂直业务逻辑,并抽象出原子间接口,以形成编排组合能力。
2、relation
关系,实现特定业务流程,即把代表业务逻辑的原子/组合子(原子组合后的东东)进行编排组合成组合子,组合子还具备抽象性,还可以通relation和其他原子/组合子进一步组合,从而实现递归,当然这些原子都通过语法/词法表达,最终编排出完整端到端的业务app。
下面我们以一个案例详细讲解梳理语义(action和relation)、抽取语法/词法、编排app的整个过程。
案例
一、梳理语义
1、梳理业务流
2、抽取action语义
- 侦测是否有某个告警
- 侦测是否无某个告警
- 屏蔽某个告警
- 产生某个告警
3、抽取relation语义
- 判断同时具有多个告警
- 判断至少有某一种告警
- 顺序执行多个屏蔽或生成操作
- 满足判断后执行
这里使用变种事件风暴等抽取语义,一阶逻辑集合论等的法,这方面内容和要点较多,详见我的《DSL战训营》。
二、定义语法/词法
根据上面的语义,我们定义出语法/词法:
- 侦测是否有某个告警
has
- 侦测是否无某个告警
not
- 屏蔽某个告警
screen
-产生某个告警
generate
- 判断同时具有多个告警
and(has,has,has...)
- 判断至少有某一种告警
or(has,has,has...)
- 顺序执行多个
sequential(screen/generate/sequential)
- 满足判断后执行
optional(has/and/or,screen/generate/sequential)
三、编排应用
语法/词法具备后,就形成了一门小语言,我们使用该小语言二次开发编写业务app。
1、告警屏蔽
2、告警生成
有了这个语法/词法形成的小语言之后,再去选择合适的低代码平台并使用低代码平台对小语言进行映射和表达后,就可以让业务人员去二次开发维护调试告警处理的相关业务规则,实现真正的低代码应用。
至此,通过实现对业务建模来分离关注点形成语义,通过语法/词法表达出语义,从而把业务的编排权交给业务团队;把业务语义、语法/词法设计、app执行交给框架团队,形成了良好工作界面,实现语言层面的易于编排组合的接口,分层合理,职责清晰,让适合的人干合适的事。
总结
低代码平台要用好,首先是要做好业务建模,然后通过业务模型提炼业务语义,通过业务语义抽象出语法/词法,然后才能选择合适的低代码平台,并做好语义到低代码平台的映射。
才能消除业务和低代码平台之间的Gap;
才能做到使用低代码比直接通用语言硬编码取得更高的性价比;
才能做到低代码的应用维护成本较低;
才能最终做到真正的低代码。