技术

底座能力的边界:什么该沉进数据字典,什么必须单独立表

B 端底座能力的边界判断:哪些配置该沉进数据字典、哪些必须单独立表,以及单级字典不升级成树的理由。

目录
  1. 判断标准只有一条:它是不是"纯枚举"
  2. 汇率为什么必须单独立表
  3. 税码为什么不属于字典
  4. 单级字典就够了,不升级成树
  5. 底座该提供什么,不该提供什么
  6. 一条容易被忽略的约束:迁移与建表策略
  7. 小结

平台底座做久了会发现,"这个能不能用字典配"是一个高频但容易答错的问题。

判断标准只有一条:它是不是"纯枚举"

数据字典适合承载的是取值固定、没有独立生命周期、不需要被业务单据直接外键引用的枚举。典型如:客户分类、供应商分类、区域、行业、原因码、结算方式、币种代码。

不适合的是那些自己会变化、需要被引用、需要留历史的对象。

用这条标准检验几个常见候选:

候选能否沉字典理由
币种代码可以ISO 4217 固定集合,业务只需读代码与精度
结算方式可以纯枚举,变化频率极低
客户/供应商分类可以分级靠编码前缀即可,不需要树
税码不建议税码是客户/供应商主数据上的属性,税率会随时间调整,且有申报口径
汇率不可以有时效性、需按币种对与日期查询、是计算依据

汇率为什么必须单独立表

汇率有两个字典给不了的能力:

  1. 时间维度。同一个币种对,不同日期的汇率不同,业务单据必须能取到"单据日期对应那一版"的汇率。字典项没有生效区间。
  2. 双向与精度。汇率是有方向和小数位要求的数值,不是标签。

用字典硬塞,最后一定会演化成"把汇率拼进字典项的扩展字段里再按日期解析",这比一开始就建表麻烦十倍。

所以汇率单独建表、单独页面管理,并且明确:单据在创建时快照汇率,不随后续汇率调整而变。

税码为什么不属于字典

税码看起来像枚举,实际上它是客户/供应商主数据上的采购/销售属性。同一个物料卖给不同客户,适用的税码可能不同;同一个供应商供不同物料,进项税码也可能不同。

把它做成全局字典,会导致业务在单据上无法区分"这条行项目的税率从哪来"。放在主数据上,来源唯一,追溯清晰。

这条结论在评审时被反复确认过:税码是主数据字段,不是字典项。

单级字典就够了,不升级成树

底座现有的字典是单级的:一个类型下挂若干项,没有父子层级,也没有生效区间。

评审时讨论过要不要升级:

  • 加树结构:分类类数据(如物料分类)确实有多级需求;
  • 加生效区间:看起来能给字典项做版本管理。

最后的决定是都不加,理由分别是:

分类不用树,用编码前缀。 三级分类用 01、0101、010101 这样的编码就能表达层级,查询时用 LIKE '01%' 即可。真正需要树结构的场景(比如组织架构、BOM)本身就不是字典该管的对象。为一个边缘需求给全平台字典加一套递归查询,收益不成立。

生效区间不加。 需要版本语义的对象(如 BOM)已经在各自的领域模型里用生效区间解决。字典项如果也要区间,说明它其实不是字典项,是业务实体。

这两条决定的本质是:底座的能力边界要窄而硬,业务差异放在业务模块里解决。底座一旦为了通用性引入复杂度,所有接入方都要为它付维护成本。

底座该提供什么,不该提供什么

在 super-admin 这个底座上,我划的线大致是:

能力归属
RBAC、组织、用户、菜单权限底座
工作流引擎(四表模型、节点类型、转办/加签/撤回/抄送)底座
数据字典(单级枚举)底座
审计日志、操作留痕底座
主数据(客户/供应商/物料/BOM)业务模块
汇率、税率、多币种结算业务模块(单独表)
业务单据状态机业务模块

判断依据很简单:底座负责"谁能在系统里做什么",业务模块负责"业务长什么样"。 工作流之所以在底座,是因为几乎所有业务单据都需要审批,且审批的语义与业务无关;主数据之所以不在底座,是因为每类业务的字段、状态、引用约束都不同。

一条容易被忽略的约束:迁移与建表策略

底座的能力再清楚,落地时也会被工程细节绊一下。比如用 TypeORM 的项目里,data-source.ts 只手写了几个实体,DB_SYNC=false,migrations 目录又是空的——部署到空库时直接报实体元数据找不到。

处理办法是:实体注册改成 glob 扫描模块目录,保证所有实体都被加载;在没有迁移文件的情况下,先用 DB_SYNC=true 让同步建表跑通,再把迁移补起来。这个顺序不要反,反了就是空库启动失败。

小结

"能不能用字典"这个问题,答案不在字典本身,而在对象的语义:有没有时间维度、要不要被引用、会不会有独立生命周期。

底座的克制,换来的是每个业务模块可以按自己的语义建模;底座的"通用",最后往往变成所有人的技术债。