4、数据契约的存储与检索:基于Git的存储方案、元数据仓库、契约的搜索与发现
数据契约写好了,版本也定了,接下来就是怎么存、怎么找的问题。我见过不少团队,契约写得漂漂亮亮,结果存在共享文件夹里,或者塞在Wiki某个角落。等到要用的时候,谁也找不到最新版本。说白了,存储和检索这件事,决定了数据契约能不能真正用起来。
4.1 基于Git的存储方案
Git是我个人最推荐的契约存储底座。为什么?因为它天然支持版本管理、分支协作、变更审计。你想想看,数据契约本质上就是一组结构化文本文件,Git处理这个再合适不过了。
4.1.1 仓库结构设计
我建议一个团队维护一个独立的契约仓库,不要跟业务代码混在一起。仓库结构可以这样组织:
data-contracts/
├── README.md
├── .gitignore
├── .github/
│ └── workflows/
│ └── validate-contracts.yml
├── contracts/
│ ├── product/
│ │ ├── product_created_v1.yaml
│ │ ├── product_updated_v1.yaml
│ │ └── product_deleted_v1.yaml
│ ├── order/
│ │ ├── order_placed_v1.yaml
│ │ ├── order_placed_v2.yaml
│ │ └── order_shipped_v1.yaml
│ └── user/
│ ├── user_registered_v1.yaml
│ └── user_profile_updated_v1.yaml
├── schemas/
│ ├── common/
│ │ ├── address.yaml
│ │ └── money.yaml
│ └── enums/
│ ├── order_status.yaml
│ └── user_type.yaml
├── templates/
│ └── contract_template.yaml
└── scripts/
├── validate.sh
└── publish.sh
这里有个细节:按领域划分目录,每个契约文件以事件名_版本号.yaml命名。我在项目中遇到过,有人把所有契约堆在一个目录里,三个月后目录里两百多个文件,找起来简直噩梦。
4.1.2 分支策略
Git分支怎么用?我建议参考Trunk-Based Development的简化版:
| 分支 | 用途 | 生命周期 |
|---|---|---|
main |
已发布、不可变的契约 | 永久 |
develop |
待评审的变更 | 短期 |
feature/xxx |
单个契约的新增或修改 | 几天到一周 |
release/x.y |
版本发布前的冻结检查 | 按需 |
嗯,这里要注意:一旦契约合并到main,就不允许再修改。如果发现错误,只能发布新版本。这个规则虽然严格,但能避免很多混乱。
main上修改契约文件,然后说"我就改个字段名,不影响兼容性"。结果下游消费者没收到通知,线上直接报错。从那以后,我强制要求所有变更必须走PR流程,并且触发自动化兼容性检查。
4.1.3 提交规范
Git提交信息要有固定格式,方便后续检索和审计。我推荐这种:
feat(contract): 新增订单已发货事件 v1
- 事件名称: order_shipped
- 版本: 1.0.0
- 生产者: order-service
- 消费者: notification-service, analytics-service
- 变更说明: 首次创建
BREAKING CHANGE: 无
4.2 元数据仓库
光有Git还不够。Git擅长存文件,但不擅长搜索和关联查询。这时候就需要元数据仓库了。
说白了,元数据仓库就是把契约的元信息提取出来,存到数据库里。这样你就可以问:"哪些契约涉及用户地址字段?"或者"订单服务发布了哪些事件?"
4.2.1 元数据模型
我设计过一套元数据模型,核心表结构大概这样:
| 表名 | 字段 | 说明 |
|---|---|---|
contracts |
id, name, version, domain, producer, status, created_at | 契约基本信息 |
contract_fields |
id, contract_id, field_path, field_type, required, description | 字段级元数据 |
contract_consumers |
id, contract_id, consumer_service, consumer_version | 消费者信息 |
contract_dependencies |
id, contract_id, depends_on_contract_id, dependency_type | 契约间依赖关系 |
contract_changes |
id, contract_id, change_type, old_version, new_version, commit_hash | 变更历史 |
4.2.2 同步机制
Git和元数据仓库之间怎么同步?我建议用Git Hooks或者CI/CD Pipeline:
- 开发者提交PR到
develop分支 - CI触发契约格式校验和兼容性检查
- PR合并到
main后,触发同步任务 - 同步任务解析YAML文件,提取元数据
- 元数据写入数据库,更新索引
这样做的好处是:元数据仓库始终与main分支保持一致,不会出现脏数据。
4.3 契约的搜索与发现
存储做好了,接下来就是怎么让团队找到需要的契约。我见过最糟糕的情况:新来的同事想订阅某个事件,不知道有没有现成的契约,结果自己又定义了一个重复的。
4.3.1 搜索维度
一个好的搜索系统应该支持以下维度:
- 按领域搜索:product、order、user等
- 按事件名称搜索:支持模糊匹配
- 按字段搜索:比如搜索"address"找到所有包含地址字段的契约
- 按生产者/消费者搜索:某个服务生产或消费了哪些契约
- 按版本搜索:查找某个版本范围内的契约
- 按变更类型搜索:查找近期有breaking change的契约
4.3.2 搜索实现方案
我个人习惯用Elasticsearch来做搜索索引。元数据仓库的数据同步到ES,前端提供一个搜索界面。搜索结果的展示可以这样:
搜索结果示例:
订单已发货事件 (order_shipped)
版本: v1 (最新), v2 (即将废弃)
生产者: order-service
消费者: notification-service, analytics-service
字段: order_id, shipping_address, tracking_number, estimated_delivery
最近变更: 2024-03-15 新增 tracking_number 字段 (兼容)
核心原则:搜索结果的展示要包含足够的信息,让用户一眼就能判断这个契约是不是自己需要的。不要只给个文件名。
4.3.3 契约发现机制
除了主动搜索,我还推荐建立被动发现机制:
- 订阅通知:团队可以订阅某个领域的契约变更通知
- 定期报告:每周自动生成契约变更报告,推送到团队群
- API网关集成:在API网关中嵌入契约查询接口,开发者可以直接通过命令行查询
- IDE插件:开发者在IDE中编写代码时,自动提示可用的数据契约
你想想看,如果开发者写代码的时候,IDE自动提示"这个字段对应的数据契约是xxx",是不是很爽?
4.4 整体架构图
下面这张图展示了数据契约存储与检索的整体架构:
这张图把整个流程串起来了:Git仓库存原始文件,CI/CD做校验和提取,元数据仓库存结构化信息,搜索层提供发现能力。每一层各司其职,缺一不可。
我的建议:不要一开始就把所有功能都做全。先搞定Git存储和基本的搜索,再逐步加入元数据仓库、IDE插件、通知机制。迭代推进,比一次性搞个大而全的系统要靠谱得多。