社区里缺的不是架构图,而是可供参考的架构实践。程序员缺的不是技术原理知识,而是抽象来的可供复用的方案思路。为了切实帮助技术人成长,在信息爆炸时代获取最精华的架构知识,腾讯云开发者携手腾讯云架构师技术同盟推出架构师系列文集,每期会以《如何设计一个 XX 系统》为主题,分享同盟架构师们多年经验抽象来的经典方案设计思想。 本文是系列第二篇,作者分享了一个运行十四年,底层系统架构一直没有大改的低代码系统是如何设计的,作者确信:如果有对业务的深入理解,有着架构性思维体系,有面对复杂的体系进行抽象的能力,是完全可以用低代码做出一套可用、好用的核心业务系统的。
关注腾讯云开发者,一手技术干货提前解锁👇
腾讯云开发者
腾讯云官方社区公众号,汇聚技术开发者群体,分享技术干货,打造技术影响力交流社区。
970篇原创内容
公众号
「腾讯云架构师技术同盟」是腾讯云为架构领域知名专家与从业精英打造的专业技术社交圈,通过多样的技术交流会议、社群专业探讨、权威内容输出,打造业界领先的架构师专业技术组织。同盟共创,携手同道,关注每一位中国架构师成长。
扫码报名加入
今天我们从细胞层面来剖析一个低代码平台的设计过程,也让更多对低代码只停留在字面意义上认知的同学们,有一个深入围观解剖低代码平台的机会。
这套低代码平台开发于2004年,与Oracle开始构建Apex是同一年,最早的用户包括深圳新一佳、李锦记无限极、上海家得利、马来西亚最大零售商Sunshine集团等中大型企业,鉴于年代比较久远,所以当时它能做到的,现在一定能够做到,它不能够做到的,现在很多也都能做到。
首先是起源,为什么想做这样一个平台,在好多场合我也都分享过一个模块编译了160多次的故事,本质因为2B软件的迭代要求太高,即使我们使用的是当时最高效的开发工具之一“Delphi”,但是还是跟不上客户的需求迭代,纯粹的用高代码方式费时费力,简单地说,没钱挣。
我们先看一下当时的背景:2004年,零售业大发展,对系统的要求也越来越高。当时中国还没有形成完整的管理体系,对零售管理的理念也是五花八门,主要的派系有万客隆(麦德龙)、沃尔玛、家乐福(大润发、好又多)、日系7-11、港系Circle 便利等等,随着运营高管背景的不同,都会深深的带着原来企业的烙印,这时候对软件挑战极大,换一个高管,基本所有的报表和流程都要重新迭代一次,不然验收别想通过。
2004年,Intel推出的最新CPU是奔腾四,现在几乎所有人的手机性能都远远超过了它,鉴于零售业大量的数据和运算需求,有些规模比较大的企业会购置小型机来进行运算、SAN来进行数据存储。当时新潮的软件架构是SOA(Service-Oriented Architecture 面向服务的架构),数据库开发也有了ORM的雏形。
那么,我们需要怎么做一套适合零售企业的系统平台呢?
首先是抽象,必须把所有的业务抽象出来,有了抽象就有了架构。使用的开发语言Delphi本身就是一种面向对象的语言,大约用了一周的时间,把所有零售系统功能抽象为3块:“业务对象DataObject”、“业务逻辑(Business)”、“查询(StandardQuery)”、由于零售业流程性较弱,到后期用了一种很有意思的方式加入了流程管理。
中央部分就是数据对象、业务、标准查询,还有一个数据字典
其次是目的,为什么要搞这么一个平台?只有一个口号:提升零售管理系统的开发效率,具体分为几点:“开发人员分层”、“减少低级错误”、“绝大多数客户需求调整时,无需重新编译程序“。
需要注意什么?只有两个字“性能”,因为平台一定带来性能的损失。作为核心业务系统,性能不行,那就是完全的失败了,这个问题又受限于当时的软硬件环境。
我们下面就以上这些内容一一切开、掰碎、分享一下当时的整体思考过程。
首先回到零售业务的抽象,这是当时能够想到的最极致的抽象方式了,我们逐一分析一下。
A) 业务对象,零售有大量的表单,很多软件企业以表单驱动为荣,但是表单本身就是一种经常变动的东西,这是一个迭代的重灾区。业务对象内部也有层级,我们需要设计一个可以容纳比较复杂的业务对象的体系,但是实际应用中发现,大部分时候,2层已经足够。
我们在以前做数据库应用时,经常碰到一些好几张表的关联,做查询时性能很低,因此我们采用了这样的方式来设计业务对象: 首先,业务对象是一个表或者多个表的集合。其次,他们之间需要通过一个ID字段或多个字段进行关联。比如订单对象是一个订单头+一个订单体,两者之间用订单编号关联,对于连锁体系,可能还需要加上店号字段进行关联。
通过业务对象,我们可以清晰知道要对哪些数据表进行读取与操作。
如何构建这个业务对象呢?我们做了一个工具,你把表的名字给它,它自动取出每一个字段,进行一个简单翻译,构建了一个SQL,你可以对这个SQL做一些小调整,然后给他设定关联字段,你需要几个表就设定几个表,最后记录为一个数据对象。(这里的逻辑,还是先有表再构建对象,在目前的AI技术下,AI可以直接根据业务需求来自动构建表结构,然后再自动构建对象。)
数据对象和业务逻辑
为什么要设计业务对象?就是为了灵活改动,同时,只要遵循一定规范,开发工作变得很简单高效,以前经常碰到的新手程序员不记得给界面上字段加上录入限制啥的这一类低级问题,都可以一次开发,多次收益。
某一个模块—里面有多个数据对象;某个数据对象—里面有多个表
B) 业务逻辑,这是很有意思的一部分,当时的设计是倒推式的,先看哪些是开发重灾区,尤其是哪些今天客户要求由A改到B,明天由B改到C,后天又要求你改回A的需求。作为乙方,客户是上帝,他想咋改就咋改,但是,我必须要用最低的成本陪他折腾。
业务逻辑一直是一个重灾区,我们知道,企业随着发展,管理的视角、重点、都会变化,有时候,碰到了某些“运动“,又会临时提出新的要求。这些要求有一个特点,他们都会在某个操作节点发生。这些操作节点又分为三类,事前事中事后。
很多时候,使用系统就是为了自动化。比如一张订货单,里面100个商品,如果是手工填入订货数量,保不齐就有这么几个商品考虑不周,导致订货数量过大或者过少,这时候,我们需要进行一个简单的自动计算,例如,对于该次订货的某个商品的数量,需要保证他单张订单订货数量不得超过30 X日均销量,原理很容易理解,但是靠人工核对肯定不现实,这时候系统就需要自动核对,这就是隐藏在审核按钮后面的逻辑。
这个审核按钮后面可能会跟着20~30个不同的业务控制点的关系,而这些控制点,又会和企业的规模、业态、品类有着关系,这意味着,即使只有20个控制点,但是不同企业有着不同的排列与组合,一个简单功能迭代160多次都很常见。
怎么办?把不同的控制点抽象成一个一个独立的业务逻辑,部署实施的时候,根据客户的具体情况来进行勾选即可。这就是低代码的雏形,我们将业务逻辑用低代码形式开发,可以沉淀与积累,在不同用户上就可以反复使用,而无需重新开发。
我们仍然回到表单驱动这个概念,我们发现,表单本质是一个业务对象,常见的业务操作例如新增、修改、审核,甚至打印,都是一个一个业务逻辑组,这样,我们完成了业务控制逻辑的抽象,也完成了对CUID的封装,这一点很重要。这里的问题就是颗粒度到底多细合适,如果是做通用低码平台,可能不封装更灵活,还能做成微服务来用,但是对于当时的主机性能,我们封装以后的性能较好,而且对开发更友好。
下一步,如果这些个审核的逻辑检查无误了,那么就要进行数据库的实际操作,也就是写入物理表,这里也可能会有很多步骤,比如,先要取出唯一单号,然后对应业务对象进行一一写表。
同样,这里也有着大量的不确定性。比如,A超市100多家,横跨不同城市与省份,一个新商品是否在不同省份的门店进行售卖,需要一个独立的“商品引入门店”动作;但是B企业只有3家标准超市门店,我们做新商品的审核时,一旦审核完成,我们就应该在门店和商品的关联表中,对这三家门店都自动增加对应关系。所以A超市不能执行自动引入门店这个操作,而B企业则有这个自动化的需求。因此,我们把这个“自动引入门店”独立为一个业务执行逻辑,可以根据不同企业的要求进行配置。业务逻辑还有一些保留字,可以指定连接短信或者email平台,在业务执行过程中完成和外部的互动。
基于简单SQL和配置构建的业务逻辑Check,列表显示界面可以勾选是否生效
基于SQL实现业务逻辑Process,可以勾选是否生效
C) 查询
查询其实是一个非常广泛被运用的功能,比如,我们进入一个模块,往往第一件事就是先进行一个查询,然后对查询出来的结果进行详细的编辑等处理;同样,我们在进行一个对门店的管理操作,往往会点一个小按钮,在弹出窗口中选择一个或多个门店,然后再进行操作,一些日常的报表,比如门店当日销售,门店实时商品分类销售排行,都在业务系统的各个角落分布着。查询的条件也是经常会变动,比如,今天财务可能要对所有商品的税率进行核对,起因可能是国家新的税务政策,这时候,他们要么独立做一张报表进行查询,也可能会要求在商品管理模块中,增加一个对税率的查询条件,从而让各个部门的采购自己核对与调整,毕竟,很多超市有上万个品项,财务不方便自己直接去操作。
特别的,查询还面临一个性能的问题,我们经常看到的自由组合条件查询功能,在性能上有非常大的短板,毕竟当时才2004年,最新的CPU是奔腾4 处理器而已,做数据库的同学也都知道,针对任意组合的查询,由于没办法提前进行索引的优化,性能是完全无法保障的。
所以,我们采用了SQL构建的方式,你只需要写一个恰当的SQL,工具会自动地构建界面,查询条件只需要进行极少量配置,就可以生成一个查询功能,大到复杂报表,小到点下下拉框显示不同结算条件,在这里,SQL就是低代码的工具。对于复杂业务逻辑的SQL,因为全部进行了拆分,反而变得非常简单。
标准查询的配置界面
灵活的SQL代码实现
查询参数的封装
在当年的这个平台上,无论是数据对象、业务逻辑、查询,都是用SQL工具来构建的,好处是人才好找,SQL是大学的必修课嘛,而Java、.Net的人才,被互联网和游戏公司挖走得太快了……。很多企业本身就有一些软件人才的储备,他们一般都精通SQL,可以直接从数据库为企业高管取数,写报表等, 对于这一类人员,大概2周左右时间,就可以非常熟练的使用这个平台。
在2004年的时候,SQL还有一个很好的优点就是性能高,毕竟是直接对数据库操作,对于一些复杂的查询,我们甚至还可以用临时表来优化,毕竟性能是我们考量的一个重要指标。
凡事有利就有弊,万一某个同学写了一个愚蠢的SQL怎么办?我们在工具中引入了一个性能审计功能,一旦打开这个开关,那么每一条通过平台执行的SQL都会被记录,包括SQL语句,执行参数,执行开始时间,执行结束时间,如果是循环执行某些小SQL,那么循环了多少次等等都会被记录,这样我们可以随时进行性能检查。
打开审计功能后,可以针对执行超过设置下限时间的SQL做全量信息记录
特别的,为了提高开发效率,针对常见的需求例如“查询结果计数”,“字段汇总”,“锁定查询结果表格的前几列”等等,都可以用配置方式完成,如果在SQL中针对不同结果(大于0小于0)做出配置,数据结果显示的颜色都可以自动完成,所有SQL生成的查询结果,也都可以挑出几列直接转换为各种折线图、饼状图。对于一个报表查询出来的结果,我们还可以配置成双击某一列,就会自动跳转至另一张报表,同时又自动将该条记录的信息作为跳转后报表的查询条件自动填入。
当然,鉴于OLAP和OLTP对服务器的要求不同,我们还是将特别复杂的查询都独立到业务系统之外,由BI工具来进行处理。
对于一套系统,权限是不可或缺的,对于零售业的特殊情况,还会有一些特别的权限维度,比如,两个人都是采购经理,但是一个管服装一个管家电,他们之间的数据需要隔离,也就是不能看到自己管理的品类之外的数据,这些都在平台的权限配置模块和所有查询模块中内置了,无需再次开发。
业务逻辑作为权限基础,再叠加了数据维度
我们都知道,灵活的东西往往会失控,因此版本管理也是一早就注意到的功能。
软件开发是一个系统工程,我们可能会同时有三个环境,一个是开发环境,在这里,可能同时存在几个新的功能在开发,他们可能需要对数据库结构有调整,比如增删了几个字段,这里的开发阶段的代码,和这些调整紧密关联;第二个是测试环境,在这里,测试人员针对开发提交的代码、新数据结构等等进行部署和测试;第三个是正式环境,这里有些规模比较大的企业还会设置多个灰度环境。因此,我们开发的新代码,要到正式环境部署,必须有一个自动化工具,鉴于当时的低代码和功能都是采用了SQL,而且低代码平台自己(界面布局、执行逻辑、配置、数据字典等)也是以Blob方式存贮在数据库中,所以我们开发了一个导出功能,开发人员可以将自己对模块做的所有修改,都以多个SQL的方式导出,只需要将这个SQL包在新的环境执行一遍,就可以完成部署。同样的,也可以进行回滚。导出功能是可以打包进行的,如果我们给出了表单的各字段范围,还能够进行一定程度上的自动测试。
文挡为中心的开发管理体系
C/S架构在录入大量数据时,本地其实有个缓存,我们也利用了这个缓存做出了一些比较接地气的功能,比如录入个几百条数据,尚未提交,中途断电,当你打开电脑,会惊喜的发现数据都还在。C/S软件在录入的时候,执行效率比较稳定,因此,有很多盲录的高手,一边看着左边的打印文档,一边手指如飞的在键盘上敲编号,回车,敲数字,回车,这个也在系统中专门做了支持,如果录入的内容较多,还支持直接从Excel中拷贝进入剪贴板、直接粘贴进系统。
软件开发离不开文档,为什么后续的程序员都宁愿推翻重来而不愿意去修改代码,因为读代码太累了,有些稀奇古怪的功能背后是业务的差异化与特殊性,这时候,我们设计了一个文档工具,文档与模块紧密集成,可以记录每一次迭代的需求来自哪里,还可以记录对应的迭代版本做了些什么设计,形成了一个连续的研发体系,同时,平台可以帮助开发人员忽略掉那些常量、变量、内存回收、错误提醒的高代码,平台解耦了那些业务对象与业务逻辑以后,可以使得开发人员专注在实现业务功能的SQL代码和逻辑关系上,从而让软件的维护与迭代变得非常轻松。
文档以模块为单位导出
在导出功能之上,还有一个逻辑导出的功能,将每一个业务逻辑以文字标题的方式按模块导出,目的是让不懂开发的业务人员可以通过这个逻辑导出的文字清楚的知道,这个审核按钮到底干了些什么。毕竟,我们问100个采购总监,99个都不会清楚系统到底干了啥。
刚才我们提到了流程,BPM工具其实一直做的不错,当然当时也有性能瓶颈的问题。在研发过程中,我们做了两个功能,第一,业务逻辑可以和外部流程打通,比如发送短信、调用及回调OA流程等,可以方便的对接外部流程,满足标准审批的要求;第二,在软件首页加入一个可以配置的流程图,这个流程图和权限系统集成,因为我们发现,零售从业人员更换频繁,他们碰到一个新的系统,往往会问的是俩个问题,“这个功能在哪里?我在干这个事之前需要先干(看)什么?”这个首页的流程图可以根据不同岗位的工作性质,加入常用的功能流程,比如,先是一个超链接到FTP上的一个文档模板或者在线文档,填写上交,然后进入某个具体功能模块,填写数据,再进入某个报表,根据指引看一下报表发现问题,然后再打开某个具体功能模块做一些单据来解决刚才报表里发现的问题。在实施过程中,就可以帮助客户构建这个流程图。
用户可以自己定义的流程图
随着服务客户的规模增长,性能还是一个无法摆脱的瓶颈,这时候我们发现配置的业务逻辑、业务对象,完全可以部署在不同的服务器与数据库,因此后来迭代了一个多数据库配置功能,可以指定某一个业务逻辑、业务对象、查询是在哪一台服务器上执行,也算是分布式部署的一个雏形,的确大大缓解了性能问题。
CS年代手搓的分布式数据库
平台成熟后,我们将一直用于内部开发的工具整理了一下,加入一些常用的功能,在实施过程中我们直接给予客户培训,教会他们如何使用,后期的80%左右需求都是客户自己的几个懂SQL的同学自行迭代维护,客户省下了二开成本,我们突破了实施的运维的瓶颈,双赢。
尤其是零售2B业务大部分表单都非常类似,我们专门抽象以后做了一个“通用表单”,只需要配置他用哪个数据对象,哪个业务逻辑,首页用哪个查询,模块就做好了,是最早无代码的雏形。
通用表单,选择连接哪个数据对象以后,数据对象的字段就可以拖过来形成界面
从提出这个平台的设想到原型,大约5个开发人员加4个测试,花了半年时间,然后用了一年时间全面替换了客户的原有系统,2009~2010年,该平台进行了一次B/S迭代,从C/S架构平移到了C#.NET构建的B/S体系,2014年左右,又迭代了一个Java的版本,2016年支持H5,2018年开始支持微服务架构,意味着原来执行的SQL可以全部替代为执行微服务,同时,所有的业务逻辑业务对象也可以以微服务方式对外提供服务,十四年的时间内,系统的底层结构一直没有大的变动,只是适配了多种数据库,用不同语言重构了解释器。正是因为底层几乎没变,2019年还帮助十年前的客户马来西亚Sunshine集团进行了平滑升级,从C/S架构升级为基于云服务的B/S体系。
虽然从2017年开始我本人就没有再参与该平台的商业化迭代与开发,但是纵观系统的整个演变过程,我们认为,低代码完全可以用于复杂的大型核心应用,当低代码插上了云+微服务的双翅,犹如鲤鱼化龙,足以胜任企业内的大部分挑战。
以上就是这个十八年前的“低代码”平台从构思、到设计、研发、落地、应用的整个历程,我们可以看到,如果有对业务的深入理解,有着架构性思维体系,有面对复杂的体系进行抽象的能力,是完全可以用低代码做出一套可用、好用的核心业务系统的。大道可以传承,也可以自悟,只要你的思维方向正确,又愿意投入时间去研究,前人能够做出来的,我们也一定能做出来。这篇文章希望能够给低代码从业人员和甲方人员一些启发,帮助大家少走一点弯路,少踩一点坑,就功莫大焉。
作者介绍
沈欣,广东省连锁经营协会技术委员会主席、上海交通大学终身教育学院特聘讲师,中国信通院低代码委员会顾问,曾任喜茶数字化高级副总裁,曾任百果科技首席技术市场官,百果科技首任轮值主席,曾任和君咨询合伙人、上海齐扬软件技术有限公司总经理,多次担任红杉、中信、太盟等投资基金IT顾问,负责对投资企业进行尽调及投资后IT规划,为好又多、真功夫快餐、万客隆(中国)、中油BP、OK便利店、周大福、本来生活、台湾三商集团(中国)、马来西亚Sunshine集团、中国供销总社等企业进行过IT规划、开发及咨询。
-End-
原创作者|沈欣