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 Dispatcher | Polling 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.
Не подходит или требует дополнения, если:
- нужен обзор Kafka, RabbitMQ, acks, ISR — 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events);
- описываете потребителя (offset, DLQ, идемпотентность обработки) — 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ);
- синхронный REST/gRPC — 10.01.04 Шаблон спецификации REST API;
- платформенные NFR (RPS, retention, audit) — 10.01.12 Шаблон нефункциональных требований (NFR), 08.02 Нефункциональные требования (НФТ) — классификация и стандарты;
- политика эволюции контрактов и согласование с consumer — 03.05.07 Управление версионированием API и жизненным циклом, 02.05.02 Управление изменениями (Change Control Board).
Входные условия: границы сервиса (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
| Поле ключа | Тип | Описание | Пример |
|---|---|---|---|
orderId | UUID | Идентификатор агрегата; все события по заказу — один ключ | "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 Шаблон глоссария и словаря данных):
| Путь | Тип | Обяз. | Описание | Пример |
|---|---|---|---|---|
eventId | UUID | да | Уникальный ID события; генерирует outbox writer при создании outbox-записи (UUID v4/v7); не переиспользовать при retry | "f47ac10b-58cc-4372-a567-0e02b2c3d479" |
eventType | String | да | Литерал типа события | "OrderCreated" |
occurredAt | ISO 8601 | да | Время возникновения на стороне producer | "2025-05-27T10:15:30Z" |
order.orderId | UUID | да | ID заказа | |
order.userId | UUID | да | ID покупателя | "b2c3d4e5-f6a7-8901-bcde-f12345678901" |
order.totalAmount | Decimal | да | Сумма заказа | 2590.00 |
order.currency | String(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 и разработка расходятся):
| Статус | Кто выставляет | Когда |
|---|---|---|
PENDING | Outbox Writer | Успешный INSERT outbox в транзакции с бизнес-данными |
SENT | Dispatcher | Ack/confirm от брокера получен |
POISON | Outbox Writer | Валидация payload до INSERT outbox не прошла (невалидные данные, несовместимая схема на стороне сервиса) |
POISON | Dispatcher | Ошибка сериализации при чтении outbox или перед вызовом producer API (SerializationException) |
FAILED | Dispatcher | Исчерпаны retry по §3.6 |
Паттерн outbox и согласованность — 03.05.06 Event-Driven Architecture, 03.06.01 Архитектурные паттерны распределённых систем.
3.5. Обработка ошибок отправки
| Ситуация | Код / причина | Действие | Актор / статус outbox |
|---|---|---|---|
| Таймаут брокера | TimeoutException | Retry по §3.6 | Dispatcher; остаётся PENDING до SENT или FAILED |
| Недостаточно реплик (Kafka) | NotEnoughReplicasException | Retry по §3.6 | Dispatcher |
| Ошибка сериализации | SerializationException | Без retry; алерт | Dispatcher → POISON (см. §3.4) |
| Невалидный payload до outbox | Валидация домена | Без outbox; алерт | Outbox Writer → POISON или отказ транзакции |
| Слишком большое сообщение | RecordTooLargeException | Без retry; алерт; redesign payload | Dispatcher → POISON или runbook |
| RabbitMQ nack / return | basic.return | Retry по §3.6; проверить routing key и ACL | Dispatcher |
После исчерпания 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 attempts | 3 |
| 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_total | counter | Успешные отправки |
producer_error_total | counter | Ошибки отправки |
producer_latency_seconds | histogram | Время до ack (включая retry) |
producer_outbox_pending | gauge | Записи outbox в статусе PENDING |
producer_outbox_failed | gauge | Записи FAILED / quarantine |
producer_outbox_lag_seconds | gauge | *При 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 dispatcher | 100 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 |
| Критерии приёмки QA | Given оплата прошла → 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:
- Асинхронные брокеры и гарантии доставки — 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events)
- Event-Driven Architecture и transactional outbox — 03.05.06 Event-Driven Architecture
- Безопасность канала (TLS, SASL, ACL) — 03.05.05 Безопасность API
- Версионирование контрактов и breaking changes — 03.05.07 Управление версионированием API и жизненным циклом
- Декомпозиция producer / dispatcher / outbox — 03.01.03 Декомпозиция системы на подсистемы и модули
7. Источники и дополнительные материалы
Книги и стандарты
- JSON Schema Draft 07 — https://json-schema.org/draft-07/json-schema-release-notes.html
- AsyncAPI Specification — https://www.asyncapi.com/docs/reference/specification/latest
Документация и практики
- Apache Kafka Documentation — Producer API — https://kafka.apache.org/documentation/#producerapi
- Confluent Schema Registry — https://docs.confluent.io/platform/current/schema-registry/index.html
- RabbitMQ Documentation: Publisher Confirms — https://www.rabbitmq.com/docs/confirms
- Designing Event-Driven Systems (Ben Stopford) — https://www.confluent.io/designing-event-driven-systems/
- Microservices Patterns: Transactional Outbox (Chris Richardson) — https://microservices.io/patterns/data/transactional-outbox.html