Playbook Декомпозиции Сервисов
Назначение
Документ помогает решить, где должна жить новая функциональность: в отдельном сервисе, feature group, shared module/package или projection. Главный критерий - владение инвариантом и source of truth, а не удобство UI или наличие похожего endpoint.
Решение В Одну Строку
Функциональность помещается туда, где принимается необратимое бизнес-решение и хранится authoritative state. Если state производный, нужен projection. Если логика не владеет state, нужен module/package или policy внутри владельца.
Матрица Размещения
| Признак | Новый сервис | Feature внутри сервиса | Shared module/package | Projection |
|---|---|---|---|---|
| Собственный source of truth | Да | Нет, использует owner service | Нет | Нет |
| Собственные инварианты | Да | В рамках existing aggregate boundary | Нет | Нет |
| Независимый lifecycle | Да | Нет | Версионируемый library lifecycle | Rebuildable |
| Отдельный scaling profile | Сильный аргумент | Обычно нет | Нет | Часто да для read load |
| Отдельная security/compliance boundary | Сильный аргумент | Только если owner совпадает | Нет | Только masking/tenant trimming |
| Cross-service events/API | Да | Может публиковать owner events | Нет runtime ownership | Потребляет events |
| Нужна transaction boundary | Локальная в новом сервисе | Локальная в existing service | Нет | Нет mutation |
| Eventual consistency допустима | Для связей с другими | Внутри сервиса нет | Не применимо | Да |
Когда Нужен Отдельный Сервис
Создавай отдельный сервис, если одновременно есть несколько условий:
- отдельный source of truth и owned aggregates;
- бизнес-инварианты не принадлежат существующему сервису;
- другие сервисы зависят от его decisions/events;
- нужны отдельные permissions, audit, retention или compliance controls;
- нагрузка, доступность или релизный цикл заметно отличаются;
- команда может владеть операционной ответственностью сервиса.
Примеры: Accounting для ledger, User Safety для self-exclusion, Compliance/AML для AML cases, Execution Gateway для provider rounds.
Когда Достаточно Feature Group
Feature group подходит, если новая возможность меняет state уже существующего aggregate owner. Например, withdrawal approvals остаются в Accounting, потому что hold/withdrawal/ledger являются его инвариантами. Новый folder/API внутри сервиса лучше нового сервиса, если split создает distributed transaction там, где нужен один локальный commit.
Когда Нужен Module Или Package
Shared module допустим только для технических building blocks без бизнес-владения: correlation, paging, idempotency primitives, authorization abstractions, typed result, common validation helpers. Domain policy с money/risk/KYC decision не выносится в shared library, если у нее есть owner service.
Когда Нужна Projection
Projection нужна для read-heavy UI, dashboards, search, admin queues, exports и cross-service summaries. Projection:
- rebuildable из events/source queries;
- имеет freshness SLA и watermark;
- не является source of truth;
- fail closed для financial/security-critical commands;
- содержит только разрешенные поля и masking.
ADR Triggers
ADR обязателен, если:
- предлагается новый сервис или merge/split существующего сервиса;
- появляется новый source of truth или смена owner;
- вводится synchronous dependency между сервисами для command decision;
- financial/security/compliance flow начинает зависеть от stale projection;
- требуется distributed transaction или отказ от outbox/inbox;
- provider-specific модель предлагается вне adapter boundary;
- PII, payment data или support evidence меняют storage/retention boundary.
Anti-Patterns
| Anti-pattern | Почему опасно | Правильный ход |
|---|---|---|
shared-domain с бизнес-правилами | Размывает ownership и релизные границы | Owner service + events/API |
| Service per screen | UI shape не равен bounded context | Feature/projection для экрана |
| Cross-service foreign key | Ломает автономность и миграции | ForeignId, snapshot или replica |
| Direct table read другого сервиса | Обходит security/audit/invariants | Owner API, event replica или projection |
| Balance update в нескольких местах | Нарушает double-entry | Только Accounting command |
| Provider model in domain | Протаскивает внешний контракт в core | Anti-corruption adapter |
| Projection as command source | Stale decision может менять деньги/доступ | Fresh owner lookup или fail closed |
Generic payload event | Невозможно проверить совместимость | Named typed event model |
DoR Для Декомпозиции
Перед implementation должны быть зафиксированы:
- owner service или причина нового сервиса;
- authoritative entities и forbidden state;
- commands/queries/events и CQRS mapping;
- transaction boundary, idempotency и audit;
- consumers, replicas/projections и freshness SLA;
- security permissions, tenant boundary и masking;
- reconciliation/compensation/runbook для failure cases;
- ADR, если сработал trigger.
Обратные ссылки
Автоссылки: где используется этот документ.
- adr (1): ADR-0037: Service Landscape И Business Capabilities
- architecture-overview.md (1): Архитектура Pin
- backend-shared-standards-index.md (1): Backend Shared Standards Index