引言
数据模型设计器是低代码平台里最「危险」的组件。它让不懂数据库的人也能定义实体、字段、关系,一键生成物理表。方便的另一面是风险:一次误操作可能删列、改类型、丢数据。所以设计器的核心不是「能建表」,而是「能安全地建表、可回滚地演进」。
另一个常被低估的问题是「模型的双重身份」。在低代码平台里,数据模型既是存储结构的定义(物理表),又是界面的绑定目标(表单字段、列表列、查询条件)。同一份模型要被数据库、后端 API、前端渲染三处消费。如果这三处各自解释模型,就会漂移;正确做法是模型作为唯一真相,物理表、API、UI 都由它派生。
本文按「类型系统 → 关系建模 → 物理表生成 → 迁移 → 索引约束 → 外部数据源 → 校验 → UI 联动 → 多环境」展开,每节给出具体的 Schema 定义与 SQL。读完后你应当能判断:一个数据模型设计器在哪些地方必须保守,在哪些地方可以激进。
目录
- 数据模型设计器的职责
- 实体与字段的类型系统
- 关系建模
- 从模型到物理表
- 迁移与演进
- 索引与约束
- 数据源抽象与外部表
- 校验规则与模型约束
- 模型与 UI 的联动
- 版本与多环境
1. 数据模型设计器的职责
负责:
- 定义实体、字段、类型、约束
- 定义实体间关系与级联行为
- 生成 DDL 并执行迁移
- 为 API 生成 CRUD 接口
- 为 UI 提供字段元数据
不负责:
- 复杂业务逻辑(那是后端服务)
- 复杂查询与报表(那是查询层)
- 跨库分布式事务
职责边界决定了设计器的复杂度上限:它只做「结构」,不做「逻辑」。
2. 实体与字段的类型系统
类型系统是模型的基础,也是最需要克制的地方。
type FieldType =
| 'string' | 'text' | 'integer' | 'decimal'
| 'boolean' | 'date' | 'datetime' | 'enum'
| 'json' | 'file' | 'ref';
interface EntityField {
id: string;
name: string; // 列名,snake_case
label: string;
type: FieldType;
length?: number; // string 长度
precision?: number; // decimal 精度
scale?: number;
nullable?: boolean;
unique?: boolean;
defaultValue?: unknown;
enumValues?: string[]; // enum 取值
refEntity?: string; // ref 指向的实体
comment?: string;
}
| 平台类型 | PostgreSQL | MySQL | 说明 |
|---|---|---|---|
| string | varchar(n) | varchar(n) | 需指定长度 |
| text | text | text | 长文本 |
| integer | integer | int | 整数 |
| decimal | numeric(p,s) | decimal(p,s) | 金额 |
| boolean | boolean | tinyint(1) | 布尔 |
| datetime | timestamptz | datetime | 带时区 |
| json | jsonb | json | 结构化数据 |
| enum | varchar + check | enum | 枚举 |
平台类型与物理类型必须有一层映射,不要让用户直接选 varchar(255)。这层映射让平台可以在换数据库时统一翻译。
3. 关系建模
关系是数据模型的核心,也是最容易做错的部分。
一对一(1:1):用户 ↔ 用户档案
外键放在从表,加唯一约束
一对多(1:N):订单 → 订单项
外键放在「多」的一方
多对多(M:N):学生 ↔ 课程
需要中间表,含两个外键 + 联合唯一
{
"entities": {
"order": {
"fields": [
{ "name": "id", "type": "string", "unique": true },
{ "name": "customer_id", "type": "ref", "refEntity": "customer" }
]
},
"order_item": {
"fields": [
{ "name": "id", "type": "string" },
{ "name": "order_id", "type": "ref", "refEntity": "order", "nullable": false },
{ "name": "qty", "type": "integer" }
]
}
},
"relations": [
{ "type": "1:N", "from": "order", "to": "order_item", "onDelete": "cascade" }
]
}
3.1 级联行为的默认值
onDelete 的默认值应该是 restrict 而不是 cascade。删除主表连带删除从表数据是最危险的操作,必须让用户显式选择,且在删除前提示影响行数。
4. 从模型到物理表
DDL 生成是把模型变成可执行 SQL 的过程。
-- 由模型生成的建表语句(PostgreSQL)
CREATE TABLE "app_1024"."order" (
id varchar(36) PRIMARY KEY,
customer_id varchar(36) NOT NULL REFERENCES "app_1024"."customer"(id),
amount numeric(12,2) NOT NULL DEFAULT 0,
status varchar(20) NOT NULL DEFAULT 'draft',
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
tenant_id varchar(36) NOT NULL,
CONSTRAINT chk_status CHECK (status IN ('draft','paid','shipped'))
);
CREATE INDEX idx_order_tenant ON "app_1024"."order"(tenant_id);
生成规则:
1. 每个实体一张表
2. 每个字段一列(类型映射)
3. 主键固定为 id(varchar(36) 或 bigserial)
4. 审计列固定:created_at / updated_at / created_by
5. 多租户加 tenant_id 并建索引
6. ref 字段生成外键约束
7. 唯一约束与 check 约束按模型生成
4.1 系统列的约定
不要把所有系统列都暴露给用户编辑。created_at、updated_at、tenant_id 应由平台自动维护,用户可见但不可改。
5. 迁移与演进
模型改动的落地必须走迁移,且每一步可回滚。
迁移类型:
新增表 → CREATE TABLE(安全)
新增可空列 → ALTER TABLE ADD COLUMN(安全)
新增非空列 → 需默认值或分步(先可空 → 回填 → 设非空)
改列类型 → 可能锁表,需评估(用 USING 转换)
改列名 → 破坏性,需同步更新引用
删列 → 最危险,先软删(标记废弃)再物理删
interface Migration {
id: string;
version: number;
up: string[]; // 正向 SQL
down: string[]; // 回滚 SQL
destructive: boolean; // 是否破坏性
requiresConfirm?: string; // 需用户输入确认短语
}
5.1 分步迁移非空列
-- 第 1 步:加可空列
ALTER TABLE "order" ADD COLUMN remark varchar(200);
-- 第 2 步:回填历史数据
UPDATE "order" SET remark = '' WHERE remark IS NULL;
-- 第 3 步:设为非空
ALTER TABLE "order" ALTER COLUMN remark SET NOT NULL;
对千万级表,第 3 步在 PostgreSQL 11+ 是元数据操作(快),但 MySQL 可能需要重建表,必须用在线 DDL 工具。
6. 索引与约束
索引由平台自动建议,但允许用户调整。
自动生成的索引:
- 主键(隐含唯一索引)
- 外键列(加速关联查询)
- tenant_id(多租户隔离)
- 高频过滤字段(基于查询统计推荐)
用户可加:
- 唯一索引(业务唯一)
- 复合索引(多列组合查询)
CREATE UNIQUE INDEX uk_order_no ON "order"(tenant_id, order_no);
CREATE INDEX idx_order_status_created ON "order"(tenant_id, status, created_at DESC);
约束分三类:主键约束、唯一约束、检查约束。外键约束在多租户平台要谨慎——跨租户的外键必须避免,且删除时的影响要可控。
7. 数据源抽象与外部表
设计器不只管「平台自建的表」,还要能挂接外部数据源。
数据源类型:
1. 平台内建表(由设计器管理 DDL)
2. 外部库只读(映射已有表,不管理 DDL)
3. 外部 API(虚拟实体,无物理表)
统一抽象:
Entity {
kind: 'internal' | 'external' | 'virtual',
ddlManaged: boolean,
...
}
interface Entity {
id: string;
name: string;
kind: 'internal' | 'external' | 'virtual';
ddlManaged: boolean; // 平台是否管理其 DDL
fields: EntityField[];
dataSourceId?: string; // 外部数据源引用
}
对外部表,设计器只做「映射与元数据同步」,绝不允许改其结构——否则会破坏其他系统。
8. 校验规则与模型约束
模型层的校验是数据质量的最后一道防线。
三层校验:
1. 数据库层:NOT NULL / UNIQUE / CHECK / FK
2. 应用层:字段级校验(长度、范围、格式)
3. 业务层:跨实体规则(如库存足够)
低代码设计器通常管前两层,第三层交给流程/服务。
{
"name": "amount",
"type": "decimal",
"precision": 12,
"scale": 2,
"nullable": false,
"validators": [
{ "type": "min", "value": 0 },
{ "type": "max", "value": 1000000 }
]
}
校验规则同时写入数据库约束与元数据,这样绕过平台直连数据库也受约束保护。
9. 模型与 UI 的联动
模型是 UI 的上游,改模型要能自动反映到界面。
联动链路:
模型字段 → 表单字段(自动生成默认表单)
模型字段 → 列表列(自动生成默认列表)
模型关系 → 关联选择器(ref 字段渲染成下拉)
模型枚举 → 选项源
重命名字段的联动:
模型层改 name → 扫描所有引用 → 批量更新绑定 → 生成迁移
这要求平台维护一份「引用图」:哪个表单、哪个列表、哪个流程引用了哪个字段。没有引用图,重命名就会留下大量断链。
10. 版本与多环境
模型变更必须跨环境(开发 → 测试 → 生产)一致地推进。
环境流转:
开发环境改模型 → 生成迁移脚本 → 版本化
→ 测试环境执行 → 验证
→ 生产环境执行 → 记录
要点:
- 模型版本与应用版本绑定
- 迁移脚本随版本归档,可重放
- 生产迁移需审批 + 备份
- 结构漂移检测(实际表结构 vs 模型定义)
10.1 结构漂移
有人绕过平台直接改了数据库,导致实际表结构与模型定义不一致。平台应定期比对并告警,必要时提供「反向同步」把实际结构拉回模型。
权衡取舍
| 决策点 | 选项 A | 选项 B | 建议 |
|---|---|---|---|
| 类型暴露 | 直接选物理类型 | 平台类型映射 | 映射,便于换库 |
| 级联删除 | 默认 cascade | 默认 restrict | restrict,安全优先 |
| 删列 | 立即物理删 | 先软删后物理删 | 软删,留回滚窗口 |
| 外部表 | 可改结构 | 只读映射 | 只读,避免破坏他系统 |
| 迁移 | 自动直改 | 生成脚本 + 审批 | 脚本,可回滚可审计 |
常见坑清单
- 默认级联删除:删主表连带删从表,需默认 restrict 并提示影响行数。
- 直接暴露物理类型:换数据库时全量返工,需平台类型映射层。
- 加非空列一步到位:大表锁表或失败,需先可空、回填、再设非空。
- 删列即物理删:无法回滚,应先标记废弃、观察后再删。
- 无引用图:重命名字段留下断链,必须维护引用关系并批量更新。
- 外部表可改结构:破坏其他系统,外部表必须只读映射。
- 校验只写在应用层:绕过平台直连数据库即可绕过校验,需下沉到数据库约束。
- 跨租户外键:多租户下数据越权,外键必须带 tenant_id。
- 无结构漂移检测:手改数据库后模型与实际不一致,需定期比对告警。
- 迁移脚本不归档:环境不一致时无法重放,脚本必须随版本归档。
小结
数据模型设计器的骨架是「类型系统 → 关系 → DDL 生成 → 迁移 → 索引约束 → 数据源抽象 → 校验 → UI 联动 → 多环境」。贯穿全文的一条主线是保守:默认不级联、删除先软删、外部表只读、迁移走脚本。数据模型是平台里最难回滚的部分,激进的设计在这里代价最高。
另一条主线是单一真相:模型同时驱动物理表、API 与 UI,三者必须由同一份模型派生,否则必然漂移。这与 元数据驱动架构设计 的核心原则一致。
模型定义好之后,下一步是让它「动起来」:数据如何被流程编排、如何在节点间流转,见 低代码与工作流引擎集成 。而模型在多租户下的隔离与扩展,见 多租户与 SaaS 化落地 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。