3. 接口契约设计原则
在分布式事务画布体系中,接口契约是服务间通信的“宪法”。本章聚焦四大核心原则:API First 设计、OpenAPI/Swagger 规范、契约即文档、契约即测试。这些原则确保画布中的每个事务参与者(如 TCC 的 Try/Confirm/Cancel、Saga 的补偿步骤)都能以一致、可验证的方式交互。
3.1 API First 设计:先契约,后实现
API First 要求在设计阶段先定义接口契约,再编写业务代码。这避免了“实现驱动接口”导致的语义不一致和频繁变更。
核心流程:
- 业务场景分析:识别分布式事务中的参与者(订单、库存、账户)及其操作(预留、提交、回滚)。
- 契约定义:使用 OpenAPI 规范描述每个操作的请求/响应结构、状态码、错误格式。
- 契约评审:跨团队(业务、开发、测试)基于契约达成共识。
- Mock 与并行开发:基于契约生成 Mock Server,各团队可独立开发。
- 实现与验证:实现代码通过契约测试确保与定义一致。
3.2 OpenAPI/Swagger 规范
OpenAPI 3.0 是描述 RESTful API 的事实标准。在分布式事务画布中,每个事务步骤(Step)都应映射为一个 OpenAPI 路径(Path),并明确其幂等性、超时和补偿语义。
| OpenAPI 元素 | 分布式事务映射 | 示例(TCC Try) |
|---|---|---|
| 路径 (Path) | 事务操作端点 | /api/order/try |
| 请求体 (RequestBody) | 事务上下文(TxId、BranchId、Payload) | { "txId": "uuid", "amount": 100 } |
| 响应 (Response) | 事务状态(SUCCESS/FAIL/UNKNOWN) | { "status": "SUCCESS", "message": "" } |
| 错误码 (Error Code) | 事务异常类型 | 409 Conflict(资源锁定) |
| 扩展 (Extension) | 事务元数据(幂等键、超时时间) | x-transaction-timeout: 5000 |
最佳实践:在 OpenAPI 规范中使用
x- 扩展字段标记事务属性,例如 x-idempotent: true、x-compensate-path: /api/order/cancel。这使契约本身成为事务编排的“活文档”。
openapi: 3.0.3
info:
title: 分布式事务画布 - 订单服务
version: 1.0.0
paths:
/api/order/try:
post:
summary: TCC Try 阶段 - 预留资源
x-transaction-phase: try
x-idempotent: true
x-compensate-path: /api/order/cancel
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
txId:
type: string
format: uuid
branchId:
type: string
payload:
type: object
properties:
orderId:
type: string
amount:
type: number
responses:
'200':
description: 预留成功
content:
application/json:
schema:
type: object
properties:
status:
type: string
enum: [SUCCESS, FAIL, UNKNOWN]
message:
type: string
'409':
description: 资源冲突(已锁定)
3.3 契约即文档
基于 OpenAPI 规范,可以自动生成交互式文档(如 Swagger UI、Redoc)。在分布式事务画布中,文档不仅展示接口参数,还应可视化事务流程:
- 事务步骤图:从契约中提取
x-transaction-phase和x-compensate-path,自动生成 Saga/TCC 流程图。 - 状态机说明:每个接口的响应状态码映射到事务状态(如
200 → SUCCESS、409 → CONFLICT)。 - 调用示例:自动生成 curl 命令和 Go 客户端代码片段。
文档生成工具链:
- Swagger UI:浏览器内交互式测试。
- Redoc:静态站点,适合嵌入内部 Wiki。
- OpenAPI Generator:从契约生成 Go 客户端/服务端桩代码。
3.4 契约即测试
契约不仅是文档,更是可执行的测试边界。在分布式事务画布中,契约测试确保每个事务参与者(服务)的行为与契约一致,避免“协调者认为服务已提交,服务实际已回滚”的灾难。
| 测试类型 | 验证内容 | 工具 | 分布式事务场景 |
|---|---|---|---|
| 消费者驱动契约测试 | 提供方接口是否满足消费者期望 | Pact, Spring Cloud Contract | 订单服务调用库存服务的 Try 接口 |
| 提供方契约测试 | 提供方实现是否遵循 OpenAPI 规范 | Dredd, Postman/Schema 验证 | 库存服务返回的 status 字段是否在枚举内 |
| 事务状态机测试 | 接口响应状态码与事务状态转换是否一致 | 自定义测试框架 + OpenAPI | Try 返回 409 后,协调者是否触发 Cancel |
常见陷阱:仅测试“正常路径”(200 OK),忽略事务特有的“未知状态”(UNKNOWN)和“冲突状态”(409)。在分布式事务中,UNKNOWN 是最危险的状态,契约测试必须覆盖超时和重试场景。
// Go 契约测试示例(使用 Pact Go)
func TestOrderTryContract(t *testing.T) {
// 定义消费者期望
pact.AddInteraction().
UponReceiving("A request to try order").
WithRequest{
Method: "POST",
Path: "/api/order/try",
Body: map[string]interface{}{"txId": "abc-123", "amount": 100},
}.
WillRespondWith{
Status: 200,
Body: map[string]string{"status": "SUCCESS"},
}
// 执行测试
err := pact.Verify(func() error {
resp, _ := http.Post(server.URL+"/api/order/try", "application/json",
strings.NewReader(`{"txId":"abc-123","amount":100}`))
return nil
})
if err != nil {
t.Fatal("契约验证失败:", err)
}
}
3.5 原则协同:画布中的契约闭环
四个原则在分布式事务画布中形成闭环:
- API First 定义事务步骤的边界。
- OpenAPI 规范 提供机器可读的契约格式。
- 契约即文档 让团队和协调者理解事务流程。
- 契约即测试 在 CI/CD 中自动验证,防止契约腐化。
画布集成建议:在 Go 项目中,将 OpenAPI 文件放在
api/ 目录下,使用 go-swagger 或 oapi-codegen 生成服务端桩代码和客户端 SDK。在 CI 流水线中加入 dredd 或 pact 步骤,确保每次提交都通过契约测试。