3. 接口契约设计原则

在分布式事务画布体系中,接口契约是服务间通信的“宪法”。本章聚焦四大核心原则:API First 设计、OpenAPI/Swagger 规范、契约即文档、契约即测试。这些原则确保画布中的每个事务参与者(如 TCC 的 Try/Confirm/Cancel、Saga 的补偿步骤)都能以一致、可验证的方式交互。

3.1 API First 设计:先契约,后实现

API First 要求在设计阶段先定义接口契约,再编写业务代码。这避免了“实现驱动接口”导致的语义不一致和频繁变更。

核心流程:
  1. 业务场景分析:识别分布式事务中的参与者(订单、库存、账户)及其操作(预留、提交、回滚)。
  2. 契约定义:使用 OpenAPI 规范描述每个操作的请求/响应结构、状态码、错误格式。
  3. 契约评审:跨团队(业务、开发、测试)基于契约达成共识。
  4. Mock 与并行开发:基于契约生成 Mock Server,各团队可独立开发。
  5. 实现与验证:实现代码通过契约测试确保与定义一致。
API First 设计流程 业务场景分析 契约定义 契约评审 Mock 并行开发 实现与验证 反馈迭代 先契约后代码,减少返工

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 客户端/服务端桩代码。
契约即文档:从 YAML 到可视化文档 OpenAPI 契约 order-service.yaml x-transaction-phase x-compensate-path 文档引擎 Swagger UI Redoc 可视化文档 接口参数表格 事务流程图 Go 代码示例 curl 命令 文档与契约同步,永不脱节

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 步骤,确保每次提交都通过契约测试。