Перейти к основному содержимому

Playbook Декомпозиции Сервисов

Назначение

Документ помогает решить, где должна жить новая функциональность: в отдельном сервисе, feature group, shared module/package или projection. Главный критерий - владение инвариантом и source of truth, а не удобство UI или наличие похожего endpoint.

Решение В Одну Строку

Функциональность помещается туда, где принимается необратимое бизнес-решение и хранится authoritative state. Если state производный, нужен projection. Если логика не владеет state, нужен module/package или policy внутри владельца.

Матрица Размещения

ПризнакНовый сервисFeature внутри сервисаShared module/packageProjection
Собственный source of truthДаНет, использует owner serviceНетНет
Собственные инвариантыДаВ рамках existing aggregate boundaryНетНет
Независимый lifecycleДаНетВерсионируемый library lifecycleRebuildable
Отдельный 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 screenUI shape не равен bounded contextFeature/projection для экрана
Cross-service foreign keyЛомает автономность и миграцииForeignId, snapshot или replica
Direct table read другого сервисаОбходит security/audit/invariantsOwner API, event replica или projection
Balance update в нескольких местахНарушает double-entryТолько Accounting command
Provider model in domainПротаскивает внешний контракт в coreAnti-corruption adapter
Projection as command sourceStale 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.

Обратные ссылки

Автоссылки: где используется этот документ.