3. 数据契约的 Schema 设计:字段定义、数据类型、约束条件、版本管理
好,咱们进入正题。上一章聊了数据契约是什么、为什么能保障安全,这一章咱们得落地了——Schema 怎么设计。
说白了,数据契约的 Schema 就是一份“数据接口的说明书”。你想想看,跨团队协作最怕什么?怕 A 团队说“我传了个 userId”,B 团队理解成“用户昵称”。Schema 就是用来消灭这种歧义的。
我个人习惯把 Schema 设计拆成四个维度:字段定义、数据类型、约束条件、版本管理。咱们一个一个来。
3.1 字段定义:名字要见名知意
字段名是契约的第一道防线。我在项目中遇到过最离谱的事——有个字段叫 f1,文档里写“字段1”,没人知道它存什么。后来查了三个月前的邮件才知道是“首次登录时间”。
所以我的建议是:
- 全小写 + 下划线:比如
user_id、created_at,别用驼峰,也别用拼音。 - 命名要自解释:
is_active比status好,phone_number比phone好。 - 加前缀区分业务域:比如
order_、user_、payment_,一眼就知道归属。
核心原则:字段名就是契约的“身份证”,别让下游团队猜。
3.2 数据类型:别让“字符串”背所有锅
很多团队图省事,所有字段都写成 string。嗯,这里要注意——数据类型越模糊,安全风险越大。
举个例子:一个“年龄”字段,你用 string 存,下游可能传进来“abc”,解析直接崩。用 int 就天然过滤掉了。
我常用的数据类型清单:
| 类型 | 适用场景 | 避坑点 |
|---|---|---|
string |
名称、描述、备注 | 必须限制最大长度,否则可能被注入超长文本 |
int |
ID、数量、年龄 | 注意范围,比如 uint32 还是 int64 |
float/double |
金额、百分比 | 金额建议用 decimal 避免精度丢失 |
boolean |
开关、状态标记 | 别用 0/1 代替,语义不清晰 |
timestamp |
时间点 | 统一用 UTC,别用本地时间 |
enum |
有限状态集合 | 必须显式列出所有枚举值 |
小技巧:我习惯在契约里加一个 example 字段,写一个真实示例值。比如 "phone_number": "13800138000",比任何文档都管用。
3.3 约束条件:给数据上“紧箍咒”
约束条件就是告诉下游:这个字段能是什么、不能是什么。我把它分成三类:
3.3.1 非空约束
这个最简单,但最容易漏。我曾经遇到一个支付系统,amount 字段没设非空,结果某次上游没传,下游直接扣了 0 元——嗯,那天的工单我到现在还记得。
- 用
required: true标记必填字段 - 关键字段如
user_id、order_id、amount必须非空
3.3.2 唯一约束
唯一约束不只是数据库的事,契约里也要声明。比如 email 字段,如果两个用户传了同一个邮箱,业务逻辑会乱套。
- 用
unique: true标记 - 注意:唯一约束通常只在“主键”或“业务标识”上使用
3.3.3 枚举约束
这个我特别想多说两句。很多团队用 string 存状态,然后文档里写“取值:0-待支付,1-已支付,2-已退款”。结果下游传了个“3”,系统直接崩了。
正确的做法:在契约里显式定义枚举。
// 错误示范
"status": {
"type": "string",
"description": "订单状态"
}
// 正确示范
"status": {
"type": "string",
"enum": ["pending", "paid", "refunded", "cancelled"],
"description": "订单状态:pending-待支付,paid-已支付,refunded-已退款,cancelled-已取消"
}
警告:枚举值一旦发布,不要删除!只能新增。否则下游还在用旧值,上游突然不认了——这就是典型的“契约断裂”。
3.4 版本管理:让契约“活”起来
Schema 不是写一次就完事的。业务在变,字段在加,类型在改。没有版本管理,契约就是一纸空文。
我推荐语义化版本:MAJOR.MINOR.PATCH。
| 版本号变化 | 含义 | 示例 |
|---|---|---|
| MAJOR 增加 | 不兼容变更 | 删除字段、修改类型、重命名字段 |
| MINOR 增加 | 向后兼容的新增 | 新增可选字段、新增枚举值 |
| PATCH 增加 | 内部修复 | 修正描述、调整示例值 |
我个人习惯在契约文件头部加一个版本块:
{
"schema_version": "1.2.0",
"last_updated": "2025-03-15",
"changelog": [
"1.2.0 - 新增 phone_number 字段(可选)",
"1.1.0 - 新增 status 枚举值 'cancelled'",
"1.0.0 - 初始版本"
]
}
避坑指南:我曾经见过一个团队,版本号从 1.0.0 直接跳到 2.0.0,结果下游还在用 1.x 的客户端,全线报错。记住:MAJOR 版本升级必须通知所有下游,并给过渡期。
3.5 一张图看懂 Schema 设计
说了这么多,咱们用一张图把整个流程串起来:
3.6 实战建议:从“能用”到“好用”
最后,分享几个我在实战中沉淀下来的习惯:
- 先写契约,再写代码:我习惯在项目启动时就把 Schema 定下来,哪怕后面改,也比“先写代码再补契约”好一百倍。
- 契约要自动化校验:别靠人工 review。用工具(比如 JSON Schema、Protobuf)自动检查字段类型、约束是否合规。
- 版本变更要留痕:每次改 Schema,必须写 changelog。我曾经靠 changelog 定位过一次线上事故——原来是某个字段从
int改成了string,下游没适配。 - 给下游留缓冲期:MAJOR 版本升级,至少提前 2 个迭代通知。别搞“突然死亡”。
一句话总结:好的 Schema 设计,能让跨团队协作从“猜谜游戏”变成“按图索骥”。字段定义是地图,数据类型是路标,约束条件是护栏,版本管理是导航——缺一个,都可能翻车。