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

Контракт, отказ и replay события

Ненормативный пример. Иллюстрирует INT-EVT-001, INT-EVT-002, INT-EVT-003, INT-EVT-004, INT-EVT-005, INT-EVT-009 и INT-EVT-013. Запрещено использовать показанный payload как контракт прикладного проекта.

Ограничения применимости: показано синтетическое событие изменения одного агрегата при at-least-once доставке. Пример не определяет broker, topology, retention, транзакционную технологию, формат project schema или процедуру операционного доступа к dead-letter storage. Показанная изоляция является рекомендуемым вариантом, а не обязательным компонентом каждого consumer.

Общий конверт и project payload

{
  "timestamp": "2026-07-24T12:34:56.123Z",
  "event.id": "evt-example-1",
  "event.name": "order.approved",
  "event.schema_version": 2,
  "producer.id": "orders-api",
  "trace_id": "0123456789abcdef0123456789abcdef",
  "span_id": "0123456789abcdef",
  "correlation_id": "corr-example-1",
  "aggregate.version": 7,
  "payload": {
    "order_id": "ord-example",
    "approved_at": "2026-07-24T12:34:56.123Z",
    "approval_channel": "operator"
  }
}

Общая часть проверяется schemas/event-envelope.schema.json. Project schema версии 2 дополнительно определяет aggregate.version и payload. Поле approval_channel является необязательным добавлением относительно версии 1; consumer версии 1 его игнорирует. Изменение смысла или типа существующего поля потребовало бы новой несовместимой версии и миграции.

Producer одной локальной транзакцией сохраняет изменение заказа и outbox-запись с сериализованным событием. Отдельный publisher отмечает запись отправленной только после положительного durable-подтверждения broker. При timeout или потере соединения publisher повторяет исходные bytes с тем же event.id, поэтому consumer ожидает redelivery.

Изоляция после исчерпания повторов

{
  "original_event_id": "evt-example-1",
  "contract_name": "order.approved",
  "contract_version": 2,
  "attempts": 5,
  "reason": "consumer_dependency_unavailable",
  "original_bytes_base64": "ZXhhbXBsZQ=="
}

Это запись изолированного хранилища, а не новое бизнес-событие. Значение original_bytes_base64 синтетическое.

Replay

Управляемый replay:

  1. читает неизменённые исходные bytes;
  2. повторно проверяет project schema версии 2;
  3. сохраняет event.id и aggregate.version;
  4. отправляет событие через обычный consumer path;
  5. применяет deduplication и сравнение версии атомарно с изменением проекции.

Повторный replay того же события не создаёт второй эффект. Если проекция уже применила версию 8, событие версии 7 не откатывает её состояние.