
有人问我低代码平台怎么设计的时候我一般不会先甩架构图出来而是反问他一句你是想做个给外部客户用的商业化产品还是想解决自己团队内部大量重复的CRUD页面开发问题这两个答案对应的设计路径可以说是天差地别。我这些年看过不少团队做低代码平台的经历自己也踩过不少坑。一个很残酷的现实是大部分团队在动手写第一行代码之前就已经把方向走偏了——要么一上来就研究拖拽组件库要么先纠结前端框架选Vue还是React等到真正开始面对一个页面配置到底应该存成什么数据结构的时候才发现前面做的那些都立不住。这篇文章我打算系统性地聊一聊低代码平台的设计思路。咱们不聊那些花里胡哨的概念就从实际落地的角度把平台的核心架构、关键模块、技术选型思路、以及最容易让人翻车的地方都过一遍。无论你是准备从零自研还是想基于开源项目做二次开发这篇文章应该都能帮你少走不少弯路。1. 先想清楚低代码平台到底在解决什么问题很多人一提低代码第一反应就是让不懂代码的业务人员也能做系统。这个说法不能说错但如果你真奔着这个目标去设计大概率会把平台做成一个谁都用不顺手的四不像。我见过太多团队死磕可视化配置做了大半年业务人员依然不会用开发人员又嫌它不如直接写代码来得快。1.1 低代码不等于低门槛这句话值得每个做低代码平台的人抄下来贴在工位上。低代码的核心价值不是降低使用门槛而是提升交付效率。它是把过去写一遍要两三天的增删改查页面压缩到配置半小时搞定的程度。至于这个配置的操作者是业务人员还是开发人员其实是第二位的。你去看市面上活得好的低代码产品比如国外的Retool、国内的宜搭、简道云它们的核心用户画像基本都是懂业务但又不完全不懂技术的半技术人员或者干脆就是前端开发自己。真正让一个完全不懂逻辑的运营直接拖出一个带审批流的系统目前还没有哪个平台能做到也不建议你做平台时把这件事当成首要目标。想清楚这一点你设计平台时的很多决策就会有完全不同的取舍。比如你还会不会为了追求业务人员零学习成本把配置界面做得过度简化反而牺牲了表达复杂逻辑的能力大概率就不会了。1.2 两个目标场景决定了架构走向我做过的项目里低代码平台的需求大概可以归成两类第一类是管理后台快速搭建。内部系统、运营后台、数据管理平台这类场景的特点是页面结构固定表格、表单、详情、筛选逻辑相对简单增删改查、导入导出、简单审批但是量大、重复度高。团队里一个后端带一个前端一个月能做三五个系统已经算快了但需求排期排了十几个。第二类是业务系统的可视化编排。这类平台走得更深它不仅要配置页面长什么样还要把业务逻辑、流程流转、数据联动都纳入配置范畴。比如一个工单系统表单要联动客户信息提交后要走多级审批审批结果要回调更新订单状态。这种平台的复杂度和第一类完全不是一个量级它实际上是一个低代码应用平台LCAP。你的平台定位如果是第一类那核心引擎是表单渲染和列表渲染数据模型可以做得相对简单如果是第二类那核心引擎就要加上流程引擎、规则引擎、数据关联引擎整体架构复杂度至少翻一倍。这个决定做得越早越好因为架构一旦定下来后续想从第一类升级成第二类基本等于重写。1.3 平台边界什么必须做什么必须交给二次开发每个想做低代码平台的团队都容易犯一个毛病什么都想做成配置。我的建议是从一开始就划好边界平台只解决80%的标准化问题剩下20%的场景通过扩展机制解决而不是死磕配置化率100%。为什么因为低代码平台的本质是用有限的配置能力覆盖尽量多的场景但业务是无边界的。如果一个场景用配置实在表达不了那就不如提供一个扩展接口让开发者写一段自定义代码。这段代码可以是自定义组件、自定义钩子函数、自定义后端逻辑不管叫什么名字核心思路是一样的平台提供插槽扩展代码提供复杂度。那些追求一切皆配置的平台往往最后配置项多到连开发者都记不住文档比框架还厚学习成本比直接学框架还高。这正好违背了我们做低代码平台的初衷。所以设计的第一步不是画架构图而是写一份边界文档。明确哪些是平台内置能力哪些走扩展机制哪些干脆不在平台范围内。这份文档后面所有技术决策的依据。2. 平台的命根子元数据驱动的核心思想如果你只从这篇文章里记住一个词那应该就是元数据驱动。低代码平台和普通应用开发最大的区别在于普通应用是代码-运行低代码平台是配置-元数据-运行。所有的页面结构、字段定义、校验规则、联动逻辑、权限配置在低代码平台里都是数据而不是代码。这个数据就是元数据。2.1 为什么一定要把配置变成数据我们一开始做低代码平台的时候也走过弯路。第一版我们直接把拖拽生成的组件树存成JSON渲染引擎拿到JSON就递归渲染。这个方案在前端跑得通但很快就碰到了问题你想对某个页面做A/B测试或者想对不同的用户展示不同版本的页面没法做因为JSON直接和页面绑死了没有版本概念也没有条件加载的入口。后来我们把元数据拆成了三层数据模型层定义业务对象、字段、字段类型、关联关系。相当于传统开发里的建表语句CREATE TABLE。页面模型层定义页面长什么样包含哪些组件、布局结构、组件属性。相当于传统开发里的JSX/HTML模板。行为模型层定义页面上发生什么事提交到哪里、校验规则是什么、联动关系怎么处理。相当于传统开发里的JS业务逻辑。每一层都是独立的、可寻址的、可版本化的数据对象。平台运行时拿到一个应用ID先把这三层元数据全部加载出来然后再实例化成运行中的应用实例。这个方案带来的最大好处是配置(数据)和运行(逻辑)彻底解耦了。你想改页面改的是数据不用发版你想做多租户隔离不同租户配不同的元数据集合就行你想做版本回滚直接切元数据的版本号。这些能力在没有元数据驱动架构的时候实现起来极其痛苦。2.2 表单是元数据设计的试金石如果说低代码平台是个人那表单就是它的脸。虽然完整的低代码平台远不止表单但几乎每个平台都是从表单引擎开始做的因为表单的配置需求最清晰、覆盖场景最广、也最容易抽象。表单元数据的设计核心是字段描述。一个最简单的字段描述长这样{ type: input, field: customerName, label: 客户名称, placeholder: 请输入客户名称, required: true, rules: [ { pattern: ^[a-zA-Z\\u4e00-\\u9fa5]{2,20}$, message: 名称格式不正确 } ] }这个不难理解。但真正考验平台设计能力的是字段联动和动态行为。比如当客户类型选择企业时显示税号字段并且税号字段必填这种场景怎么配置我的做法是在元数据层增加一个effects属性里面包含条件和动作。条件可以是某个字段的值等于什么动作可以是显示/隐藏某个字段设置某个字段的值为触发某个校验。渲染引擎在渲染时会注册一个事件监听器字段值变化后触发条件判断满足条件就执行动作。这个机制虽然简单但能覆盖大部分表单联动场景。2.3 元数据建模时的两个关键取舍第一个取舍元数据粒度。粒度越细越灵活但渲染引擎越复杂配置成本也越高。比如一个输入框字段你是把它定义为一个组件节点还是把它拆成标签输入框校验规则三个独立节点我倾向于组件级别的粒度就是把一个输入框封装成一个组件对外暴露属性配置项。这样既保证了配置的简洁性也保留了足够的扩展空间。第二个取舍开发者友好度。低代码平台最终还是要被开发者使用的再次强调不要幻想纯业务人员能搞定一切。所以你的元数据设计一定要有可读性最好能支持可视化配置和源码编辑两种模式。可视化配置生成JSON源码编辑直接改JSON两者实时同步。高级用户会用源码模式解决可视化配置搞不定的边缘场景这种双模式设计能大幅提升平台的用户接受度。3. 前端架构怎么搭设计器与渲染引擎的配合低代码平台的前端可以拆成两条完全不同的产品线设计器配置页面时用的IDE和渲染引擎运行配置结果时用的解释器。这两个东西虽然都在浏览器里跑但它们的关注点完全不同架构上必须分开。3.1 为什么要设计器和渲染引擎分离很多低代码新手最爱犯的错误是把设计器和渲染引擎混在一起写。结果就是配置界面上拖拽一个组件实际上操作的是运行页面的DOM组件被移动了位置运行时的数据绑定状态也被动来动去。这种耦合到后期就是一场维护噩梦。设计器和渲染引擎应该通过一份Schema协议通信。设计器负责生成和编辑Schema渲染引擎负责消费和渲染Schema两边不直接依赖。举个例子设计器的工作流是这样的用户在画布上拖一个按钮进来 - 设计器更新组件树的Schema - 把新的Schema传给渲染引擎 - 渲染引擎根据Schema重新渲染画布上的预览效果。用户改属性面板上的按钮文字 - 设计器更新Schema中该节点的props.text- 渲染引擎监听变化并更新视图。这样做的好处是Schema是唯一的事实来源任何一端出问题都可以通过Schema来排查。更重要的是将来如果你要出移动端渲染引擎或者要嵌入到别的系统里直接复用Schema协议就行设计器可以删掉换成别家的只要协议兼容。3.2 设计器内部的核心模块一个完整的设计器应该包含这么几个核心模块物料面板Components Panel展示所有可用组件。组件可以从内置组件库来也可以是从外部注册的自定义组件。物料面板的数据来源是一个组件注册表里面记录了组件的名称、图标、分类、属性和默认配置。画布区Canvas所见即所得的配置区域。画布本质上是渲染引擎的一个实例只不过多了一层选中、拖拽、缩放的交互层。画布区最考验性能因为用户拖动组件时整棵组件树要实时重绘。我建议这一层做两级优化拖动时只更新位置信息松开后再更新Schema组件树的Diff算法要写在渲染层之外避免大规模重渲染。属性配置面板Properties Panel选中画布上的组件后属性面板展示该组件的可配置项。这里有一个设计经验不要在属性面板里堆砌组件的所有底层属性而是把常用的配置项归纳成基础属性校验规则高级属性等分组否则配置体验会极其糟糕。大纲树Outline Tree以树形结构展示页面组件的层级关系。看起来简单但实际非常有用尤其是页面层级深的时候从画布上不好选中深层组件大纲树一选一个准。功能操作区Toolbar撤销、重做、预览、保存、发布等全局操作。3.3 渲染引擎的递归渲染与组件注册机制渲染引擎的设计核心是一个递归渲染函数。给它一个Schema节点它能递归地把整棵树渲染出来。伪代码大概是function renderNode(node, context) { const component registry.getComponent(node.type); if (!component) { return renderFallback(node); // 未注册组件时的兜底处理 } const props resolveProps(node.props, context); // 解析数据绑定 const children node.children?.map(child renderNode(child, context)); return createElement(component, { ...props, children }); }这里有几个点需要注意组件注册表Registry是渲染引擎和设计器共用的。设计器的物料面板从注册表取组件列表渲染引擎按node.type从注册表取组件实例。注册表的作用相当于前端框架里的依赖注入容器。数据绑定解析是渲染引擎的核心能力。一个组件的某个属性在Schema里既可以是一个静态值label: 客户名称也可以是一个动态绑定表达式label: {{ formData.customer.name }}。渲染引擎要能识别这两种情况并在运行时正确解析。这块推荐用表达式解析器来做自己用eval实现虽然快但存在安全风险和调试困难的问题后面章节再细聊。3.4 状态管理画布状态、页面状态和业务数据的隔离低代码平台的前端状态管理严格来说要管三种不同类型的状态。第一种是设计器自身的UI状态比如当前选中的组件ID、面板的展开折叠状态、撤销栈的记录。这些状态只在设计器里存在不影响运行时的任何行为。第二种是页面Schema的编辑状态也就是当前正在编辑的组件树。很多平台把这两种状态混在一个Store里管理结果就是改选中组件和高亮展示的逻辑耦合在一起代码一多就乱。第三种是运行时的业务数据状态比如表单填写的值、列表加载的数据。这个状态在预览模式和发布模式下是会真正参与业务逻辑的。我的建议是设计器自身的状态选中等用Zustand或者Redux单独管页面Schema用不可变数据结构存方便做撤销/重做业务数据在渲染引擎内部独立管理三块互不干扰。这个隔离看起来是细节但实际开发时能省掉大量互相踩坑的时间。4. 后端与数据层低代码平台的隐藏复杂度前端能拖能拽大家一眼就能看到。但低代码平台真正拉开差距的其实是后端数据层。一个配置好的页面数据存到哪、怎么校验、权限怎么控制、和别人配置的页面数据怎么关联这些才是设计一个低代码平台时最硬核的部分。4.1 数据存储方案三种路线怎么选低代码平台的数据存储市面上主流方案有三种各有利弊方案一动态建表。平台根据元数据里定义的数据模型在运行时动态地在数据库里创建真实的物理表。字段有新增就执行ALTER TABLE加列。好处是数据查询效率高可以直接走SQL适合数据量大、查询复杂的场景。坏处是动态DDL有风险物理表和元数据的同步容易出问题而且每个租户单独建表的话表的数量会膨胀得比较快。方案二JSON字段存储。平台不去动数据库表结构所有的业务数据存在一个统一的表里其中有一个JSON类型的字段存全量数据。需要查询的时候要么用数据库的JSON查询能力比如MySQL的JSON_CONTAINS、PostgreSQL的GIN索引要么在应用层做好索引。好处是灵活度极高元数据随便改不用动数据库。坏处是查询效率和复杂查询能力受限数据量上去了之后容易变成性能瓶颈。方案三混合模式。这是我在实际项目中比较推荐的路线基础字段比如通用ID、创建人、创建时间、所属租户设计成物理表的固定字段业务字段存在JSON扩展字段里。平台在设计和运行时都通过元数据来读写JSON字段但物理表提供了稳定的主键和通用字段兼容了标准的数据库运维操作。这三个方案没有绝对的好与坏主要看你的平台承载什么样的数据规模。如果做的是几十人用的内部工具方案二就足够了生命周期短的系统根本不需要什么复杂的数据存储如果要做成SaaS平台方案一或方案三是更好的选择。4.2 数据权限的设计要点低代码平台的数据权限是后端设计里一个绕不开的硬骨头也是很多平台被客户吐槽最多的地方。原因在于传统开发里数据权限是每个接口自己控制的。比如订单列表接口里写死只能查到自己负责区域的订单页面调用时天然就带上了这个逻辑。但低代码平台不一样页面是配置出来的接口是通用接口平台根本不知道这个页面的数据应该按什么规则过滤。所以我建议在平台里内置一套数据权限模型核心是行级权限和列级权限。行级权限用规则表达式来配置比如{ resource: order, rule: { operator: or, conditions: [ { field: ownerUserId, operator: eq, value: {{ currentUser.id }} }, { field: status, operator: in, value: [pending, processing] } ] } }渲染引擎发起数据请求时把当前用户的上下文注入到规则表达式里解析成SQL的WHERE条件拼到查询上。列级权限则是在接口返回前对数据做字段级别的过滤比如普通员工查不到薪资字段。这里有一个很重要的经验权限规则一定要在服务端做兜底校验前端隐藏字段只是体验优化真正生效的过滤必须发生在后端。否则只要有人绕过你的前端直接调接口所有权限都形同虚设。4.3 后端服务的设计思路低代码平台的后端本质上是一个元数据解释型的通用数据服务。它不是一个一个业务接口而是一套通用的资源接口把CRUD操作解析成针对具体元数据的查询和写入。比如一个请求长这样POST /api/v1/resource/order Body: { data: { customerName: 张三, amount: 1000 } }后端服务拿到这个请求会去元数据Context里找到order这个资源的定义校验字段是否存在、类型是否匹配、必填项是否完整然后才真正写到数据存储里。整个过程中后端代码不需要为订单或客户写一行专用的业务逻辑。这就带来了一个额外的好处通用接口的开发量是固定的增加一个业务对象只需要在元数据里定义它后端代码一行不用改。对交付团队来说这意味着新需求上线的时间大幅压缩。但代价也存在通用接口的性能优化空间有限无法针对某个特定接口做查询优化。所以做平台时一定要预留一个自定义后端接口的扩展口允许开发者在通用接口不满足需求时写一个自定义的后端方法手动实现复杂逻辑然后像调用标准资源接口一样调用它。平台的核心是提效不是为了限制你。5. 扩展机制设计决定你的平台能走多远我见过不少低代码平台内部团队用得挺好一放到外部客户手里就崩了。原因往往不是技术不行而是扩展机制太弱——客户遇到平台覆盖不了的需求时没有合法的逃生通道只能抱怨平台不行。5.1 三个层次的扩展能力分三层设计扩展机制基本可以覆盖绝大多数场景。第一层是属性配置扩展。这是最轻量的扩展方式。平台的内置组件要预留足够多的配置项并且允许开发者自定义组件的属性schema。比如内置的表格组件除了列配置还能改分页行为、行选择模式、自定义操作列。属性配置扩展不要求写代码但对平台设计的前瞻性要求高需要提前想到各种可能的需求。第二层是脚本扩展。在Schema的某些节点上允许配置一段JavaScript表达式或者函数。最常见的是事件回调比如提交前校验数据格式列表数据加载后对字段做格式化。这里强烈建议使用沙箱环境来执行脚本不要直接用浏览器原生eval或new Function。简单做法是在Web Worker里跑脚本或者用一些Js沙箱库做隔离避免用户的脚本污染平台主环境。第三层是代码级扩展自定义组件。这是扩展机制里最重但最灵活的一层。平台定义好组件接口规范开发者按照规范写一个自定义组件注册之后就能像内置组件一样被拖拽到画布上使用。5.2 自定义组件的接口设计自定义组件需要遵守什么接口规范我建议至少包含这几个部分{ // 组件在注册表里的唯一标识 name: MyCustomTable, // 展示在物料面板里的元信息 meta: { title: 自定义表格, category: 数据展示, icon: table-icon, props: [/* 属性定义渲染引擎根据这个生成属性配置面板 */] }, // 渲染函数接收属性和上下文 render: (props, context) { /* 返回虚拟DOM节点 */ }, // 组件生命周期钩子 mounted: (el, props, context) {}, updated: (el, props, context) {}, destroyed: (el) {}, // 运行时暴露给外部的方法 methods: { reload: () {}, getSelectedRows: () [], } }自定义组件一旦注册它和内置组件的地位是一样的。渲染引擎不管组件是内置的还是外部的只认注册表。这个设计是平台生态建设的基础——将来你能开放第三方组件市场的可能性就在于这个接口规范。5.3 流程引擎低代码平台的分水岭如果表单引擎决定了一个低代码平台能用那流程引擎决定了一个平台是不是真的好用也是平台之间分水岭级别的差异。流程引擎要处理的东西比表单复杂得多。表单处理的是静态的字段关系流程处理的是动态的状态流转。一个审批流程可能涉及发起、部门审批、财务会签、总经理办结、驳回重填等多个环节每个环节还有不同的人员规则和操作按钮。在技术选型上行业内比较通用的做法是遵循BPMN 2.0标准来做流程引擎比如基于Flowable、Camunda这类开源工作流引擎做二次开发。低代码平台负责把可视化的流程配置转化为标准的BPMN定义文件流程引擎负责解析和执行BPMN模型。用标准的好处是平台使用者熟悉BPMN的表示法社区资料多而且Flowable这类框架本身就解决了流程持久化、版本管理、驳回、会签等难题比自己从零写一套流程引擎靠谱得多。但要注意的是BPMN的完整规范非常重如果全部开放给用户配置学习成本会飙升。我的建议是做一层流程模板引擎的封装平台内置几种常用流程模式单级审批、多级审批、条件分支等用户只需要填人员和条件平台自动生成对应的BPMN文件。这种简化版的流程设计器既好上手又保留了底层BPMN的完整能力作为优雅的过渡方案值得推荐。6. 落地过程中的实际经验与避坑清单到了最后一个章节我想把这几年来做低代码平台实际踩过的坑和一些心得体会如实分享给大家。这些经验不会出现在任何官方文档里但对正在做平台的团队可能非常有用。6.1 最容易失控的几个环节第一个失控点元数据变得越来越抽象直到谁也看不懂。很多团队一开始把元数据设计得特别优雅抽象出各种高级概念比如实体聚合值对象领域事件。结果实施到一半发现连写平台的团队自己都要靠查文档才能写对一份配置。记住元数据的核心是让配置者能理解不是让技术方案看起来高大上。如果一份Schema需要看两遍以上才能看懂那就是过度设计。宁可元数据笨一点、直白一点配置起来好懂比什么都强。第二个失控点性能问题在低代码平台里比传统开发更难排查。传统页面慢你直接看接口调用、看SQL就知道了。低代码平台的页面慢往往发生在渲染引擎解析Schema、动态组件加载、表达式解析这些中间层。建议从第一天起就给平台加上性能埋点每一项元数据解析耗时多少、组件渲染耗时多少、接口请求耗时多少都用日志记下来。尤其在Schema层级很深、组件很多的时候性能埋点是定位卡顿的唯一抓手。第三个失控点拖拽生成器变成了玩具。很多平台花了大力气把拖拽体验做得很流畅——组件可以随便拖、随便缩放、自由布局。但到了实际业务场景用户根本不需要这种像素级的自由度他们要的是表单、列表、详情页能用标准布局稳定地排出来。自由布局意味着渲染复杂度高、响应式适配难、生成的页面还容易在手机上变形。推荐的做法是提供标准布局容器栅格布局、单列布局、Tabs布局拖拽只在容器内生效自由度交给布局容器来控制。这种情况要取舍设计自由度和生产可用度后者永远是第一位。6.2 实施节奏先解决你自己团队的痛点做低代码平台最忌讳一上来就想着做出一个通用平台卖给全世界的客户。“遍布各种行业的通用”某种程度上等于“每个行业都用不顺手”。我的建议是反过来的路子先选一个垂直场景做成内部工具。选一个你团队最痛、重复度最高的场景比如运营后台管理页面用低代码平台把这个场景做深、做透过程中持续优化元数据模型和渲染引擎。等你自己团队用得顺手了再把平台推广到其他团队最后才考虑商业化。这一步还有个好处你自己的团队就是这个平台的第一批用户需求和反馈都是真实的、即时的迭代速度会快很多。等到平台稳定了再对外开放边界和分寸感也会更清晰。6.3 关于技术栈选择的几条朴素建议这个部分其实没有什么必须怎么选只有结合场景的推荐和取舍前端Vue和React都可以不用纠结。选React你可能会对富交互的组件生态更喜欢选Vue你在国内招人和学习资料方面可能更顺。如果非要说建议考虑团队熟悉的那个比选最热门的更靠谱。设计器建议用Typescript开发这个不用犹豫元数据、Schema、组件接口这些复杂类型定义TypeScript能在编译期帮你挡掉大量低级错误也能直接作为组件接口文档用。后端推荐Node.jsNestJS或JavaSpring Boot。Node.js更适合前端团队全栈化前后端统一写TypeScriptJava生态适合以后要接BPMN、规则引擎等重量级组件的平台。数据库如果没有特殊需求PostgreSQL是更好的选择JSONB字段类型和低代码平台的JSON存储方案配合得很好。最后不要一开始就引入微服务、消息队列、Kubernetes这些重型基础设施。低代码平台的核心复杂度在前端的渲染引擎和后端的元数据解释器其余的都是外围。等平台的业务规模真的到了需要拆分的时候你会发现架构演进是有章可循的不用一开始就背上大包袱。如果你打算现在动手我推荐起步路径是这样的先实现一个最简版的项目只支持表单配置和列表配置能够配置一个用户管理页面并跑通CRUD这个闭环能跑通再往外围探索扩张。这就像写好了一个基础代码骨架骨架立住了后面的肌肉才能长在上面。另外一个小建议在设计元数据协议时多去看看JSON Schema标准和OpenAPI规范虽然它们不是为低代码设计的但它们在表达数据结构和约束上非常成熟。借鉴它们的设计思路能让你的元数据模型从一开始就站在一个比较高的水平线上——这个经验应该算是低代码平台设计里被提到得最少的软性关键点了。