Skip to content
PПромтбук
RUEN
07Моделирование

Data contracts: producer ↔ consumer без сюрпризов

Schema versioning, политика breaking changes, валидация в pipeline, choice block vs warn, Avro/Protobuf/JSON Schema на практике.

Спроектируй data contract между {{producer}} и {{consumers}}. Транспорт: {{transport}}.

Зачем контракт

Без контракта producer катит «безобидное» изменение — переименовал поле, поменял тип, выкинул enum-значение — и downstream падают на проде через сутки, потому что schema-on-read узнала об этом первой. Контракт переносит проблему влево, на CI producer'а.

Что входит в контракт

  1. Schema — поля, типы, optionality, defaults.
  2. Semantics — что значит каждое поле, единицы, timezone, enum-значения.
  3. SLA — частота, задержка, объём, доступность.
  4. Owner — команда + on-call, не email-алиас.
  5. Versioning policy — что breaking, что нет.
  6. Validation — где валидируется и что происходит при нарушении.

Schema format — выбор

| Формат | Плюсы | Минусы | Когда | | Avro | Schema registry, evolution rules встроены | Бинарный, читать глазами больно | Kafka streaming | | Protobuf | Языковая поддержка, версионирование полей по номеру | Строже эволюция | gRPC, межсервисное | | JSON Schema | Человекочитаемо, валидаторы везде | Эволюция — на тебе | REST API, файлы | | Iceberg/Delta | Schema evolution в storage | Привязка к платформе | Lakehouse tables |

Правило: формат source-of-truth — Avro или Protobuf. JSON Schema — для документации API.

Политика breaking changes

Не breaking (safe):

  • Добавить optional поле с default
  • Добавить новое enum-значение (если consumer его игнорирует unknown-routed)
  • Добавить новый event type в топик

Breaking (требует версии):

  • Удалить или переименовать поле
  • Сменить тип (int → string)
  • Сделать optional обязательным
  • Удалить enum-значение
  • Изменить семантику поля (была «cents» — стала «dollars»)

Версионируй namespace: events.orders.v1.OrderPlaced. v2 живёт параллельно. Старая версия deprecate-ится с deadline.

Валидация в pipeline

Три точки:

  1. Producer CI — schema linter, compatibility check против registry. Block merge при breaking.
  2. Producer runtime — каждое сообщение валидируется до публикации. Невалидное → DLQ + alert.
  3. Consumer ingestion — повторная валидация (defence in depth). Невалидное → quarantine table.

Block vs Warn

  • Block при breaking change в schema registry — producer не может задеплоиться.
  • Warn при появлении нового optional поля — consumer узнаёт, но не падает.
  • Block при превышении SLA на задержку — page on-call.
  • Warn при росте volume на 50% — Slack-сообщение.

Tooling stack (пример)

Schema source: .avsc / .proto в репо producer
Registry: Confluent Schema Registry / Buf Schema Registry
CI check: buf breaking, avro-tools compatibility
Runtime validation: producer SDK (avro-python, protobuf-go)
Consumer side: schema-aware deserialiser + DLQ
Observability: schema_violations metric, contract_age dashboard

Документация

Каждый contract имеет:

  • README в репо: владелец, slack-канал, on-call, примеры
  • Generated docs из schema (комментарии в .proto → markdown)
  • Changelog: что изменилось, когда, breaking ли

Anti-patterns

  • ❌ "JSON-as-contract" без schema — типы плывут, optional vs required размывается
  • ❌ Полагаться на producer'а «он не сломает» — без CI-check сломает в течение квартала
  • ❌ Менять семантику поля без переименования (cents → dollars в том же price) — schema валидна, данные битые
  • ❌ Один топик на несколько event types без discriminator field — consumer не знает, что десериализовать
  • ❌ Deprecate старой версии без deadline — она будет жить вечно, оба варианта надо поддерживать
К подразделу «Моделирование»
Похожие промты