
钉钉宜搭、腾讯微搭、简道云、JeecgBoot……过去两年国内每一家低代码厂商的发布会PPT 几乎是同一张一个浏览器画布、一个 AI 副驾驶、一句业务人员也能搭。只有 Erupt 在做一件反方向的事——一行Erupt注解一个DataProxyT扩展点一组 Handler 接口。没有画布。没有AI 生成代码。注解就是 UI Schema 本身。这一期把我们为什么没去做拖拽画布的工程理由讲透。01国内低代码三个字底下藏着三种完全不同的东西先做一个不太友好的拆解。市面上喊自己低代码的产品可以分成三派——第一派画布即应用代表钉钉宜搭、腾讯微搭、简道云、明道云。特点浏览器里画一个表单发布就能用。配置存在 SaaS 内部数据库里导出仅作为备份。适合业务部门、运营、轻 IT。第二派画布生成代码代表JeecgBoot、JNPF。特点在线设计器画好表单导出一份 Java Vue 代码给你拿回去改。适合想快速跑通 PoC 的 Java 小团队。第三派代码即配置代表Erupt。特点没有画布没有生成注解就是配置。适合有工程团队、领域模型稳定、合规审计是硬约束的项目。02画布派最大的隐藏成本不是会用而是长大之后听起来很反直觉但仔细想想——画布派的产品前两个月真的爽业务自己拖几下admin 后台就跑起来了。可是当系统活到第二年下面这些问题会一起冒出来业务逻辑稍微复杂一点画布属性面板里写的那段 JS 就开始看不懂、不敢删、不能测。想 code review只能截图对比。想跨环境部署只能导出应用包。想跑单测没有这个概念。想接外部 HTTP / 消息队列 / Redis要么去找平台扩展要么不行。第二派画布生成代码听起来折中但它有一个致命的双向同步问题——一旦你回到 IDE 改了 service 层画布就不再可信。下次想用画布再加个字段要么承担合并冲突要么放弃画布。最后大家都在做同一件事用画布生成完之后永远不再回画布。那它和脚手架的区别到底在哪里03Erupt 的反向押注领域模型 注解 UI直接看代码——Erupt(name客户,powerPower(importabletrue,exporttrue))Table(namet_customer)EntitypublicclassCustomerextendsBaseModel{EruptField(viewsView(title客户名),editEdit(title客户名,notNulltrue,searchSearch))privateStringname;EruptField(viewsView(title等级),editEdit(title等级,typeEditType.CHOICE,choiceTypeChoiceType(vl{VL(valueA,labelA 类),VL(valueB,labelB 类)})))privateStringlevel;}3 个注解 2 个字段Erupt 自动给你生成——✅ 列表页搜索框、Excel 导入导出、按等级过滤✅ 新建 / 编辑表单必填校验、下拉选项✅ 权限 / 菜单 / API endpoint✅ 前端表格行为排序、列宽、操作列为什么这么短的注解能撑起整个 UI打开erupt-core/proxy/AnnotationProxyPool.java你会看到Erupt 启动时把Erupt、EruptField、View、Edit这些注解通过AnnotationProxyT,R序列化成 JSON前端 Angular 拿到 JSON 就动态渲染界面。也就是说——注解不是装饰品注解就是 UI Schema 的物化形态。只不过这份 schema 写在源码里、被 IDE 自动补全、被 Git 版本管理。04业务逻辑写在哪写在 Spring Bean 里画布派被问到的第一个问题永远是那如果业务逻辑很复杂怎么办宜搭、微搭的答案是事件 JS 片段——你在画布属性面板里写function onSubmit(formData) { ... }没有 IDE、没有 Spring 容器、没有事务。JeecgBoot 的答案是回 IDE 改 service——然后画布就废了。Erupt 的答案在erupt-annotation里有一个接口publicinterfaceDataProxyMODELextendsMetaProxyMODEL{defaultvoidvalidate(MODELmodel)throwsEruptException{}defaultvoidbeforeAdd(MODELmodel){}defaultvoidafterAdd(MODELmodel){}defaultvoidbeforeUpdate(MODELmodel){}defaultvoidafterUpdate(MODELmodel){}defaultvoidbeforeDelete(MODELmodel){}defaultvoidafterDelete(MODELmodel){}// ...}任意实现这个接口的 Spring Bean被Erupt(dataProxy ...)引用之后就在 admin 的增删改查生命周期里跑。它是普通的Service所以—— 能Autowired任何东西 能打Transactional、PreAuthorize 能调 JPA、Redis、MQ、HTTP 能 code review、写单测、IDE 重构ServicepublicclassCustomerProxyimplementsDataProxyCustomer{ResourceprivateCustomerServiceservice;ResourceprivateAuditLoggerauditLogger;OverrideTransactionalpublicvoidbeforeAdd(Customermodel){service.assertNameUnique(model.getName());model.setOwnerId(MetaContext.getUser().getId());}OverridepublicvoidafterUpdate(Customermodel){auditLogger.log(customer.update,model.getId());}}和画布派在属性面板里写 JS的本质区别是——这段代码跟你 service 层共享同一个事务管理器、同一个安全上下文、同一份单元测试。05所有动态行为都收敛到 Java 接口画布派第二个常吹的点是无代码动态行为——某个下拉框的选项动态拉取、某行只对管理员可见、某个按钮触发自定义流程……画布派的方案是事件配置面板 自创 DSL。Erupt 的方案是Handler 接口想做的事在注解里写实现这个接口数据过滤行级权限Filter(conditionHandler…)FilterHandler自定义操作按钮RowOperation(operationHandler…)OperationHandler动态权限控制Erupt(powerHandler…)PowerHandler下拉选项动态拉取ChoiceType(fetchHandler…)ChoiceFetchHandler输入框自动补全InputType(autoCompleteHandler…)AutoCompleteHandler源码位置erupt-annotation/src/main/java/xyz/erupt/annotation/fun/举个最常见的——只让用户看到自己创建的订单ServicepublicclassMyOrderFilterimplementsFilterHandler{OverridepublicStringfilter(Stringcondition,String[]params){LonguidMetaContext.getUser().getId();returnownerId uid;}}Erupt(name订单,filterFilter(conditionHandlerMyOrderFilter.class))EntitypublicclassOrderextendsBaseModel{/* ... */}注意这里的设计选择——Handler 不是字符串表达式不是平台 DSL是一个真的 Java 接口。这意味着——✅ 能Autowired注入RoleService、TenantContext✅ 能写单元测试✅ 能 IDE 跳转、追引用、重构✅ 复杂逻辑多租户、季度切片、A/B 实验就是普通 Java不需要学任何低代码 DSL我们的赌注是——宁可让你写 6 行 Java也不要让你学一套只能用一次的 DSL。06跟国内主流低代码到底怎么比放一张干货对比截图收藏用——真理来源宜搭SaaS 画布的内部 DBJeecgBoot一次性生成的.java/.vueEruptGit 里的.java源文件加一个字段宜搭进画布配 → 同步表结构JeecgBoot改实体 改 Vue 改 mapperEruptEruptField加一行Diff / Review宜搭截图对比JeecgBootgit diff但 diff 的是自动生成的代码Eruptgit diff且只 diff 注解业务逻辑写在哪宜搭表单后端事件 JS 片段JeecgBoot改 service / controllerEruptSpring Bean DataProxyT跨环境部署宜搭应用包导入导出JeecgBootjar 同步 SQLEruptmvn package一个 jar我们不是在说画布派一定错——对没有工程团队的业务部门来说在浏览器里搭出一个查库 App就是真实生产力。JeecgBoot 这类生成派也有它的位置——只要你只关心快速跑起来且永远不会回到画布它是合理的脚手架。但当系统已经长大、领域模型稳定、合规和审计是硬约束时——画布即应用会撞上「逻辑表达力上限」。画布生成代码会撞上「双向同步漂移」。这两个问题在第一性原理上都不可解——因为它们都在尝试既要画布的轻又要代码的强。Erupt 的反向押注是承认代码本身就是低代码的下限。注解已经是配置面再压缩就不诚实了。剩下的事交给 IDE、Git、Spring——这套已经被验证了 20 年的工具链。075 分钟跑通从空项目到 admin 页面如果你看完想立刻动手下面这条路径已经踩平了——Step 1建一个空 Spring Boot 工程引入 erupt-jpa 依赖Step 2在application.yml配数据库Step 3写第一个带Erupt的实体Step 4启动访问/默认账号 erupt / erupt整个过程不超过 5 分钟。跑通之后把本文 §04DataProxy和 §05Handler翻回来对照——加业务逻辑写DataProxyT。加动态过滤写FilterHandler。加自定义按钮写OperationHandler。每一步都是写 Java每一步都被 IDE、被 Git、被 CI 接管没有任何一步回到画布。写在最后低代码的尽头可能不是 AI 自动拖拽。而是——让代码本身成为最小的配置面。如果你认同这条路欢迎做三件事——⭐GitHub 给 Erupt 点个 Stargithub.com/erupts/erupt