
1. 代码生成器为什么会沦为一次性脚手架先抛一个稍显扎心的结论大多数团队做低代码平台最早动工、最快出成果的模块往往是代码生成器可真正落地后最先被团队抛弃的通常也是它。原因很现实——第一版生成器花两周弄出来输入表结构一键生成增删改查大家皆大欢喜。等项目跑起来字段要加、接口要改、权限要调所有人发现改生成过的代码远比重跑一遍生成器来得快。于是生成器从此闲置平台上只留下一堆出厂后就没有回头路的手写代码。这种现象太普遍了以至于很多人默认代码生成器就是个脚手架工具把它和项目初始化模板画上等号。但低代码平台的商业逻辑不是这样运转的。低代码平台的核心价值在于配置一次、持续演进页面要跟着业务变接口要跟着数据变权限要跟着组织变。如果生成器只能帮你完成首次创建没有办法承担后续同步那它在平台里的定位就只是生命周期前 10% 的一次性劳工后面 90% 的维护成本依然要人力扛。这也是为什么构建可演进的低代码平台这句话里可演进三个字才是真正难啃的骨头。代码生成器要从一次性脚手架进化为持续演进的核心引擎必须先搞清楚它现在为什么会被弃用。我拆了一下大致逃不出这四类问题。第一类是生成物与模型之间的关联断裂。生成器跑完一遍把代码落在磁盘上模型信息只存在于生成器启动前的数据库连接串和模板变量的那一瞬间。等业务上加了个字段、改了个枚举没有机制把变更传导回生成物代码自然就分叉了。第二类是手工代码与生成代码不分区。很多人刚开始用模板引擎时图省事把业务逻辑直接写进模板比如在 Service 层模板里顺手拼了一段审批逻辑。等重新生成时这段手工逻辑要么被覆盖要么藏在模板里不断被复制到每一个新模块变成了团队中最隐秘的屎山来源。第三类是编辑器、元数据仓库、生成器三者的关系没有理清。不少平台把可视化编辑器直接绑死在一个大数据库表设计器上拖拽出来的配置转手就变成 SQL 语句再扔给生成器。这种链路看着很顺但其实每一步都是强耦合配置一改生成器要么全量重跑要么直接罢工。第四类是生成这个动作本身没有版本和追踪概念。低代码平台有大量元数据变更如果生成器每次都是全量生成、无差别覆盖团队不敢升级模型因为跑一次生成器等于全部推倒重来。这四类问题叠加起来生成器就只剩下初始搭建这一条命。要走出这个困境就得从架构层面重新回答三个问题模型放哪、渲染怎么隔离、生成物如何与模型保持同步。这三点对应到架构上正好是模板驱动与模型驱动的路线之争、生成器内部的渲染器设计以及生成后的增量同步机制。2. 模板驱动与模型驱动两条路线的底层分歧代码生成器的本质是把一份结构化描述转换为一份可运行代码。这个结构化描述是什么决定了整个架构的走向。2.1 模板驱动的逻辑把生成看作字符串渲染最广为人知的一类生成器把结构化描述定义成数据库表结构。操作方式通常是连上数据库读取 information_schema 里的表、字段、约束然后丢给 Velocity、FreeMarker 或者 Razor 这类模板引擎按既定的模板渲染出 JavaBean、Mapper、Service、Controller。典型代表就是当年红极一时的动软代码生成器 2.7.8以及国内大量以代码生成器名义发布的三层架构工具。这类工具的核心抽象其实只有两个数据表结构模型 模板集合。表结构模型描述了输入模板集合描述了输出。它的优点非常突出上手门槛极低任何一个会写 SQL 的人都能在十几分钟内跑通全流程。模板就是普通文本加占位符会写 Java 就会写模板。所以早期团队做内部提效工具时几乎无一例外都会选择这条路。但模板驱动的天花板也很明显。模板描述的是怎么渲染而不是生成什么。当你需要生成 API 接口文档、生成前端表单页、生成数据库初始化脚本、生成测试用例时模板会越来越复杂。因为每种产物的输入信息都不一样API 文档需要字段注释和接口语义前端表单需要校验规则和控件类型测试用例需要业务规则。这些东西数据库表结构里根本不存在于是你只能不断往模板里塞各种约定——字段名前缀代表控件类型注释里写正则表达式字典表里硬编码下拉选项。等到约定数量超过模板行数的时候这个系统就再也撑不住了。2.2 模型驱动的逻辑先有语义再有渲染模型驱动路线把结构化描述升级为领域模型。它不直接面向数据库表而是面向业务概念实体、聚合、值对象、枚举、领域服务、应用服务、出入参对象、页面表单、流程节点。所有渲染器只认一套统一的模型仓库接口至于这个模型是从数据库逆向来的还是从可视化编辑器拖出来的或者是 DDL 解析出来的都不重要。有了统一模型多个渲染器就可以各取所需。后端渲染器读取实体的字段和类型生成 Java 代码前端渲染器读取同一个实体的表单配置和校验规则生成 Vue 组件文档渲染器读取注释和关系定义生成接口文档数据库渲染器读取字段和索引定义生成迁移脚本。每一个渲染器都是模型仓库的一个纯函数消费者不修改模型不持有状态可以独立升级也可以按需插拔。这套设计的核心优势在于演进。业务增加一个字段模型里加一个属性所有渲染器在下次运行时自动同步产出。生成物不再是某个时刻的静态快照而是模型当前状态的投影。这正好对得上低代码平台配置一次、持续演进的诉求。2.3 两类路线怎么选没有最优只有取舍我不是说所有团队都应该一步到位上模型驱动。这个问题必须结合团队规模、平台定位和演进阶段来看。模板驱动更适合三种场景内部工具型生成器服务对象是本公司那几个后端开发大家能接受各种约定一次性交付型项目比如外包项目中快速搭建后台管理界面技术栈极其单一只会产出一种语言的 CRUD 代码。在这种场景下模板驱动效率最高维护成本也最低。模型驱动则适合平台型产品。当你的低代码平台要做成 N 个企业、N 条业务线、N 套技术栈都能使用的产品时没有统一模型仓库每个接入方都要定制一套模板生成器本身就会变成业务瓶颈。还有一个容易被忽略的点低代码平台的元数据仓库和代码生成器共享同一套领域模型时可视化配置、数据模型管理、代码生成三个模块才能形成闭环。否则前端拖拽配置存一份模型后端生成器读数据库表结构拿一份模型两份模型是两套世界观迟早要打架。这里我建议用一张表来做选型决策维度模板驱动模型驱动输入抽象数据库表结构领域模型元数据核心组件模板引擎模型仓库 多渲染器学习成本低高支持产物类型单技术栈代码为主代码、文档、脚本、配置多端产物演进能力弱约定膨胀不可控强模型变更自动传导典型代表动软代码生成器、MyBatis Generator、RuoYiJHipster JDL、EF Core Power Tools、平台型低代码生成引擎适用阶段内部提效、单项目脚手架平台化、多租户、多技术栈2.4 动软 2.7.8 的启示单机生成器的贡献与局限既然热搜词里反复出现动软代码生成器 2.7.8 原代码我多聊几句。动软这类老牌单机生成器在当年确实功不可没它把读表结构—选模板—生成三层代码这个流程打磨得非常顺手让很多刚接触三层架构的团队第一次体验到一键生成的快感。但它的架构本质上是数据库优先 单机模板渲染没有模型仓库没有增量同步没有协作能力。你用它在本地生成了一套代码提交到 Git 之后生成器本身并不参与后续演进。团队修改数据库加字段要么手工改代码要么再生成一次然后手动解决覆盖冲突。这就是为什么这类工具在个人项目和小团队里至今依然好用但没人会用它作为低代码平台的底座。一个平台型生成器的核心资产是模型仓库和演进机制不是模板。3. 让生成代码活下来可演进架构的关键设计选定了模型驱动这条主线下一步就要解决全文最关键的问题生成器生成的代码能不能在项目后续迭代中持续吸收变更。可演进架构我拆成四个关键设计点逐一展开。3.1 生成物与模型的双向同步不只是生成还要能回读大多数生成器只做单向转换模型往代码走。但真实业务里代码往往会先于模型发生变更尤其是初期快速迭代的时候。开发直接改数据库加了一个字段或者直接在 Service 里加了一个方法此时模型仓库不知道这些变化。如果模型不能自动吸收代码侧的变更模型仓库就会逐渐沦为摆设。所以一个可演进的生成器架构必须包含回读机制。回读的常见做法有三种数据库逆向回读从 information_schema 读取当前库表结构对比模型仓库中的实体定义生成差异建议代码注解回读解析已生成代码中的 JPA 注解或 Swagger 注解反推出模型变更Git 差异回读对比上一版生成物和当前代码的差异让开发选择哪些变更要固化到模型里。我实际项目里最常用的是第一种数据库逆向回读。它不依赖生成代码是否被手改过只要数据库是事实源就能拿到准确的结构差异。但也因此带来一个架构决定低代码平台里数据库表结构和领域模型到底谁才是事实源我建议把领域模型作为唯一事实源数据库结构只是领域模型的一种物理实现。一旦接受了这条原则回读机制就变成把物理变更反向同步回逻辑模型的配套工具而不是反过来推翻模型。3.2 生成代码的分层策略允许再生成的区域与手工区域的边界生成器最招人恨的行为就是覆盖代码。让开发在一个明确界定的空间里做手工修改是生成器获得信任的前提。我见过的可落地做法是把生成代码按可覆盖性分为三类。第一类是完全生成区域任何重新生成都会被覆盖包括实体类、DTO、数据库迁移脚本、基础 CRUD 接口定义。这些是机械产物不应该有人手工改。第二类是扩展区域生成器生成一个基础的 partial 类或基类并在其中预留扩展点开发在另一个文件里写业务逻辑。Java 可以用部分类或者继承C# 用 partial class前端可以分基础组件与业务组件两层。第三类是注册式扩展区域生成器生成一个可注册的接口或事件总线开发通过实现接口或监听事件来扩展行为生成器完全不去动这些实现类。以典型的分层式架构为例领域层的实体、值对象由生成器负责领域服务可以生成骨架但在方法体内部留空应用层的事务边界和用例编排由生成器生成模板具体业务规则通过注册方法回调基础设施层的数据访问代码全部生成但自定义查询方法写在另一个独立仓储接口里生成器不接管。这一套切下来开发者自己维护的代码和生成器管理的代码各占一块互不侵犯。3.3 增量生成的实现思路从全量重写到精准更新有了分层策略接下来就是增量生成。增量生成本质上是在生成器里做代码合并常见的做法是标记区合并。生成器在输出代码时在允许手写的区域内写入特定的标记注释例如// BEGIN: USER_IMPL // END: USER_IMPL 下一次生成时生成器解析已存在文件提取两个标记之间的用户代码重新生成模板内容再把用户代码塞回原位置。这个方法实现简单但有个致命弱点——如果开发者不遵守标记约定把逻辑写在标记区之外重新生成时依然会被覆盖。更好的方案是生成器记录每次生成的模型指纹把模型信息序列化成一个哈希值生成前对比磁盘文件指纹与模型仓库版本。如果文件被改动过生成器就进入三方合并流程类似 Git 的 diff 冲突处理。这个方案对开发者的约束最少但实现成本最高。我建议平台化的代码生成器启动时先做模型指纹校验再根据版本差异选择性触发全量、局部或冲突合并。至于具体选择哪种取决于团队是否愿意承担冲突解决成本。3.4 运行时与生成时的解耦配置层、模型层、生成层三层分离可演进架构再往上走一步就会碰到低代码平台真正的大考生成时逻辑和运行时逻辑解耦。很多低代码平台跑着跑着就卡死了卡死的原因往往是生成的代码只在生成时有效运行时环境一变就得重新生成。解耦的关键是把平台分成三层。配置层存放可视化编辑器产出的 JSON/DSL 配置包括数据模型、页面布局、表单规则、按钮权限、流程定义模型层把配置层数据解析成内存中的领域模型做合法性校验、默认值补全、版本管理生成层基于领域的模型生成源码、配置文件、部署脚本。平台运行时只依赖配置层和模型层不依赖生成层。也就是说即使某条业务链路没有触发代码生成运行时也能直接读取模型层解释执行。代码生成只是把模型层编译为部署产物的一个可选出口。这样设计后平台既能支持拖拽配置 解释执行的轻模式也能支持模型生成 编译部署的重模式两套模式共用一套模型才真正具备演进能力。4. 低代码平台语境下生成器该管到哪一层生成器在低代码平台里不是一个独立工具它是平台中枢神经系统的一部分。它的边界划在哪里直接影响平台上其他模块的架构设计。4.1 生成器与平台运行时的职责划分我见过不少低代码平台走入极端有的把生成器做成平台主入口所有配置交付时必须先编译成代码才能部署有的完全废除代码生成一切都在运行时解释执行。这两种极端各有问题。全量“代码化”路线的缺陷在交付速度配置改个表单必须重新生成、重新编译、重新部署敏捷性还不如手写代码。全解释执行路线的缺陷在性能和可控性不生成代码意味着引擎必须承载所有逻辑复杂流程和高并发场景下性能和可调试性都不理想。合理的划分方式是运行时优先解释生成器按需编译。普通配置改动走解释执行改动即刻生效关键路径或性能敏感模块通过生成器编译为代码提升运行效率。生成器在这里的服务对象是平台运行时不是最终用户因此它的输入输出都必须以平台中间产物为准而非以数据库表结构为准。4.2 多端生成一套模型如何支撑服务端、Web、小程序与异构部署低代码平台一定会遇到多端诉求同一个业务模型要同时生成后端接口、后台管理页面、用户端小程序甚至移动端 App。如果后端生成器和前端生成器各读各的模型多端一致性就无从谈起。模型仓库里必须维护一份与端无关的领域模型同时预留针对各个端的视图模型扩展点。领域模型包含实体、属性、关联、业务规则和权限定义视图模型在领域模型之上补充 UI 元数据比如列表页展示哪些字段、表单页控件类型、校验规则、查询条件。举个具体场景实体 Order 有金额字段使用 BigDecimal 类型。后端渲染器读取领域模型生成 Java BigDecimal 属性并在数据库迁移脚本中生成 decimal(10,2)前端 Web 渲染器读取同一字段渲染 el-input-number保留两位小数小程序渲染器读取该字段渲染 input typedigit。这就是一套模型多端渲染的基本形态。为了做到这一点生成器架构必须支持渲染器注册机制——模型仓库不关心你有多少个渲染器每个渲染器是独立插件通过配置决定启停顺序。4.3 从代码生成到配置生成的演进路径还有一个趋势值得关注代码生成器的远期形态可能不再以生成源代码为最终产物。低代码平台发展到一定阶段模型仓库本身就可以直接作为运行时配置被消费代码生成变成了配置编译的一个分支。这个演进路径大致分三步。第一阶段模型驱动代码生成。页面和接口都生成代码交付物是项目源码。第二阶段模型驱动配置生成。模型仓库同时产出代码和 JSON 配置运行时优先读取 JSON 配置解释执行。第三阶段配置化运行时 定向代码生成。平台主链路完全配置化只有性能热点和定制模块才通过生成器产出高性能代码。最终生成器的角色从什么都能生成变成只在需要的时候出手。4.4 一个特殊场景软著代码生成器网络热词里出现的软著代码生成器是个挺有意思的细分需求。很多软件企业申请软著时需要提交六十页源代码文档而有些项目的代码量远超过六十页人工整理又费时。这类场景下代码生成器反而被当作文档整理器在用从模型仓库里批量导出代码并排版成符合规范的文档。它其实印证了一个结论生成器并不拘泥于生成源代码这一种产物只要模型仓库足够干净它能支撑的产物种类可以远超最初设计。工程取舍的关键在于事先想清楚模型仓库面向的是哪些消费者而不是急着堆模板。5. 我踩过的坑与选型建议讲了这么多架构层面的事最后落回实践把我这些年做代码生成器踩过的坑和沉淀下来的选型建议交个底。5.1 五个让我印象深刻的坑第一个坑模板里塞业务逻辑。早期做内部工具时图方便把某条业务线特有的校验逻辑直接写进模板结果另一个业务线接入时发现模板完全不可复用。这个坑的直接后果是团队花了两周把模板里的业务逻辑全部剥出来重构为可配置规则。现在我的铁律是模板只允许出现与渲染相关的循环、条件、变量引用出现一次 if 参数不等于空就拼 SQL 的写法就是重构信号。第二个坑忽略了模型版本管理。早期模型仓库只有当前快照没有版本概念。后来有一次业务方改了一组字段定义连带影响十几个实体结果生成器跑完线上部分接口的请求参数和数据库结构不匹配排查了半天。现在每个模型实体都有版本号生成器默认断言版本一致性不匹配就拒绝生成并提示差异报告。第三个坑没有生成指纹。以为生成器只在构建时才跑结果协同开发时两个人同时跑生成器互相覆盖对方的文件。后来在每个生成文件头部写入生成器版本号和模型版本号再配一次增量合并才彻底解决。第四个坑允许生成范围过宽。一开始把 Controller 和 Service 都交给生成器全量管理导致开发想加自定义接口时无从下手只能绕过生成器手写另一个文件。后来划出固定扩展区在 Service 接口和实现类里预留了扩展方法区域开发在区域内手写代码重新生成时不会被覆盖大家才真正接受生成器。第五个坑忽略生成后的测试验证。生成器输出代码没有自动跑测试出现过生成出的代码编译失败因为模板用了某个已经不存在的依赖类。现在生成器在每次生成动作触发后会调用一个验证管道至少保证生成的代码可以编译再做接口冒烟。5.2 不同团队规模的架构落地方案对于十人以下、内部提效为主的小团队我建议不要追求模型驱动直接上模板引擎 数据库逆向即可。选型目标是把常见 CRUD 代码在几分钟内稳定产出把团队的重复劳动降下来。动软这类老工具依然能打或者基于 Spring Boot FreeMarker 自己撸一套都很适合。对于五十人以上、正在做平台型产品的中型团队模型驱动基本是必选项。建议以模型仓库 渲染器插件机制为核心先把数据模型、表单模型、权限模型的元数据结构定义清楚再逐步扩展多端渲染器。这一阶段的重点不是追求生成能力覆盖面而是先把同步机制做好让模型和生成物的关系从单向生成变为双向可追溯。对于平台已商业化、面对多租户多技术栈的大型团队需要考虑更重的配置化运行时架构。代码生成器降级为可插拔的编译出口平台主链路以配置模型 解释执行为主。这一阶段的架构核心是向配置编译演进让生成器服务于特殊场景而非通用场景。5.3 演进路线参考我建议不论当前处于哪个阶段都按三步走推进。第一步先做单点工具化把数据库表到代码的生成链路打通验证模板引擎和基础分层能力。哪怕只用模板驱动也要遵守模板不塞业务逻辑、生成文件带标记、允许手工扩展区这三个纪律。这套纪律是后续任何架构重构的基础因为生成器的价值不在于模板多漂亮而在于生成物能不能被人信任。第二步做模型仓库化把数据库逆向替换成模型仓库生成器从读表直接渲染升级为读模型多端渲染。这一步会带来明显的架构变化所有渲染器必须改为无状态函数只依赖模型上下文。第三步做运行时与生成时分离让平台解释执行链路和代码生成编译链路并存模型共用一份。到了这一步生成器就真正成为平台的一部分而不是悬在平台之上的独立工具。我个人在实际项目里最深的一点体会是代码生成器不是一个可以一锤子砸出来的交付物它更像一棵树需要不断根据平台的演进修正分支。架构选择上没有用哪个框架就对了的银弹只有把模型、渲染、同步、边界这几个底层问题想清楚后才能在各种具体场景里做出恰当的取舍。希望这篇总结能帮正在做低代码平台的朋友少走几条弯路。