数据契约基础:什么是数据契约、为什么需要数据契约、数据契约的核心要素

大家好,我是老赵。今天咱们聊聊数据契约。

你可能听过「契约驱动开发」这个词。微服务架构里,服务之间怎么约定数据格式?怎么保证改了接口不会炸?数据契约就是干这个的。

说白了,数据契约就是服务之间关于数据交换的一份正式约定。它不光定义了字段类型,还规定了数据的含义、质量要求、可用性承诺。我习惯把它比作「数据界的 API 文档」,但比 API 文档更严格、更可执行。

什么是数据契约

数据契约是一组机器可读、人可理解的规范。它描述了数据提供方和数据消费方之间的交互规则。

举个例子。订单服务要给用户服务推送订单数据。传统做法是写个接口文档,发个 JSON 示例。但这样够吗?不够。字段改了没人通知,类型变了没人知道,数据延迟了没人负责。

数据契约把这些问题都摆到台面上。它明确告诉你:

  • 数据长什么样(Schema)
  • 每个字段是什么意思(语义)
  • 数据多久到、多可靠、多新鲜(SLA)

我在项目中遇到过最典型的场景:两个团队各自开发,联调时发现字段名对不上。A 团队叫 user_id,B 团队叫 userId。这种问题在数据契约下根本不会发生——契约就是唯一的真相来源。

核心观点:数据契约不是文档,是代码。它应该被版本管理、被测试、被自动校验。

为什么需要数据契约

你可能会问:我们之前没有数据契约,不也活得好好的?

嗯,确实能活。但代价是什么?

  • 联调痛苦:接口改了,下游不知道,线上出问题才排查
  • 数据质量差:字段为空、格式不对、含义模糊,全靠人工兜底
  • 变更成本高:改一个字段,要通知所有下游,挨个确认
  • 缺乏信任:每个服务都怀疑上游数据不可靠,加一堆校验和重试

我曾经在一个项目里,光处理数据格式不匹配的 bug 就花了三周。后来引入数据契约,这类问题减少了 80%。

数据契约带来的好处很实在:

  1. 减少沟通成本:契约就是文档,不用反复确认
  2. 提前发现问题:CI/CD 阶段就能校验契约是否被破坏
  3. 支持自动化:代码生成、Mock 数据、测试用例都能从契约自动生成
  4. 建立信任:提供方承诺 SLA,消费方按契约消费,双方都有安全感

我的经验:数据契约最适合在微服务架构中落地。单体应用里,数据契约的价值没那么大——反正都在一个进程里。但一旦拆成服务,数据契约就是你的「安全带」。

数据契约的核心要素

数据契约不是随便写写就行的。它有三个核心要素:Schema、语义、SLA。缺一个,契约就不完整。

1. Schema(模式)

Schema 定义了数据的结构。字段名、类型、是否必填、默认值、约束条件。这是数据契约最基础的部分。

我习惯用 Avro 或 Protobuf 来定义 Schema。它们比 JSON Schema 更紧凑,支持向前/向后兼容性检查。

// 一个简单的 Avro Schema 示例
{
  "type": "record",
  "name": "OrderEvent",
  "namespace": "com.example.contracts",
  "fields": [
    {"name": "order_id", "type": "string", "doc": "订单唯一标识"},
    {"name": "user_id", "type": "string", "doc": "用户ID"},
    {"name": "amount", "type": "double", "doc": "订单金额"},
    {"name": "status", "type": {"type": "enum", "name": "OrderStatus",
      "symbols": ["CREATED", "PAID", "SHIPPED", "CANCELLED"]}, "doc": "订单状态"},
    {"name": "created_at", "type": "long", "logicalType": "timestamp-millis",
      "doc": "创建时间(毫秒时间戳)"}
  ]
}

注意看,这里不光定义了类型,还加了 doc 字段说明含义。这就是 Schema 和语义的结合点。

避坑指南:我曾经见过一个团队,Schema 里所有字段都是 optional。结果消费方不敢用任何字段,因为不知道什么时候会为空。我的建议是:能必填就必填,非必填要给出默认值。

2. 语义(Semantics)

Schema 告诉你字段长什么样,语义告诉你字段是什么意思。

举个例子。一个 amount 字段,类型是 double。但它是人民币还是美元?含税还是不含税?是订单总金额还是用户实付金额?

这些信息 Schema 表达不了,需要语义来补充。

语义通常包含:

  • 业务含义:这个字段在业务场景中代表什么
  • 取值范围:合法值、边界值、特殊值
  • 单位:货币单位、时间单位、度量单位
  • 关联关系:这个字段和其他字段的关系
  • 数据来源:数据从哪里来,经过什么处理

我习惯在数据契约里加一个 语义层,用 Markdown 或 YAML 描述。比如:

# 语义定义
fields:
  amount:
    description: "用户实际支付的金额(不含运费)"
    currency: "CNY"
    range: "[0.01, 999999.99]"
    precision: "两位小数"
    source: "支付系统回调"
  status:
    description: "订单当前状态"
    enum_mapping:
      CREATED: "已创建,等待支付"
      PAID: "已支付,等待发货"
      SHIPPED: "已发货"
      CANCELLED: "已取消"
    transitions: ["CREATED->PAID", "CREATED->CANCELLED", "PAID->SHIPPED", "PAID->CANCELLED"]

你看,有了语义,消费方就知道 amount 是人民币、不含运费、最多两位小数。不用再去翻业务文档或者问人了。

3. SLA(服务等级协议)

SLA 是数据契约里最容易被忽略的部分。但它恰恰是最重要的。

数据提供方承诺:数据多久更新一次?数据延迟多少算正常?数据可用性是多少?

没有 SLA,消费方只能猜。猜错了,线上出问题,责任算谁的?

SLA 通常包含:

指标 说明 示例
新鲜度 数据从产生到可消费的最大延迟 ≤ 5 分钟(99.9% 的订单)
可用性 数据服务在指定时间内的可用比例 99.95%(月度)
完整性 数据记录不丢失的比例 ≥ 99.99%
准确性 数据值与真实值一致的比例 ≥ 99.9%
吞吐量 数据服务能处理的最大请求量 1000 TPS

我建议把 SLA 写进契约文件里,用机器可读的格式。这样监控系统可以自动校验 SLA 是否达标,不达标就告警。

我的习惯:SLA 要写「承诺值」和「测量方式」。比如「新鲜度 ≤ 5 分钟,以数据写入时间戳和消费方收到时间戳之差计算」。这样双方对 SLA 的理解完全一致,不会扯皮。

数据契约的完整结构

把三个要素合在一起,一个完整的数据契约长这样:

# 数据契约:订单事件
version: "1.2.0"
provider: "订单服务"
consumers: ["用户服务", "库存服务", "推荐服务"]

schema:
  format: "avro"
  file: "order_event.avsc"

semantics:
  description: "订单生命周期事件,包含订单创建、支付、发货、取消"
  fields:
    order_id: "全局唯一订单号,由订单服务生成"
    user_id: "用户ID,关联用户服务"
    amount: "用户实付金额,单位人民币元,两位小数"
    status: "订单状态,状态转换见 enum_mappings"
    created_at: "订单创建时间,UTC 毫秒时间戳"

sla:
  freshness: "≤ 5 分钟(99.9% 的订单)"
  availability: "99.95%(月度)"
  completeness: "≥ 99.99%"
  throughput: "1000 TPS"
  measurement:
    freshness: "数据写入时间戳 - 消费方收到时间戳"
    availability: "成功响应数 / 总请求数"

你看,一个文件就把所有信息说清楚了。提供方按这个承诺,消费方按这个消费。谁出了问题,对照契约一查就知道。

总结一下:数据契约 = Schema(结构) + 语义(含义) + SLA(承诺)。三者缺一不可。

数据契约的核心逻辑

为了让你更直观地理解数据契约在整个数据流中的位置,我画了一张图:

数据契约驱动开发核心逻辑 数据提供方 订单服务 / 用户服务 数据消费方 库存服务 / 推荐服务 数据契约 Schema(结构) 语义(含义) SLA(承诺) 契约校验(CI/CD) 兼容性检查 / 格式校验 / SLA 监控 提供方发布契约 消费方订阅契约 消费方按契约消费 提供方按契约承诺

这张图展示了数据契约的核心逻辑:提供方发布契约,消费方订阅契约,双方在契约的约束下生产和消费数据。CI/CD 阶段自动校验契约是否被破坏,确保变更安全可控。