Producer — компонент (микросервис, модуль, фоновый процесс), который формирует и публикует сообщение в брокер (топик Kafka, exchange/очередь RabbitMQ). Шаблон фиксирует контракт сообщения, параметры публикации и поведение при ошибках — для передачи в разработку и QA. Теория брокеров и паттернов EDA — в 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events) и 03.05.06 Event-Driven Architecture; машиночитаемый контракт — 03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf).

1. Назначение

Спецификацию Producer пишет системный аналитик; её читают разработка, QA, платформенная команда и владельцы 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ).

Шаблон закрывает четыре задачи:

  • Контракт сообщения — key, headers, payload, сериализация; narrative дополняет JSON Schema / AsyncAPI, не дублирует учебник по брокеру.
  • Поведение публикации — триггер, транзакционная согласованность (outbox), режим ожидания ack, политика повторов.
  • Эксплуатация — метрики, алерты, судьба сообщения после исчерпания retry, безопасность канала.
  • Трассировка — связь с FR/NFR, критериями приёмки и матрицей трассировки (10.01.15 Шаблон матрицы трассировки, 02.04.02 Критерии приёмки (Acceptance Criteria)).

Граница артефакта: Producer vs Outbox/Dispatcher

КомпонентЧто описывает спекаКогда выносить отдельно
Producer (клиент брокера)Формирование payload, key/headers, вызов API Kafka/RabbitMQ, ack/retry на уровне клиентаВсегда — ядро документа
Outbox WriterЗапись события в таблицу outbox в той же транзакции, что бизнес-данныеВ этой же спеке — подраздел §3.4; схема таблицы — 10.01.03 Шаблон спецификации БД сервиса; отдельный артефакт — если другая команда/репозиторий (03.01.03 Декомпозиция системы на подсистемы и модули)
Outbox DispatcherPolling outbox, отправка через Producer, смена статусовВ этой же спеке — §3.4 и §3.6; отдельная спека — если shared-платформа обслуживает много сервисов

В шапке документа явно укажите Scope: «одна спека на Order Service (outbox + dispatcher + producer client)» или «Producer client только; outbox — платформенный ADR». Не смешивайте без явной декларации — иначе QA и разработка расходятся в границах задачи.

2. Когда использовать

Подходит, если:

  • сервис публикует доменное событие или команду в Kafka или RabbitMQ;
  • нужен человекочитаемый контракт с триггером, матрицей ошибок и NFR задержки/throughput;
  • параллельно ведётся AsyncAPI / Schema Registry — markdown-спека добавляет семантику и governance.

Не подходит или требует дополнения, если:

Входные условия: границы сервиса (03.01.04 Интерфейсы и интеграционные взаимодействия), термины и поля payload (10.01.14 Шаблон глоссария и словаря данных), формат JSON — 07.03 Основы XML, JSON, YAML.

3. Структура документа

Скопируйте блок в Confluence или Git. Поля обяз. нужны для утверждения; да* — при выполнении условия в скобках.

Правило распределения смысла между полями:

Поле / разделЧто сюдаЧто не дублировать
Инициатор отправки (§3.1)Бизнес-что запускает публикацию: действие пользователя, шаг процесса, расписаниеТехнические детали post-commit, outbox — в §3.4
Условия отправки (§3.4)Техническое когда: после commit, outbox, режим sync/async, polling и SLA lagПовторная бизнес-формулировка инициатора
Повторные попытки (§3.6)Единственный нормативный блок retry/backoffВ §3.2 — только ссылка «см. §3.6»

3.0. Шапка governance

ПолеОбяз.Что писать
Scope компонентовдаProducer client / outbox writer / dispatcher — одна спека или перечень связанных ID
Версия документадаv1.0 — версия narrative-спеки
СтатусдаDraft → Review → Approved
Правило приоритетада«При расхождении приоритет у AsyncAPI/Schema Registry версии X; семантика триггеров и ошибок — у данного документа» (или наоборот — одно правило)
Именование окруженийдаorders.events (prod) vs orders.events.staging; префикс tenant при multi-tenant

3.1. Общая информация

ПолеОбяз.Что писать
Наименование ProducerдаУникальное имя клиента, напр. OrderEventsProducer
Бизнес-контекстдаДоменное событие, зачем публикуем, кто потребители; ссылка на UC/BPMN
Связанные требованиядаFR-…, NFR-…; трассировка — 10.01.15 Шаблон матрицы трассировки
Владелец компонентадаКоманда/сервис
Инициатор отправкидаБизнес-триггер: «оплата подтверждена», «смена статуса заказа» — без post-commit/outbox
Модель eventType на топик/exchangeдаЗафиксировать одно из: (1) один producer = один eventType; (2) один топик/exchange, envelope с полем eventType (как в примере §4); (3) отдельный топик или routing key на каждый тип. Без явного выбора команды плодят второй топик или смешивают контракты

3.2. Параметры брокера и топика/очереди

ПараметрОбяз.Что писать
Тип брокерадаApache Kafka или RabbitMQ (другие брокеры — по аналогии, вне scope шаблона)
Имя топика / exchange / очередидаПолное имя с учётом окружения, напр. orders.events
СериализациядаJSON / Avro / Protobuf; ссылка на схему в реестре — 03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf)
Subject / compatibility (Schema Registry)да**Avro/Protobuf — имя subject (напр. orders.events-value), уровень совместимости (BACKWARD, FULL); governance §3.8
Max размер сообщениядаЛимит платформы и payload (напр. 1 MB для Kafka по умолчанию); учитывать headers и сжатие
Степень сжатиянетgzip / snappy / none (Kafka)
Таймаут отправкиданапр. 2000 мс
Повторные попыткида«См. §3.6 — единственный нормативный источник»

Kafka (дополнительные поля):

ПараметрОбяз.*Что писать
Партиционированиеда**Kafka — ключ (orderId), число партиций; порядок только внутри одного ключа
acks / min.insync.replicasда*напр. acks=all, min.insync.replicas=2

RabbitMQ (дополнительные поля):

ПараметрОбяз.*Что писать
Exchange и типда*напр. orders.topic, тип topic
Routing keyда*напр. order.created.v1
Delivery modeда*persistent (2) vs transient (1)
Publisher confirmsда*Включены; ожидание confirm от broker — да/нет; таймаут
Кто объявляет exchangeда*Producer при старте / платформа (IaC) — зафиксировать
Mandatory / immediateнетLegacy; указать «не используется» или политику

Безопасность канала (чеклист, детали — 03.05.05 Безопасность API):

ПараметрЧто писать
TLSОбязателен на prod
АутентификацияSASL/SCRAM (Kafka), username/password или cert (RabbitMQ)
ACLПрава write на топик / publish на exchange

3.3. Структура сообщения

3.3.1. Ключ сообщения (Message Key) — Kafka

Поле ключаТипОписаниеПример
orderIdUUIDИдентификатор агрегата; все события по заказу — один ключ"a1b2c3d4-e5f6-7890-abcd-ef1234567890"

Логика выбора ключа: все события по одному заказу попадают в одну партицию; глобальный порядок между ключами не гарантируется.

Для RabbitMQ routing key задаётся в §3.2; отдельный message key не применяется.

3.3.2. Заголовки (Headers / Properties)

HeaderОбяз.ОписаниеПример
correlationIdдаСквозной ID бизнес-операцииUUID
traceIdдаDistributed tracing (OpenTelemetry)hex string
content-typeдаapplication/json / application/avro
schema-versionда**При эволюции схемы — 1.0.0
tenantIdда**Multi-tenant"tenant-42"

Маппинг по брокеру: в Kafka все поля — Record Headers. В RabbitMQ content-type и correlation-id — AMQP properties; traceId, tenantId, schema-version — таблица application_headers (headers). Не дублируйте одно значение в properties и headers без необходимости.

3.3.3. Тело сообщения (Value)

Схема (JSON Schema / Avro / Protobuf): храните в Git или Schema Registry; в спеке — $id, версия, ссылка на AsyncAPI.

Описание полей (согласовать с 10.01.14 Шаблон глоссария и словаря данных):

ПутьТипОбяз.ОписаниеПример
eventIdUUIDдаУникальный ID события; генерирует outbox writer при создании outbox-записи (UUID v4/v7); не переиспользовать при retry"f47ac10b-58cc-4372-a567-0e02b2c3d479"
eventTypeStringдаЛитерал типа события"OrderCreated"
occurredAtISO 8601даВремя возникновения на стороне producer"2025-05-27T10:15:30Z"
order.orderIdUUIDдаID заказа
order.userIdUUIDдаID покупателя"b2c3d4e5-f6a7-8901-bcde-f12345678901"
order.totalAmountDecimalдаСумма заказа2590.00
order.currencyString(3)даISO 4217"RUB"
order.items[]ArrayдаПозиции заказа: sku, quantity, priceсм. пример §4
PII / чувствительные поля—да**При наличии PII — перечень полей (userId, суммы), запрет в логах/metrics, маскирование в trace; retention топика/exchange — 08.07 Основы кибербезопасности для системного аналитика, 08.06 Юридические и регуляторные требования РФ (PCI-DSS)userId — не логировать; retention 7 д

3.4. Условия отправки (триггер)

АспектЧто писать
Бизнес-условиеКраткая отсылка к §3.1; не копировать абзац
Техническое условиеПосле успешного COMMIT в БД; при rollback — сообщение не создаётся
Transactional OutboxЗапись в outbox в той же транзакции; схема таблицы — 10.01.03 Шаблон спецификации БД сервиса; dispatcher читает и вызывает Producer — см. §3.6
Интервал polling dispatcherда* — *При outbox: напр. 100 ms; база для расчёта SLA lag §3.10
SLA lag outbox → brokerда* — *При outbox: целевая задержка от COMMIT до ack брокера, напр. ≤ 500 ms при polling 100 ms
Режим отправкиЗафиксировать один из вариантов (см. таблицу ниже)
РежимОпределениеКогда выбирать
Sync (blocking ack)Бизнес-поток или dispatcher ждёт подтверждения брокера до продолженияКритичные события, outbox помечается SENT только после ack
Async (non-blocking client)Вызов producer API не блокирует поток; ack обрабатывается callbackВысокая пропускная способность; статус outbox — по callback
Fire-and-forgetНе ждём ack (не рекомендуется для доменных событий)Только telemetry; явно обосновать

Распределение статусов outbox (зафиксируйте акторов — иначе QA и разработка расходятся):

СтатусКто выставляетКогда
PENDINGOutbox WriterУспешный INSERT outbox в транзакции с бизнес-данными
SENTDispatcherAck/confirm от брокера получен
POISONOutbox WriterВалидация payload до INSERT outbox не прошла (невалидные данные, несовместимая схема на стороне сервиса)
POISONDispatcherОшибка сериализации при чтении outbox или перед вызовом producer API (SerializationException)
FAILEDDispatcherИсчерпаны retry по §3.6

Паттерн outbox и согласованность — 03.05.06 Event-Driven Architecture, 03.06.01 Архитектурные паттерны распределённых систем.

3.5. Обработка ошибок отправки

СитуацияКод / причинаДействиеАктор / статус outbox
Таймаут брокераTimeoutExceptionRetry по §3.6Dispatcher; остаётся PENDING до SENT или FAILED
Недостаточно реплик (Kafka)NotEnoughReplicasExceptionRetry по §3.6Dispatcher
Ошибка сериализацииSerializationExceptionБез retry; алертDispatcher → POISON (см. §3.4)
Невалидный payload до outboxВалидация доменаБез outbox; алертOutbox Writer → POISON или отказ транзакции
Слишком большое сообщениеRecordTooLargeExceptionБез retry; алерт; redesign payloadDispatcher → POISON или runbook
RabbitMQ nack / returnbasic.returnRetry по §3.6; проверить routing key и ACLDispatcher

После исчерпания retry (outbox):

Статус outboxДействиеRunbook
FAILED (retry_count ≥ max)Алерт on-call; запись в quarantine/DLQ таблицуРучная переотправка или компенсация бизнес-процесса
POISONНе ретраить автоматическиИсправление данных/схемы, затем reprocess

Распределённая компенсация (Saga) — отдельный уровень; не смешивать с локальным outbox без ADR — 03.05.06 Event-Driven Architecture.

3.6. Политика повторов (единственный нормативный блок)

ПараметрЗначение
Max attempts3
BackoffЭкспоненциальный: 100 мс, 200 мс, 400 мс
Идемпотентность retry (логическая)Повтор отправляет тот же eventId и payload; consumer дедуплицирует — 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ)
Ответственность producerГарантировать уникальный eventId на бизнес-событие; не генерировать новый ID при retry
Kafka: enable.idempotenceда* — *Kafka: true на клиенте producer; снижает дубликаты записей на брокере при retry at-least-once. Дополняет eventId, не заменяет дедуп consumer. Transactional producer — отдельное ADR, если нужен exactly-once end-to-end

3.7. Мониторинг и метрики

Имена и пороги согласуйте с NFR (10.01.12 Шаблон нефункциональных требований (NFR)) и стандартом observability-команды (префиксы, labels, SLO).

МетрикаТипНазначение
producer_sent_totalcounterУспешные отправки
producer_error_totalcounterОшибки отправки
producer_latency_secondshistogramВремя до ack (включая retry)
producer_outbox_pendinggaugeЗаписи outbox в статусе PENDING
producer_outbox_failedgaugeЗаписи FAILED / quarantine
producer_outbox_lag_secondsgauge*При outbox — задержка PENDING → SENT; контроль SLA §3.4 / §3.10

Алерт: producer_error_total > порога за 5 мин или producer_outbox_failed > 0 на prod.

3.8. Версионирование и обратная совместимость

ПолеЧто писать
Версия схемы1.0.0
Subject (Schema Registry)Имя subject и compatibility level — см. §3.2
Стратегия эволюцииТолько backward-compatible изменения; breaking — новый eventType или routing key
РеестрConfluent Schema Registry / Git + CI
Процесс согласованияУведомление consumer, CCB при breaking — 03.05.07 Управление версионированием API и жизненным циклом, 02.05.02 Управление изменениями (Change Control Board)

3.9. Отладка (локально / staging)

Команды ниже — только локально или на staging; без prod credentials и без записи PII в shell history. Политика доступа — 03.05.05 Безопасность API, 08.07 Основы кибербезопасности для системного аналитика. Проверка доставки и потребления — 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ).

Kafka:

kafka-console-producer --broker-list localhost:9092 --topic orders.events \
  --property "parse.key=true" --property "key.separator=:"
> a1b2c3d4-e5f6-7890-abcd-ef1234567890:{"eventId":"...", "eventType":"OrderCreated", ...}

RabbitMQ (management plugin / rabbitmqadmin; key-less publish, routing key из §3.2):

rabbitmqadmin publish exchange=orders.topic routing_key=order.created.v1 \
  payload='{"eventId":"...", "eventType":"OrderCreated", ...}' \
  properties='{"delivery_mode":2, "content_type":"application/json"}'

3.10. Примечания и ограничения (NFR)

ТребованиеЗначение
Гарантия доставкиat-least-once; consumer идемпотентен — 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ)
Порядок (Kafka)Только в пределах одного message key (одна партиция)
Порядок (RabbitMQ)Producer не гарантирует FIFO; при необходимости упорядочивания — зафиксировать exchange/очередь и модель consumer в 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ)
Интервал polling dispatcher100 ms (§3.4)
Max lag outbox → broker≤ 500 ms при polling 100 ms — SLA = интервал polling + время ack + буфер; оба значения должны совпадать в §3.4 и здесь
Throughputдо 500 msg/s на prod
Max размер сообщениясм. §3.2
Критерии приёмки QAGiven оплата прошла → within 500 ms в топике сообщение с eventType=OrderCreated, валидным eventId, key=orderId — 02.04.02 Критерии приёмки (Acceptance Criteria)

4. Пример заполнения

Scope: одна спека на Order Service — outbox writer + OutboxDispatcher + OrderEventsProducer.

Модель eventType: один топик orders.events, envelope с полем eventType; routing key не используется (Kafka).

Kafka: топик orders.events, key = orderId, acks=all, enable.idempotence=true.

Триггер: после COMMIT заказа и оплаты — запись в outbox; dispatcher polling 100 ms; публикует в sync-режиме (ждёт ack). SLA lag outbox → broker ≤ 500 ms.

RabbitMQ (тот же контракт payload, другой брокер): exchange orders.topic (type topic), routing key order.created.v1, delivery_mode=2, publisher confirms включены, ожидание confirm — да, таймаут 2000 ms. Exchange объявляет платформа (IaC). Key-less publish; упорядочивание — в спеке consumer.

Фрагмент JSON Schema (полная схема — в репозитории контрактов):

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "OrderCreatedEvent",
  "type": "object",
  "properties": {
    "eventId": { "type": "string", "format": "uuid" },
    "eventType": { "const": "OrderCreated" },
    "occurredAt": { "type": "string", "format": "date-time" },
    "order": {
      "type": "object",
      "properties": {
        "orderId": { "type": "string", "format": "uuid" },
        "userId": { "type": "string", "format": "uuid" },
        "totalAmount": { "type": "number" },
        "currency": { "type": "string", "minLength": 3, "maxLength": 3 },
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "sku": { "type": "string" },
              "quantity": { "type": "integer", "minimum": 1 },
              "price": { "type": "number" }
            },
            "required": ["sku", "quantity", "price"]
          },
          "minItems": 1
        }
      },
      "required": ["orderId", "userId", "totalAmount", "currency", "items"]
    }
  },
  "required": ["eventId", "eventType", "occurredAt", "order"]
}

Пример сообщения (согласован со схемой и таблицей §3.3.3):

{
  "eventId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "eventType": "OrderCreated",
  "occurredAt": "2025-05-27T10:15:30Z",
  "order": {
    "orderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "userId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "totalAmount": 2590.00,
    "currency": "RUB",
    "items": [
      { "sku": "WIDGET-01", "quantity": 2, "price": 1000.00 },
      { "sku": "WIDGET-02", "quantity": 1, "price": 590.00 }
    ]
  }
}

Диаграмма последовательности (иллюстрация; нотация — 03.02.03 Диаграмма последовательности; сквозной BPMN — 10.01.08 Шаблон сквозной фичи (BPMN-процесс)):

sequenceDiagram
    participant Service as Order Service
    participant DB as База данных
    participant Outbox as Таблица outbox
    participant Dispatcher as Outbox Dispatcher
    participant Kafka as Apache Kafka

    Service->>DB: BEGIN; INSERT заказа; INSERT outbox
    DB-->>Service: COMMIT
    Note over Dispatcher: polling interval 100 ms
    Dispatcher->>Outbox: SELECT PENDING
    loop каждое событие
        Dispatcher->>Kafka: publish (key=orderId)
        alt ack
            Kafka-->>Dispatcher: ok
            Dispatcher->>Outbox: status=SENT
        else ошибка сериализации
            Dispatcher->>Outbox: status=POISON
            Dispatcher->>Dispatcher: алерт, без retry
        else transient error
            Dispatcher->>Dispatcher: retry §3.6
            alt retry исчерпан
                Dispatcher->>Outbox: status=FAILED
                Dispatcher->>Dispatcher: алерт + quarantine runbook
            end
        end
    end

5. Связанные артефакты

НаправлениеАртефакт
ВходVision/BRD/SRS (04.01 Форматы документов (Vision&Scope, BRD, SRS, FRD)), user story (10.01.01 Шаблон User Story), глоссарий (10.01.14 Шаблон глоссария и словаря данных)
ПараллельноСхема outbox (10.01.03 Шаблон спецификации БД сервиса), AsyncAPI/JSON Schema (03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf)), NFR (10.01.12 Шаблон нефункциональных требований (NFR)), ADR при outbox (10.01.13 Шаблон архитектурного решения (ADR))
ВыходСпеки consumer (10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ)), матрица трассировки (10.01.15 Шаблон матрицы трассировки), тест-кейсы (02.04.02 Критерии приёмки (Acceptance Criteria))
Соседний контрактREST API того же сервиса (10.01.04 Шаблон спецификации REST API)

6. Связи с другими разделами базы

Теория и контекст — без повторения артефактов из §5:

7. Источники и дополнительные материалы

Книги и стандарты

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