由ruoyi的字典表勾出的一堆伪架构 —— 为了维护而维护不但不优雅, 还显得很笨.

先讲个低代码的笑话:

客户: 开发这个项目要多久?
程序员: 1个月
客户: 如果用开源项目改一改呢?
程序员: 不到1个月
客户: 如果用低代码开发呢?
程序员: 1年.


引子:当“配置即编码”变成了“维护即业务” 📦

低代码、后台脚手架、字典表、代码生成器,本意是降本增效。现实里,很多团队却把“字典表”用成了万能遥控器:

  • 枚举、标签、列名、校验规则、流程状态、权限点……统统塞进 sys_dict。
  • 页面上“字典翻译”“字典标签”到处都是,缓存一层一层,刷新又怕脏。
  • 变更全靠“改表+热更新”,发布越来越少,偶发事故越来越多。

最后产物是一套“为了维护而维护”的伪架构:所有复杂性都被转移到了运行时和配置数据里,既不优雅,也不稳健。


一、字典表的合理边界到底在哪?

在像 RuoYi 这样的模板里,字典表通常承担这些合理职责:

  • 下拉选项与显示文本映射(如性别、简单标签)
  • 可本地化的展示文案(配合 i18n)
  • 极小变化频率、低风险的显示层参数

⚠️ 不合理扩张的常见表现:

  • 用字典来定义“业务域模型”的状态机与行为
  • 用字典替代代码中的枚举与校验规则
  • 用字典驱动接口入参/出参结构
  • 用字典存放权限、流程编排、收费策略等高风险逻辑

一句话:字典表适合“显示层的映射与轻配置”,不适合“领域模型与行为”。


二、伪架构的五大症状 🧩

  1. 类型安全退化
  • 代码里没有 enum,只有字符串/数字 + 字典翻译
  • 静态检查、IDE 补全、编译期约束全没了
  1. 读写复杂度激增
  • 每次查询都要做字典联表或翻译
  • 缓存层需要处理多维度失效与一致性
  1. 配置热更新成了悬崖
  • 运维“热改字典”= 改契约,导致历史数据与新逻辑不兼容
  • 回滚困难,无可审计的“业务行为变更”
  1. 测试不可重复
  • 一套系统依赖一堆初始字典数据
  • 单测/集成测试难以构造确定性环境
  1. 实施与交付的“黑箱化”
  • 现场实施人员改字典救火,但团队内无人知道系统真实可接受的取值与边界
  • 知识沉淀失败,问题复现依赖“环境快照”

三、为什么团队会走到这一步?🧭

  • 业务压力:要快,最好“明天就能改”
  • 组织分工:把变更从研发发布转移到实施/运维
  • 销售话术:全可配是卖点,变成“全都配”
  • 技术债滚雪球:一次妥协带来更多妥协

低代码不是原罪,把“设计期决策”挪到“运行时”才是风险根源。


四、正确姿势:清边界,做分层 ✅

  • 字典表只做“显示层映射”和“可本地化标签”
  • 领域状态、业务规则、结构约束放在代码(类型系统)里
  • 配置中心区分“运行时配置”和“设计期模型”,不混用
  • 翻译层承担“值→文案”的责任,不反向驱动领域行为

📌 基本原则:

  • 代码里的 enum 决定“允许值域”;字典只决定“这些值如何展示”
  • 改显示可以热;改值域必须走发布与数据迁移

五、落地方案(可操作清单)🛠️

  1. 枚举规范(后端)
  • 用 Java enum 定义值域,并在持久化层显式映射
  • 提供统一“枚举字典”只读接口给前端取 options
// 领域枚举(值域由代码定义)
public enum OrderStatus {
  CREATED(0), PAID(1), SHIPPED(2), DONE(3), CANCELED(4);
  private final int code;
  // getter & fromCode(...)
}
  1. 翻译层与 i18n
  • 字典表或资源文件仅承担“code → label”的映射
  • 允许多语言,禁止改变值域
  1. 缓存策略
  • 只缓存“dictType → [ {value,label} ]”
  • 变更通过事件广播/版本号兜底;失效粒度按 dictType
  1. 前端用法统一化
  • 统一组件从 /dict/{type} 取 options
  • 表格列渲染只做显示翻译,不参与逻辑判断
// 前端统一获取 options
const opts = await api.getDict('order_status')
// 业务逻辑判断用后端返回的 code,不依赖 label
  1. 代码生成器的“边界约束”
  • 允许在字段上标注一个枚举类型注解,生成展示层翻译
  • 禁止生成“可配置字段/可配置结构”的动态模型
  1. 数据迁移治理
  • 用 Flyway/Liquibase 管理“初始字典数据”和升级脚本
  • 对“值域变化”必须走版本化迁移与灰度

六、案例对比:订单状态 🔄

反例(典型“伪架构”)

  • 表字段存字符串 'created'/'paid'…
  • 页面 everywhere 用 dictTag 翻译
  • 实施可在 sys_dict 改/删状态项

问题:接口契约被现场热改,历史数据、报表、流程分支全部不可靠。

正例(推荐)

  • 表字段存 tinyint code,代码 enum 限定值域
  • sys_dict 仅提供 code 的 label,i18n 由资源文件/字典管理
  • 前后端逻辑基于 code,展示基于 label

SQL 示例:

ALTER TABLE t_order
  MODIFY COLUMN status TINYINT NOT NULL COMMENT 'OrderStatus code';

前端渲染:

<el-tag :type="statusTagMap[row.status]">
  {{ dictLabel('order_status', row.status) }}
</el-tag>

七、如何从泥潭抽身:迁移路线图 🧭

  1. 资产审计
  • 导出所有 dict_type 与业务引用点,区分“展示字典”和“值域字典”
  1. 分级治理
  • 对 SLA 关键枚举先“代码化”(引入 enum、校验、迁移脚本)
  • 低风险项保持字典,仅做显示
  1. 兼容过渡
  • 在 API 层加版本:v1 继续返回 label,v2 返回 code + label
  • 前端逐页迁移到 code 逻辑判断
  1. 自动化护栏
  • CI 加 schema 规则:禁止在运行时新增/删除“值域字典”的取值
  • 引入合规校验:字典只允许改 label,不允许改 value
  1. 债务清单与时间盒
  • 列出“字典驱动领域”的模块,按收益/风险排期,每期限定工时

八、结语:工具为人服务,而不是反过来 💡

把“设计期决策”塞进“运行时配置”看似灵活,实际是把复杂性与风险转嫁到了最脆弱的环节。

  • 架构的优雅来自清晰边界与约束
  • 维护的成本来自不必要的自由

低代码不是“全都能配”,而是“把该配的配好,把不该配的收回”。当我们把字典从“万能遥控器”收回到“展示层映射”,系统才会重新变得简单、稳定、可演进。


附:快速自查清单 ✅

  • 是否存在用字典控制值域/流程/权限的场景?
  • 是否能在代码里找到所有“允许值”的单一真相来源(enum)?
  • 测试环境是否能在不导入“现场字典”的情况下稳定运行?
  • 字典变更是否有版本化、审计、回滚策略?
  • 代码生成器是否越界生成了“动态结构/动态枚举”的能力?

如果以上有两条以上回答“不确定/否”,你大概率正被“为了维护而维护”的伪架构拖累。现在,就是把复杂性还给设计期、把确定性还给代码的时候。

posted @ 2025-08-25 16:15  Only丿阿海  阅读(4)  评论(0)    收藏  举报