304 lines
17 KiB
Markdown
304 lines
17 KiB
Markdown
# 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 обязан принимать / обрабатывать:
|
||
- `businessKey`
|
||
- `operationId`
|
||
- `correlationId`
|
||
- `processInstanceKey`
|
||
- `traceId` или `traceparent`
|
||
|
||
### Контракт воркера
|
||
|
||
Для каждого Zeebe worker:
|
||
- читать входные данные только из process / job variables;
|
||
- валидировать обязательные поля корреляции до начала обработки;
|
||
- проверять dedup store по `operationId` до выполнения side effects;
|
||
- возвращать семантические результаты:
|
||
- `SUCCESS` -> `CompleteJob`
|
||
- `RETRYABLE_ERROR` -> `FailJob` с retry / backoff
|
||
- `BUSINESS_ERROR` -> `ThrowError`
|
||
- `FATAL_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)`.
|
||
|
||
---
|
||
|
||
## Порядок транзакции и публикации событий
|
||
|
||
Когда операция изменяет доменное состояние и публикует события:
|
||
|
||
1. Выполнить доменную операцию.
|
||
2. Записать событие(я) в outbox в рамках той же транзакции.
|
||
3. Сделать commit транзакции.
|
||
4. Только после этого подтверждать 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.
|
||
|
||
Допустимые классы результатов:
|
||
- `SUCCESS`
|
||
- `RETRYABLE_ERROR`
|
||
- `BUSINESS_ERROR`
|
||
- `FATAL_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_id`
|
||
- `event_type`
|
||
- `event_version`
|
||
- `occurred_at`
|
||
- `producer`
|
||
- `business_key`
|
||
- `correlation_id`
|
||
- `trace_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 и jitter
|
||
- `BUSINESS_ERROR`: никаких слепых retries; отправка в operator handling / parking lot / DLQ
|
||
- `FATAL_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_id`
|
||
- `trace_id`
|
||
- `process_instance_id`
|
||
- `business_key`
|
||
- `operation_id`
|
||
- `service_name`
|
||
- `env`
|
||
|
||
Обязательная телеметрия:
|
||
- 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.
|