Files
dc-observatio/docs/standards/PLATFORM_STANDARD.md
Дмитрий Соловьев d48ddd2657 Init
2026-05-25 14:23:52 +03:00

17 KiB
Raw Permalink Blame History

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.
  • Для нашей платформы безопасным внутренним ориентиром считается бюджет 50100 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.