17 KiB
PLATFORM_STANDARD.md
Назначение
Этот документ задаёт платформенные требования для сервисов, работающих в BPM / Kafka / Zeebe-среде.
Документ определяет:
- архитектурные инварианты;
- требования к идемпотентности и надёжности;
- правила BPM / Kafka интеграции;
- требования к observability, compatibility, retry / DLQ, compensation и rollout safety.
Это внутренний платформенный стандарт. Некоторые правила ниже являются осознанной инженерной политикой команды и не должны трактоваться как универсальная истина для всей отрасли.
Архитектурные инварианты
- BPM — только оркестратор процесса.
- Доменное состояние и бизнес-правила принадлежат сервису, а не BPM.
- BPM не должен использоваться как master data store для доменных данных.
- BPM может хранить только идентификаторы, ссылки и технические метаданные, необходимые для оркестрации.
- Везде предполагается at-least-once доставка. Повторная доставка — норма, а не исключение.
- Exactly-once не предполагается; корректность достигается через идемпотентность, dedup, Outbox и управляемые retries.
- Каждый обработчик обязан быть идемпотентным.
Правила использования BPM
- BPM использовать только для операторских / регламентных процессов и long-running orchestration.
- Не использовать BPM для высокочастотных near-real-time streaming-потоков.
- Каждый BPM service task должен представлять ровно одну атомарную доменную операцию.
- Логика ожидания, таймаутов, ретраев, эскалаций и компенсаций должна моделироваться явно в BPM, а не быть скрыта внутри сервиса.
- BPMN flow не должен становиться местом владения бизнес-логикой.
Camunda 8 / Zeebe
Граница интеграции
- BPM-to-service-task интеграция должна идти через Zeebe gRPC Job Worker.
- Для нашей платформы использование REST connector для service task считается не рекомендуемым и по умолчанию запрещённым, если нет отдельного архитектурного согласования.
- Нельзя изменять
jobType, пока существуют активные инстансы процесса.
Обязательные поля корреляции
Каждый service task обязан принимать / обрабатывать:
businessKeyoperationIdcorrelationIdprocessInstanceKeytraceIdилиtraceparent
Контракт воркера
Для каждого Zeebe worker:
- читать входные данные только из process / job variables;
- валидировать обязательные поля корреляции до начала обработки;
- проверять dedup store по
operationIdдо выполнения side effects; - возвращать семантические результаты:
SUCCESS->CompleteJobRETRYABLE_ERROR->FailJobс retry / backoffBUSINESS_ERROR->ThrowErrorFATAL_ERROR-> ветка инцидента или обработка через исчерпание retries
- никогда не генерировать
operationIdвнутри сервиса для входящих оркестрируемых вызовов; - вызывать
CompleteJobтолько после commit доменной транзакции.
Идемпотентность
Все операции, вызываемые из BPM, и все Kafka consumers обязаны быть идемпотентными.
Обязательные правила:
operationIdдолжен приходить от вызывающей стороны / оркестратора;- dedup storage должен обеспечивать atomic check-and-write semantics;
- dedup storage должен защищать от race conditions;
- повторный вызов с тем же
operationIdдолжен возвращать тот же семантический результат и не должен повторять side effects; - для записей dedup должен быть документирован TTL в соответствии с бизнес-окном возможного повтора;
- non-atomic реализация идемпотентности запрещена.
Дополнительное обязательное правило:
- повторный вызов с тем же
operationId, но с другимbusinessKeyили семантически иным payload / параметрами, считается конфликтом контракта, а не валидным retry; такой случай должен приводить к явной ошибке / инциденту и быть достаточно залогирован для расследования.
Рекомендуемая реализация:
- таблица в доменной БД, например
idempotency_keys(operation_id, business_key, status, result_hash, created_at).
Порядок транзакции и публикации событий
Когда операция изменяет доменное состояние и публикует события:
- Выполнить доменную операцию.
- Записать событие(я) в outbox в рамках той же транзакции.
- Сделать commit транзакции.
- Только после этого подтверждать BPM (
CompleteJob) или внешний success.
Публикация событий вне Outbox запрещена.
Это правило одинаково применяется к:
- синхронным сервисным операциям;
- Zeebe workers;
- Kafka consumers, которые меняют состояние и публикуют новые события.
Process variables
- Process variables должны быть минимальными.
- Хранить только ID, ссылки, routing data и небольшие технические метаданные.
- Не хранить файлы, большие JSON payload, большие массивы в process variables.
- Большие payload должны храниться в доменной БД или object storage; в process variables должны храниться только ссылки.
- Process variables должны быть версионированы и документированы.
- Для long-running процессов изменения набора или формы variables должны оставаться совместимыми с уже запущенными инстансами, либо сопровождаться явной migration strategy.
- Для нашей платформы безопасным внутренним ориентиром считается бюджет 50–100 KB на один process instance. Это не технический максимум платформы, а engineering guardrail для управляемости и надёжности процессов.
Семантика результатов
Каждая операция должна возвращать семантическую классификацию результата, а не только transport status codes.
Допустимые классы результатов:
SUCCESSRETRYABLE_ERRORBUSINESS_ERRORFATAL_ERROR
Каждая long-running операция должна иметь:
- явно заданный SLA / timeout;
- async processing model, где это нужно;
- status lookup по
operationId; - определённое поведение cancel, если отмена поддерживается.
Kafka / event-driven
Базовые правила
- Базовое предположение по доставке — at-least-once.
- Consumers должны быть идемпотентными по
event_idи / илиoperation_id. - Нельзя размещать бизнес-логику напрямую в consumer / listener классах.
- Consumers / listeners должны делегировать в application / domain services.
- Сохранять текущую retry / DLQ-стратегию, если явно не запрошено её изменение.
Семантика event_id и operation_id
event_idиспользуется для dedup повторной доставки одного и того же опубликованного события.operation_idиспользуется для dedup повторного выполнения одной и той же бизнес-операции, если этот идентификатор протягивается через события и обработчики.- Если в сообщении присутствуют оба идентификатора, обработчик обязан явно понимать и документировать, какой из них используется для какой семантики dedup.
Контракты событий
Требования ниже относятся прежде всего к domain events. Для технических, retry, DLQ, compacted state и других служебных топиков может использоваться отдельная документированная конвенция.
Для domain events:
- именование топиков должно следовать шаблону
<domain>.<entity>.<event>.v<major>; - partition key должен сохранять порядок событий по одной сущности через
business_key; - каждое событие должно содержать как минимум:
event_idevent_typeevent_versionoccurred_atproducerbusiness_keycorrelation_idtrace_id, если доступенprocess_instance_id, если применимо
- предпочтительны Avro / Protobuf со Schema Registry;
- строго валидируемая JSON Schema допустима только если это согласовано платформой.
Что нельзя менять без явного запроса
- topic names
- event schema
- key semantics
- partition assumptions
- serialization format
- retry topology
- DLQ routing rules
Schema evolution и совместимость
- Breaking changes требуют новой major-версии.
- Добавление optional fields с default значениями допустимо.
- Удаление или переименование полей требует новой major-версии.
- По умолчанию consumers должны оставаться совместимыми как минимум с текущей схемой и согласованной политикой совместимости платформы. Если для конкретного домена зафиксировано правило поддержки текущей и предыдущей minor-версии, оно должно быть явно указано в контракте этого домена.
Для BPM, messaging и public APIs:
- сохранять backward compatibility, если явно не запрошен breaking change;
- учитывать rolling deployment compatibility;
- учитывать coexistence старых и новых consumers;
- избегать изменений, требующих lockstep deployment, если это явно не запрошено.
Retry / DLQ
Retries должны быть управляемыми и конечными.
Рекомендуемая политика:
RETRYABLE_ERROR: ограниченные retries с exponential backoff и jitterBUSINESS_ERROR: никаких слепых retries; отправка в operator handling / parking lot / DLQFATAL_ERROR: никаких слепых retries; создание инцидента и маршрутизация в DLQ, где применимо
Обязательные правила:
- бесконечные retries запрещены;
- отключение DLQ для критичных consumer flows запрещено;
- replay из DLQ без установленной причины запрещён;
- Zeebe retries и Kafka retries должны рассматриваться как два независимых контура управления.
Compensation
Если операция имеет side effects, должно быть явно определено одно из следующего:
- compensating operation;
- явное указание, что компенсация невозможна;
- ветка operator / manual remediation.
Компенсация должна быть:
- идемпотентной;
- детерминированной;
- семантически классифицированной;
- залогированной с
businessKey,operationId, причиной и результатом.
Observability
Структурированные JSON-логи обязательны.
Каждый log / event / span должен содержать, где применимо:
correlation_idtrace_idprocess_instance_idbusiness_keyoperation_idservice_nameenv
Обязательная телеметрия:
- latency (
p50/p95/p99) - error rate по семантическим классам
- retry rate / attempts histogram
- active process instances / stuck instances
- consumer lag / DLQ rate
- compensation rate
Трассировка:
- OpenTelemetry обязателен
- каждый шаг процесса должен создавать span
- span attributes должны содержать
process_instance_id,business_key,operation_id,step_name
Если меняется Kafka / BPM / external integration logic:
- сохранить или добавить correlation identifiers;
- сохранить или добавить
operation_id, где это релевантно; - логировать достаточно для дебага retries, duplicates и compensations;
- никогда не логировать секреты или sensitive payloads.
Backpressure и runtime load
- Воркеры должны явно ограничивать
maxJobsActiveи concurrency. - Нельзя использовать рост timeout или retry как механизм масштабирования.
- Backpressure должен управляться через масштабирование воркеров и управление пропускной способностью.
- Предположения о throughput воркеров должны быть документированы.
- CPU / RAM saturation должна наблюдаться и иметь алерты.
Безопасность и аудит
- Сервисы должны поддерживать JWT / OAuth2 там, где это применимо.
- Административные операции должны быть защищены role-based или attribute-based access control.
- Секреты нельзя хранить в репозитории.
- Административные действия, такие как retry / cancel / compensate / migrate, должны быть аудитируемыми.
CI / CD и production safety
Минимально обязательные гейты для изменений, где это релевантно:
- unit tests
- coverage threshold
- static analysis
- API compatibility checks
- schema compatibility checks
- BPM + worker integration tests
- resilience tests для поведения timeout / retry / DLQ
Правила деплоя:
- деплой BPMN только через pipeline;
- деплой воркеров только через pipeline;
- ручной деплой BPMN в production запрещён;
- критичные изменения retry / timeout / jobType не должны вноситься только через UI без трассируемости в VCS.