Перейти к содержанию

Contract Specification Guide

1. Обзор

Контракт данных — это набор файлов, описывающих соглашение между производителями и потребителями данных.

2. Структура контракта (несколько файлов!)

Зачем несколько файлов?

Проблема единого YAML: - ❌ Конфликты в git при параллельных изменениях - ❌ Изменение retention трогает всю схему - ❌ Сложно делать code review - ❌ Нет разделения ответственности

Решение - разделение на файлы:

domains/{namespace}/{entity}/
├── contract.yaml           # Метаданные, владелец, ссылки (Data Owner)
├── schema.avsc             # Avro схема (Application Developer)
├── quality_rules.yml       # Правила качества (QA Engineer)
├── sla.yml                 # SLA, retention (Data Owner + SRE)
├── physical_layout.yml     # Партиционирование, индексы (Data Engineer)
└── runbook.md              # Операционная документация (SRE)

Разделение ответственности

Файл Кто редактирует Когда меняется версия
contract.yaml Data Owner Minor (новые теги, описание)
schema.avsc Application Developer Major/Minor (изменение схемы)
quality_rules.yml QA Engineer Patch (новые правила)
sla.yml Data Owner + SRE Patch (изменение SLA)
physical_layout.yml Data Engineer Не меняет версию!
runbook.md SRE Не меняет версию

3. Файл: contract.yaml

Назначение: Основная метаинформация о контракте.

Редактирует: Data Owner

# ═══════════════════════════════════════════════════════════════════════════
# DATA CONTRACT: sales/orders
# ═══════════════════════════════════════════════════════════════════════════

# Версии
spec_version: "1.0.0"        # Версия спецификации (не менять)
contract_version: "2.1.0"    # Версия контракта (SemVer: MAJOR.MINOR.PATCH)

# ───────────────────────────────────────────────────────────────────────────
# МЕТАДАННЫЕ
# ───────────────────────────────────────────────────────────────────────────
metadata:
  name: "orders"                          # Имя entity (snake_case)
  namespace: "sales"                      # Домен (snake_case)
  display_name: "Заказы клиентов"        # Человекочитаемое название
  description: |
    Заказы из системы 1С Бухгалтерия.
    Содержит информацию о всех заказах клиентов,
    включая сумму, статус и дату создания.

  # Владелец данных
  owner:
    team: "sales-integration"             # Команда-владелец
    email: "sales-data@company.ru"       # Email для уведомлений
    slack: "#sales-data-alerts"          # Slack канал
    on_call: "https://pagerduty.com/sales-integration"

  # Связанные системы
  systems:
    producer: "1C Enterprise v8.3"
    consumers:
      - name: "Data Warehouse"
        team: "analytics"
      - name: "BI Dashboard"
        team: "business-intelligence"
      - name: "ML Pipeline"
        team: "data-science"

  # Теги для поиска и фильтрации
  tags:
    - "revenue"
    - "critical"
    - "pii"              # Есть персональные данные
    - "gdpr"             # GDPR compliance требуется

  # Метаданные версии
  created_at: "2026-01-15"
  updated_at: "2026-01-23"
  created_by: "ivan.petrov@company.ru"

# ───────────────────────────────────────────────────────────────────────────
# ССЫЛКИ НА ДРУГИЕ ФАЙЛЫ КОНТРАКТА
# ───────────────────────────────────────────────────────────────────────────

# Avro схема данных
schema:
  file: "./schema.avsc"
  format: "avro"
  registry_url: "http://schema-registry:8081"

# Правила качества данных
quality_rules:
  file: "./quality_rules.yml"
  enabled: true

# SLA и retention политики
sla:
  file: "./sla.yml"

# Physical layout для Data Engineers
physical_layout:
  file: "./physical_layout.yml"

# Runbook для операторов
runbook:
  file: "./runbook.md"

# ───────────────────────────────────────────────────────────────────────────
# LINEAGE (Data Lineage)
# ───────────────────────────────────────────────────────────────────────────
lineage:
  # Upstream источники (откуда берутся данные)
  upstream:
    - system: "1C Enterprise"
      database: "postgres://1c-db.local/orders"
      tables:
        - "orders"
        - "order_items"

  # Downstream потребители (куда идут данные)
  downstream:
    - system: "Clickhouse DWH"
      database: "clickhouse://dwh.local/production"
      tables:
        - "sales.orders_fact"
    - system: "S3 Data Lake"
      path: "s3://data-lake/silver/sales/orders/"

# ───────────────────────────────────────────────────────────────────────────
# CHANGELOG (История изменений)
# ───────────────────────────────────────────────────────────────────────────
changelog:
  - version: "2.1.0"
    date: "2026-01-23"
    author: "ivan.petrov@company.ru"
    changes:
      - type: "added"
        description: "Добавлено поле discount_amount (nullable)"
      - type: "added"
        description: "Добавлено поле customer_phone (nullable)"
    breaking: false

  - version: "2.0.0"
    date: "2026-01-15"
    author: "maria.ivanova@company.ru"
    changes:
      - type: "changed"
        description: "Поле status теперь enum вместо string"
      - type: "removed"
        description: "Удалено deprecated поле old_customer_id"
    breaking: true

  - version: "1.0.0"
    date: "2025-12-01"
    author: "ivan.petrov@company.ru"
    changes:
      - type: "initial"
        description: "Первая версия контракта"
    breaking: false

4. Файл: schema.avsc

Назначение: Apache Avro схема данных.

Редактирует: Application Developer

Формат: JSON (Avro schema)

{
  "type": "record",
  "name": "Order",
  "namespace": "sales.orders",
  "doc": "Заказ клиента из системы 1С",
  "fields": [
    {
      "name": "order_id",
      "type": "string",
      "doc": "Уникальный идентификатор заказа (UUID)",
      "logicalType": "uuid"
    },
    {
      "name": "order_number",
      "type": "string",
      "doc": "Номер заказа в 1С (например: ЗК-001234)"
    },
    {
      "name": "customer_id",
      "type": "string",
      "doc": "Идентификатор клиента"
    },
    {
      "name": "customer_email",
      "type": "string",
      "doc": "Email клиента (PII!)"
    },
    {
      "name": "customer_phone",
      "type": ["null", "string"],
      "default": null,
      "doc": "Телефон клиента (PII!)"
    },
    {
      "name": "total_amount",
      "type": "double",
      "doc": "Сумма заказа"
    },
    {
      "name": "discount_amount",
      "type": ["null", "double"],
      "default": null,
      "doc": "Сумма скидки"
    },
    {
      "name": "currency",
      "type": "string",
      "default": "RUB",
      "doc": "Валюта заказа (ISO 4217)"
    },
    {
      "name": "status",
      "type": {
        "type": "enum",
        "name": "OrderStatus",
        "symbols": [
          "pending",
          "confirmed",
          "processing",
          "shipped",
          "delivered",
          "cancelled"
        ]
      },
      "doc": "Статус заказа"
    },
    {
      "name": "items_count",
      "type": "int",
      "doc": "Количество позиций в заказе"
    },
    {
      "name": "created_at",
      "type": {
        "type": "long",
        "logicalType": "timestamp-millis"
      },
      "doc": "Дата и время создания заказа (UTC)"
    },
    {
      "name": "updated_at",
      "type": {
        "type": "long",
        "logicalType": "timestamp-millis"
      },
      "doc": "Дата и время последнего обновления (UTC)"
    },
    {
      "name": "items",
      "type": {
        "type": "array",
        "items": {
          "type": "record",
          "name": "OrderItem",
          "fields": [
            {
              "name": "sku",
              "type": "string",
              "doc": "Артикул товара"
            },
            {
              "name": "name",
              "type": "string",
              "doc": "Название товара"
            },
            {
              "name": "quantity",
              "type": "int",
              "doc": "Количество"
            },
            {
              "name": "price",
              "type": "double",
              "doc": "Цена за единицу"
            },
            {
              "name": "discount",
              "type": ["null", "double"],
              "default": null,
              "doc": "Скидка на позицию"
            }
          ]
        }
      },
      "doc": "Товары в заказе"
    }
  ]
}

5. Файл: quality_rules.yml

Назначение: Правила качества данных для валидации.

Редактирует: QA Engineer

# ═══════════════════════════════════════════════════════════════════════════
# QUALITY RULES: sales/orders
# ═══════════════════════════════════════════════════════════════════════════

version: "2.1.0"

# ───────────────────────────────────────────────────────────────────────────
# ПРАВИЛА ВАЛИДАЦИИ
# ───────────────────────────────────────────────────────────────────────────

rules:
  # Обязательные поля
  - name: "required_fields"
    type: "not_null"
    fields:
      - "order_id"
      - "customer_id"
      - "customer_email"
      - "total_amount"
      - "currency"
      - "status"
      - "created_at"
    severity: "error"
    description: "Критически важные поля не могут быть NULL"

  # Диапазоны значений
  - name: "valid_amount"
    type: "range"
    field: "total_amount"
    min: 0.01
    max: 10000000
    severity: "error"
    description: "Сумма заказа должна быть положительной и разумной"

  - name: "valid_discount"
    type: "range"
    field: "discount_amount"
    min: 0
    max: "{total_amount}"  # Скидка не может быть больше суммы
    severity: "error"
    description: "Скидка не может быть отрицательной или больше суммы заказа"

  # Email формат
  - name: "valid_email"
    type: "regex"
    field: "customer_email"
    pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
    severity: "error"
    description: "Email должен иметь валидный формат"

  # Телефон формат (опционально)
  - name: "valid_phone"
    type: "regex"
    field: "customer_phone"
    pattern: "^\\+?[1-9]\\d{1,14}$"  # E.164 format
    severity: "warning"
    description: "Телефон должен быть в международном формате"
    allow_null: true

  # Order ID формат
  - name: "valid_order_id"
    type: "regex"
    field: "order_id"
    pattern: "^ord_[a-z0-9]{12}$"
    severity: "error"
    description: "Order ID должен соответствовать формату ord_[12 hex chars]"

  # Валюта из списка
  - name: "valid_currency"
    type: "enum"
    field: "currency"
    values: ["RUB", "USD", "EUR"]
    severity: "error"
    description: "Поддерживаемые валюты: RUB, USD, EUR"

  # Количество товаров
  - name: "valid_items_count"
    type: "range"
    field: "items_count"
    min: 1
    max: 1000
    severity: "error"
    description: "Заказ должен содержать от 1 до 1000 товаров"

  # Соответствие items_count и длины массива items
  - name: "items_count_matches_array"
    type: "custom"
    expression: "len(items) == items_count"
    severity: "error"
    description: "Поле items_count должно соответствовать фактическому количеству товаров"

  # Свежесть данных
  - name: "data_freshness"
    type: "freshness"
    field: "created_at"
    max_age: "P7D"  # 7 дней
    severity: "warning"
    description: "Заказ создан более 7 дней назад"

  # Логическая консистентность
  - name: "updated_after_created"
    type: "custom"
    expression: "updated_at >= created_at"
    severity: "error"
    description: "Дата обновления не может быть раньше даты создания"

# ───────────────────────────────────────────────────────────────────────────
# ПРАВИЛА ДЛЯ ТОВАРОВ (NESTED)
# ───────────────────────────────────────────────────────────────────────────

nested_rules:
  items:
    - name: "valid_item_quantity"
      type: "range"
      field: "quantity"
      min: 1
      max: 10000
      severity: "error"

    - name: "valid_item_price"
      type: "range"
      field: "price"
      min: 0.01
      max: 1000000
      severity: "error"

    - name: "valid_sku"
      type: "regex"
      field: "sku"
      pattern: "^[A-Z0-9]{6,20}$"
      severity: "error"

# ───────────────────────────────────────────────────────────────────────────
# МОНИТОРИНГ КАЧЕСТВА
# ───────────────────────────────────────────────────────────────────────────

monitoring:
  # Минимальный процент записей, которые должны проходить валидацию
  min_valid_percentage: 95.0

  # Алерты
  alerts:
    - condition: "valid_percentage < 95"
      severity: "critical"
      channel: "#sales-data-alerts"
      message: "Quality threshold breached for sales.orders"

    - condition: "valid_percentage < 99"
      severity: "warning"
      channel: "#sales-data-alerts"
      message: "Quality degradation for sales.orders"

6. Файл: sla.yml

Назначение: SLA, retention, freshness политики.

Редактирует: Data Owner + SRE

# ═══════════════════════════════════════════════════════════════════════════
# SLA: sales/orders
# ═══════════════════════════════════════════════════════════════════════════

version: "2.1.0"

# ───────────────────────────────────────────────────────────────────────────
# ДОСТУПНОСТЬ (Availability)
# ───────────────────────────────────────────────────────────────────────────
availability:
  # Процент времени когда данные поступают без перебоев
  target: "99.9%"

  # Расписание (когда данные должны быть доступны)
  schedule:
    # Рабочие дни: 24/7
    weekdays: "00:00-23:59"
    # Выходные: 24/7
    weekends: "00:00-23:59"

  # Плановые окна обслуживания
  maintenance_windows:
    - day: "sunday"
      time: "03:00-04:00"
      description: "Еженедельное обслуживание БД 1С"

# ───────────────────────────────────────────────────────────────────────────
# СВЕЖЕСТЬ ДАННЫХ (Freshness)
# ───────────────────────────────────────────────────────────────────────────
freshness:
  # Максимальный возраст данных
  max_age: "PT1H"           # 1 час (ISO 8601 duration)

  # Поле для измерения свежести
  timestamp_field: "created_at"

  # Алерты
  alert_threshold: "PT2H"   # Алерт если данные старше 2 часов
  critical_threshold: "PT4H" # Критический алерт если старше 4 часов

# ───────────────────────────────────────────────────────────────────────────
# ВРЕМЯ РЕАКЦИИ (Response Time)
# ───────────────────────────────────────────────────────────────────────────
response_time:
  # Время реакции на инциденты
  critical: "PT15M"         # 15 минут
  high: "PT1H"              # 1 час
  medium: "PT4H"            # 4 часа
  low: "P1D"                # 1 день

  # Время восстановления (MTTR)
  recovery_time:
    target: "PT2H"          # Цель: 2 часа
    maximum: "PT4H"         # Максимум: 4 часа

# ───────────────────────────────────────────────────────────────────────────
# RETENTION (Хранение данных)
# ───────────────────────────────────────────────────────────────────────────
retention:
  # RAW данные в Kafka
  raw:
    duration: "P7D"         # 7 дней
    reason: "Для переобработки при сбоях"

  # PROD данные в Kafka
  prod:
    duration: "P30D"        # 30 дней
    reason: "Для анализа и отладки"

  # DLQ данные в Kafka
  dlq:
    duration: "P90D"        # 90 дней (дольше для анализа проблем)
    reason: "Для расследования проблем качества"

  # Данные в DWH
  dwh:
    hot:
      duration: "P1Y"       # 1 год в hot storage
      storage_class: "ssd"

    warm:
      duration: "P3Y"       # 3 года в warm storage
      storage_class: "hdd"

    cold:
      duration: "P7Y"       # 7 лет в cold storage (законодательство РФ)
      storage_class: "s3_glacier"

  # GDPR compliance
  pii_deletion:
    enabled: true
    on_customer_request: "P30D"  # Удалить PII в течение 30 дней по запросу

# ───────────────────────────────────────────────────────────────────────────
# BACKPRESSURE & THROTTLING
# ───────────────────────────────────────────────────────────────────────────
throughput:
  # Ожидаемая пропускная способность
  expected:
    records_per_second: 100
    peak_records_per_second: 1000
    daily_volume: 500000

  # Лимиты
  limits:
    max_batch_size: 1000
    max_message_size_kb: 1024

# ───────────────────────────────────────────────────────────────────────────
# КОНТАКТЫ ДЛЯ ЭСКАЛАЦИИ
# ───────────────────────────────────────────────────────────────────────────
escalation:
  level_1:
    team: "sales-integration"
    slack: "#sales-data-alerts"
    email: "sales-data@company.ru"

  level_2:
    team: "data-platform"
    slack: "#data-platform-oncall"
    email: "data-platform@company.ru"
    pagerduty: "https://company.pagerduty.com/services/PXXXXXX"

  level_3:
    team: "engineering-leadership"
    email: "engineering-leads@company.ru"

7. Файл: physical_layout.yml

Назначение: Настройки для Data Engineers (партиционирование, сортировка, оптимизация).

Формат хранения: Apache Parquet (колоночный формат)

Таблицы: Apache Iceberg (ACID, schema evolution, time travel)

Редактирует: Data Engineer

⚠️ Важно: Изменение этого файла НЕ меняет версию контракта!

Ключевые концепции

Apache Iceberg: - Hidden partitioning (партиции скрыты от пользователя) - Schema evolution (изменение схемы без переписывания данных) - Time travel (чтение данных на определённый момент времени) - ACID гарантии - Snapshot isolation

Apache Parquet: - Колоночный формат (читаем только нужные колонки) - Column statistics (min/max/null_count для data skipping) - Bloom filters (для ускорения точечных запросов) - Dictionary encoding (для low cardinality колонок) - Compression codecs (ZSTD, Snappy, Gzip)

# ═══════════════════════════════════════════════════════════════════════════
# PHYSICAL LAYOUT: sales/orders
# ═══════════════════════════════════════════════════════════════════════════
#
# Формат: Apache Parquet + Apache Iceberg
# ═══════════════════════════════════════════════════════════════════════════

version: "1.2"

# ───────────────────────────────────────────────────────────────────────────
# ICEBERG TABLE CONFIGURATION
# ───────────────────────────────────────────────────────────────────────────
iceberg:
  format_version: 2  # v2 поддерживает row-level updates/deletes

  # Каталог метаданных
  # ⚠️ РЕКОМЕНДУЕТСЯ: gravitino (unified metadata catalog)
  # Альтернативы: rest | hive | glue
  catalog:
    type: "gravitino"  # gravitino | rest | hive | glue
    uri: "http://gravitino-server:8090"
    warehouse: "s3://data-lake/warehouse"
    database: "sales"
    table: "orders"

  properties:
    "write.format.default": "parquet"
    "write.parquet.compression-codec": "zstd"
    "write.target-file-size-bytes": "536870912"  # 512 MB
    "history.expire.min-snapshots-to-keep": "10"

# ───────────────────────────────────────────────────────────────────────────
# ПАРТИЦИОНИРОВАНИЕ (Iceberg Hidden Partitioning)
# ───────────────────────────────────────────────────────────────────────────
partitioning:
  strategy: "iceberg_hidden"

  spec:
    # Time-based partitioning
    - source_column: "created_at"
      transform: "day"  # year | month | day | hour
      partition_field: "created_at_day"
      reason: "Большинство запросов фильтруют по дате"

    # Bucket partitioning для распределения нагрузки
    - source_column: "customer_id"
      transform: "bucket[16]"  # hash mod 16
      partition_field: "customer_id_bucket"
      reason: "Распределение по customer_id для параллелизма"

  evolution:
    enabled: true
    reason: "Можно менять партиционирование без переписывания данных"

# ───────────────────────────────────────────────────────────────────────────
# СОРТИРОВКА (Iceberg Sort Order)
# ───────────────────────────────────────────────────────────────────────────
sort_order:
  enabled: true

  fields:
    - column: "customer_id"
      direction: "asc"
      null_order: "nulls-first"
      reason: "Частые запросы по customer_id"

    - column: "created_at"
      direction: "desc"
      null_order: "nulls-last"
      reason: "Последние заказы первыми"

    - column: "order_id"
      direction: "asc"
      null_order: "nulls-last"
      reason: "Детерминизм"

# ───────────────────────────────────────────────────────────────────────────
# PARQUET CONFIGURATION
# ───────────────────────────────────────────────────────────────────────────
parquet:
  row_group_size: 134217728  # 128 MB
  page_size: 1048576  # 1 MB

  compression:
    codec: "zstd"
    level: 3
    reason: "Баланс compression/speed"

  column_compression:
    - column: "status"
      codec: "dictionary"
      reason: "6 уникальных значений - dictionary эффективен"

    - column: "currency"
      codec: "dictionary"
      reason: "3 значения - dictionary encoding"

  # Bloom filters для point lookups
  bloom_filters:
    - column: "order_id"
      fpp: 0.01
      reason: "Частые точечные запросы"

    - column: "customer_id"
      fpp: 0.01
      reason: "Фильтрация по customer_id"

# ───────────────────────────────────────────────────────────────────────────
# COMPACTION & OPTIMIZATION
# ───────────────────────────────────────────────────────────────────────────
compaction:
  small_files:
    enabled: true
    target_file_size_mb: 512
    schedule: "0 2 * * *"  # Daily at 2 AM

  # Z-ordering для multi-dimensional queries
  z_order:
    enabled: true
    columns:
      - "customer_id"
      - "created_at"
    reason: "Оптимизация для запросов по нескольким колонкам"

# ───────────────────────────────────────────────────────────────────────────
# TIME TRAVEL & SNAPSHOTS
# ───────────────────────────────────────────────────────────────────────────
snapshots:
  retention:
    min_snapshots_to_keep: 10
    max_age_days: 30
    reason: "Для debugging и rollback"

  # Примеры Time Travel:
  # SELECT * FROM sales.orders TIMESTAMP AS OF '2026-01-23 10:00:00'
  # SELECT * FROM sales.orders VERSION AS OF 123456789

# ───────────────────────────────────────────────────────────────────────────
# QUERY PATTERNS
# ───────────────────────────────────────────────────────────────────────────
query_patterns:
  - name: "get_customer_orders"
    frequency: "very_high"
    query: |
      SELECT * FROM sales.orders
      WHERE customer_id = 'cust_123'
      ORDER BY created_at DESC LIMIT 100
    optimization: |
      - Partition pruning по customer_id_bucket
      - Bloom filter на customer_id
      - Sort order → sequential read

# ───────────────────────────────────────────────────────────────────────────
# PERFORMANCE TARGETS
# ───────────────────────────────────────────────────────────────────────────
performance:
  point_lookup:
    target_latency_p50: "50ms"
    target_latency_p99: "200ms"

  range_scan:
    target_latency_p50: "500ms"
    target_latency_p99: "2s"

  full_scan:
    target_throughput: "1GB/s per core"

# ───────────────────────────────────────────────────────────────────────────
# STORAGE
# ───────────────────────────────────────────────────────────────────────────
storage:
  avg_row_size_bytes: 2048
  compression_ratio: 4.5  # ZSTD level 3

  growth:
    daily_rows: 500000
    daily_size_compressed_gb: 0.22
    annual_size_compressed_tb: 0.08

  tiering:
    hot:
      duration: "P30D"
      storage_class: "S3 Standard"
    warm:
      duration: "P365D"
      storage_class: "S3 Intelligent-Tiering"
    cold:
      duration: "P7Y"
      storage_class: "S3 Glacier"

# ───────────────────────────────────────────────────────────────────────────
# MONITORING (Iceberg Metadata Tables)
# ───────────────────────────────────────────────────────────────────────────
monitoring:
  metrics:
    - name: "table_size_gb"
      query: "SELECT SUM(file_size_in_bytes)/1024^3 FROM sales.orders.files"

    - name: "small_files_count"
      query: "SELECT COUNT(*) FROM sales.orders.files WHERE file_size_in_bytes < 128*1024^2"

  maintenance:
    - task: "expire_snapshots"
      schedule: "0 3 * * 0"
      command: "CALL system.expire_snapshots('sales.orders', TIMESTAMP '2026-01-01', 10)"

    - task: "compact_small_files"
      schedule: "0 2 * * *"
      command: "CALL system.rewrite_data_files('sales.orders')"

Ключевые отличия от традиционных БД

Концепция Традиционная БД Iceberg + Parquet
Индексы B-tree, Hash индексы Bloom filters, Column statistics
Партиционирование Видно пользователю Hidden partitioning
Сортировка Физические индексы Sort order в файлах
Компрессия На уровне таблицы Column-level + dictionary encoding
Schema changes ALTER TABLE (блокирующие) Schema evolution (non-blocking)
Time Travel Нет (нужно делать backups) Встроенная поддержка snapshots
Materialized Views Традиционные MV Трансформации через Spark/Flink

Best Practices

  1. File Size: Целевой размер 512MB (баланс между parallelism и overhead)
  2. Partitioning: Используйте bucket[N] для high cardinality колонок
  3. Sort Order: Критичен для фильтрации - сортируйте по frequently filtered columns
  4. Bloom Filters: Добавляйте для ID колонок с point lookups
  5. Compression: ZSTD level 3 для баланса, Dictionary для enum/low cardinality
  6. Compaction: Регулярно объединяйте small files (daily/weekly)
  7. Snapshots: Expire старые snapshots (держите 10+ для debugging)

8. Semantic Versioning для контрактов

Правила версионирования

Тип изменения Файл Версия Пример
BREAKING schema.avsc MAJOR 2.0.03.0.0
- Удаление поля
- Изменение типа поля
- Переименование поля
NON-BREAKING schema.avsc MINOR 2.0.02.1.0
- Добавление nullable поля
- Добавление default значения
PATCH quality_rules.yml PATCH 2.1.02.1.1
- Новое правило качества
- Изменение SLA sla.yml PATCH
- Обновление описания contract.yaml PATCH
NO VERSION CHANGE physical_layout.yml - -
- Изменение индексов
- Изменение партиционирования
- Обновление runbook runbook.md -

Примеры версионирования

MAJOR (Breaking change)

# schema.avsc
# БЫЛО:
{"name": "status", "type": "string"}

# СТАЛО (breaking!):
{"name": "status", "type": {"type": "enum", "symbols": ["pending", "confirmed"]}}

# Версия: 1.5.0 → 2.0.0

MINOR (Non-breaking change)

# schema.avsc
# Добавлено новое поле с default:
{
  "name": "discount_amount",
  "type": ["null", "double"],
  "default": null
}

# Версия: 2.0.0 → 2.1.0

PATCH

# quality_rules.yml
# Добавлено новое правило:
- name: "valid_phone"
  type: "regex"
  field: "customer_phone"
  pattern: "^\\+?[1-9]\\d{1,14}$"

# Версия: 2.1.0 → 2.1.1

9. CI/CD валидация

CI/CD должен проверять:

  1. contract.yaml:
  2. Корректность YAML синтаксиса
  3. Наличие всех обязательных полей
  4. Существование ссылаемых файлов
  5. Соответствие semantic versioning

  6. schema.avsc:

  7. Корректность Avro schema
  8. Breaking changes detection
  9. Backward compatibility

  10. quality_rules.yml:

  11. Корректность синтаксиса
  12. Валидность выражений
  13. Ссылки на существующие поля

  14. sla.yml:

  15. Корректность ISO 8601 duration
  16. Валидность процентов

  17. physical_layout.yml:

  18. Ссылки на существующие поля в schema
  19. Валидность SQL в materialized views

См. 07_cicd_pipeline.md для деталей.


10. Migration Guide

При переходе с единого YAML на несколько файлов:

# Скрипт для миграции
python scripts/migrate_contract_to_multifile.py \
    --input contracts/domains/sales/orders/contract.yaml \
    --output contracts/domains/sales/orders/

Скрипт создаст: - contract.yaml (метаданные) - schema.avsc (из секции schema) - quality_rules.yml (из секции quality_rules) - sla.yml (из секции sla) - physical_layout.yml (новый, шаблон)


См. также