Архитектурные паттерны помогают системному аналитику говорить с архитектором на одном языке: понимать trade-offs, формулировать НФТ и фиксировать решения в ADR. Статья закрывает вопрос «какой паттерн выбрать под симптом» и «что именно описать в требованиях», а не даёт инструкцию по настройке Envoy или Debezium.
1. Суть темы
Архитектурный паттерн — повторяемое решение типовой проблемы распределённой системы: транзакции через границы сервисов, отказоустойчивость, публикация событий, миграция legacy. Паттерн задаёт границы ответственности сервисов и ожидаемое поведение при сбоях — то, что аналитик переводит в сценарии, НФТ и критерии приёмки.
Не всё, что обсуждают рядом с паттернами, является паттерном в том же смысле:
| Тип | Примеры | Где фиксировать в KB |
|---|---|---|
| Архитектурные паттерны | Saga, Outbox, API Gateway, Strangler Fig | Эта статья |
| Модели согласованности | Eventual consistency, read-your-writes | 08.02 Нефункциональные требования (НФТ) — классификация и стандарты — как измеримые НФТ |
| Свойства операций и API | Идемпотентность, retry, timeout | 03.05.02 Синхронные протоколы (REST, gRPC, GraphQL), 03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf) |
Предпосылка большинства паттернов — принцип database per service: у каждого сервиса своя БД, общая схема — антипаттерн. Без этого границы Saga, Outbox и Event Sourcing не определены. Декомпозицию и границы контекстов см. 03.01.03 Декомпозиция системы на подсистемы и модули и 03.06.02 Основы Domain-Driven Design.
Граница с соседними статьями: здесь — какой паттерн и что зафиксировать в ADR/NFR; стиль интеграции через события, топология потоков и контракты брокера — в 03.05.06 Event-Driven Architecture.
2. Ключевые понятия
Saga
Управление распределённой бизнес-транзакцией через цепочку локальных транзакций. При сбое — компенсирующие операции, а не распределённый 2PC.
| Подход | Суть | Когда уместен |
|---|---|---|
| Хореография (Choreography) | Сервисы реагируют на события без центрального координатора | Небольшое число участников (ориентир 2–4), простой линейный поток; сложность растёт с числом типов событий и циклов обратной связи |
| Оркестрация (Orchestration) | Центральный оркестратор ведёт состояние и шаги | Много шагов, нужна видимость процесса для поддержки |
Хореография — частный случай event-driven интеграции: кто публикует какое событие и в каком порядке, см. 03.05.06 Event-Driven Architecture; здесь фиксируем шаги, компенсации и инварианты.
Важно про «согласованность»: Saga обеспечивает бизнес-инварианты (заказ не оплачен без резерва, компенсации откатывают шаги). Это не ACID-транзакция между сервисами и не мгновенная согласованность read-моделей. Между шагами и после компенсаций данные eventually consistent — задержка и допустимые расхождения нужно явно описать в НФТ (см. 08.02 Нефункциональные требования (НФТ) — классификация и стандарты).
Что фиксировать в требованиях: перечень шагов и компенсаций; владелец процесса (при оркестрации — модель в BPMN или state machine); correlation id / business transaction id для сквозной трассировки; идемпотентность каждого шага и компенсации; SLA оркестратора (RTO при его падении); сценарии «частично выполнено» для приёмки. Если компенсация не удалась — отдельный сценарий: политика retry с лимитом, эскалация в очередь поддержки, ручной runbook (блокировка счёта, отмена резерва оператором), статус «требует вмешательства» в UI; без этого критерии приёмки и сценарии L2/L3 остаются дырявыми.
Circuit Breaker (автоматический выключатель)
Блокирует вызовы к проблемной зависимости после порога ошибок, чтобы не каскадировать отказ (fail fast → half-open → closed).
Что фиксировать в требованиях: пороги и окно подсчёта; бизнес-семантика fallback — не только технический ответ «503». Пример: при недоступности оплаты — можно ли оформить заказ «оплата позже», нужно ли отменить резерв, как уведомить клиента. Сценарии деградации оформляйте в Use Case и 02.04.02 Критерии приёмки (Acceptance Criteria); сценарии «circuit open / half-open» — в тест-план (08.03 Тестирование требований (статическое тестирование)). Это функциональные правила деградации, не деталь Resilience4j.
API Gateway
Единая внешняя точка входа: маршрутизация, auth, агрегация, rate limiting. BFF (Backend for Frontend) — отдельный шлюз под канал (mobile/web/partner): разные контракты и агрегация, но больше команд и surface area.
Когда нужен (критерии, а не счётчик сервисов):
- несколько внешних клиентов или каналов;
- единый edge security, WAF, rate limiting — 03.06.03 Rate Limiting — управление пропускной способностью;
- агрегация ответов без N+1 запросов с клиента;
- сокрытие внутренней топологии.
Один монолит с REST наружу gateway не требует. Десять внутренних сервисов без внешних клиентов могут обойтись без него, если auth и маршрутизация решены иначе.
Что фиксировать: матрица маршрутов; политики auth — 03.05.05 Безопасность API; один gateway vs BFF по каналам (scope, команды); НФТ по latency на edge.
Service Mesh
Внутренний слой сетевого взаимодействия между сервисами через sidecar-прокси (вспомогательный контейнер рядом с приложением: mTLS, политики, метрики): discovery, retry, timeout, circuit breaker, tracing, балансировка — 03.06.04 Стратегии балансировки нагрузки, 07.08 Контейнеризация и оркестрация (Docker, Kubernetes).
Отличие от Gateway: gateway — клиент → система; mesh — сервис ↔ сервис.
Что фиксировать: какие политики resilience (retry, timeout, DLQ) задаются на mesh, а какие в контракте сервиса — см. 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events) для асинхронных цепочек; требования к distributed tracing и propagation correlation id; какие сквозные функции выносятся на платформу (единые для всех сервисов), а какие остаются в коде.
Event Sourcing
Состояние — проекция из журнала неизменяемых событий (append-only), а не только текущая строка в таблице.
Пример (заказ): события OrderCreated → PaymentReceived → OrderShipped → DeliveryFailed. Текущий статус — редукция по типам событий (state machine / fold), а не «последнее событие вообще» (DeliveryFailed после OrderShipped меняет статус, но не отменяет факт отгрузки).
Что фиксировать: каталог типов событий и версионирование схем — 03.05.07 Управление версионированием API и жизненным циклом; стратегия upcasting; снимки (snapshots) и SLA построения read-модели; аудит и хранение. Тест-кейсы на порядок и комбинации событий — в 08.03 Тестирование требований (статическое тестирование) и 02.04.02 Критерии приёмки (Acceptance Criteria).
CQRS (Command Query Responsibility Segregation)
Раздельные модели записи (commands) и чтения (queries). CQRS не требует Event Sourcing: достаточно read replica или отдельной read-БД с синхронной/асинхронной репликацией, если мотив — разная нагрузка на чтение и запись. Event Sourcing — отдельное решение про аудит, историю и воспроизведение состояния; часто сочетается с CQRS, но это не следствие «по умолчанию».
Что фиксировать: допустимая задержка репликации read-модели (eventual consistency для чтения); какие экраны требуют read-your-writes, какие терпят задержку.
Outbox
Атомарная запись бизнес-данных и записи «исходящего события» в таблицу outbox в одной локальной транзакции; отдельный механизм публикует запись в брокер. Устраняет dual-write. Публикация из outbox — часть event-driven цепочки; топология и контракты событий — 03.05.06 Event-Driven Architecture.
Polling vs CDC (критерии для SRS/ADR, без привязки к конкретному инструменту):
- Polling достаточен, если допустима задержка публикации в секундах–минутах, объём outbox умеренный, команда готова мониторить lag и нагрузку периодических SELECT;
- CDC уместен, если нужна низкая задержка (субсекунды), высокий throughput, критичен контроль нагрузки на БД без частых poll-запросов; эксплуатация сложнее, в ADR фиксируйте SLA задержки публикации и алерты на lag.
Доставка — at-least-once на стороне брокера. На стороне consumer — Inbox (таблица или dedup store уже обработанных event id): перед бизнес-логикой проверка «обрабатывали ли уже»; без Inbox и идемпотентности получите дубли заказов и платежей. Требования к ключам — в контракте consumer, 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ) и 03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf); producer — 10.01.06 Шаблон спецификации Producer (Kafka, RabbitMQ), 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events).
Альтернатива без outbox-таблицы — Polling Publisher (периодический опрос изменений в основной таблице): проще внедрить, но слабее гарантии атомарности и выше задержка; компромисс явно согласуйте с заказчиком.
Что фиксировать: семантика доставки; Inbox/dedup на consumer; идемпотентность обработки; порядок событий (ordering) где критичен; SLA задержки публикации; мониторинг «застрявших» записей в outbox.
Bulkhead (переборка)
Изоляция пулов ресурсов по зависимостям: сбой или перегрузка одной зависимости не исчерпывает все потоки.
Что фиксировать: критичность зависимостей; лимиты и поведение при исчерпании пула (очередь vs отказ); связка с Circuit Breaker там, где нужен fail fast после исчерпания.
Strangler Fig (душитель)
Постепенная замена legacy: Intercept → Replace → Redirect → Retire. Новый функционал — в новых сервисах, трафик переключается по маршрутам.
Что фиксировать: карта маршрутов по релизам; контракты на границе legacy/new при перехвате трафика — 03.01.04 Интерфейсы и интеграционные взаимодействия; критерии parity с монолитом; план отключения legacy; границы bounded context — 10.02 Разбор кейса "Миграция legacy-системы", 03.06.02 Основы Domain-Driven Design.
Согласованность и идемпотентность (сквозные темы, не отдельные «паттерны»)
Eventual consistency — при отсутствии новых записей все реплики сходятся к одному значению; между запись и чтение возможен лаг. Приемлема для каталога, рекомендаций, профиля с некритичной задержкой. Неприемлема без явных правил — для баланса, остатков со строгим учётом, бронирования единственного ресурса. Формулировки измеримых порогов — в 08.02 Нефункциональные требования (НФТ) — классификация и стандарты.
Идемпотентность обязательна там, где возможны retry: шаги Saga, компенсации, consumer Outbox/Inbox, POST платежей и создания заказов. Детали HTTP и ключей — 03.05.02 Синхронные протоколы (REST, gRPC, GraphQL), 07.02 Работа с API (практика).
3. Как применяется в работе аналитика
Точка входа — симптом из инцидента, NFR-воркшопа или архитектурного ревью → таблица выбора ниже → фиксация в 10.01.13 Шаблон архитектурного решения (ADR) с последствиями для НФТ.
Рамка выбора паттерна
| Симптом / риск | Паттерн(ы) | Что описать в SRS / ADR / NFR |
|---|---|---|
| Бизнес-операция затрагивает несколько сервисов, 2PC недоступен | Saga | Шаги, компенсации, failed compensation, correlation id, идемпотентность, eventual consistency между шагами |
| Dual-write: БД + брокер без общей транзакции | Outbox (+ Inbox на consumer) | At-least-once, polling vs CDC, SLA lag публикации, dedup по event id, мониторинг outbox |
| Каскадные тайм-ауты при падении зависимости | Circuit Breaker, Bulkhead | Пороги CB; бизнес-fallback; изоляция пулов |
| Одна зависимость исчерпывает все потоки / thread pool | Bulkhead | Лимиты по зависимости; поведение при saturation; связка с CB |
| Support не может проследить заказ через N сервисов | Distributed tracing, correlation id | Propagation id gateway → saga → логи; требования observability в NFR |
| Разная нагрузка на чтение и запись | CQRS (+ ES при аудите) | SLA lag read-модели; read-your-writes для сценариев; ES — только если нужна история |
| Полный аудит «как пришли к состоянию» | Event Sourcing | Каталог событий, версии, snapshots, upcasting |
| Много внешних клиентов, единый вход | API Gateway / BFF | Маршруты, auth, rate limit, выбор один vs BFF по каналам |
| Единые политики service-to-service на сотнях сервисов | Service Mesh | Retry/timeout на mesh vs в коде; tracing; mTLS; sidecar как носитель политик |
| Миграция монолита без big bang | Strangler Fig | Маршрутизация по релизам, контракты на границе, parity, retire plan |
Решение фиксируйте в ADR: контекст, выбранный паттерн, отвергнутые альтернативы (например 2PC vs Saga, sync publish vs Outbox, polling vs CDC), последствия для НФТ.
Вопросы на архитектурное ревью
- Какой бизнес-инвариант должен выполняться после сбоя на шаге N Saga?
- Что делать, если компенсация не удалась — retry, эскалация, ручной runbook, блокировка?
- Как пользователь видит промежуточное состояние (оплата прошла, доставка ещё нет)?
- Что происходит при повторной доставке того же события из Outbox — есть ли Inbox/dedup на consumer?
- Какой fallback при open circuit для оплаты — можно ли завершить заказ?
- Где проходит correlation id от API Gateway через Saga до логов поддержки?
- Database per service соблюдён или есть shared DB — и как это ломает границы?
- Кто владеет моделью процесса при orchestrated Saga — 03.04.01 BPMN 2.0 - введение и основы?
- Какой SLA задержки публикации из outbox и кто алертит на lag?
Связка паттернов
flowchart TB GW[API Gateway] --> S1[Service A] S1 -->|local TX + outbox| DB1[(DB A)] OUT[Outbox poller / CDC] --> K[Event bus] K -->|choreography| S2[Service B] K -->|choreography| S3[Service C] ORCH[Orchestrator] -.->|commands| S2 ORCH -.->|commands| S3 S2 -->|on failure| COMP[Compensation] S3 -->|on failure| COMP MESH[Service Mesh / sidecar] -.-> S1 MESH -.-> S2 MESH -.-> S3
Типовая цепочка: Gateway принимает запрос → сервис A в одной транзакции пишет данные и outbox → poller/CDC публикует событие → шаги Saga идут через хореографию (реакция на события) или оркестратор (явные команды) → mesh обеспечивает retry/timeout/CB между сервисами → при сбое — компенсации; consumer ведёт Inbox для dedup.
4. Типовые ошибки и ограничения
| Ошибка | Почему опасно | Как избежать |
|---|---|---|
| «Saga = ACID между сервисами» | Ложные ожидания заказчика, неверные критерии приёмки | Разделять бизнес-инварианты и модель согласованности read-моделей |
| Saga без сценария failed compensation | «Застрявшие» процессы, ручной разбор после первого инцидента | Retry, эскалация, runbook, статус в UI — в AC и ADR |
| Outbox без Inbox/идемпотентности consumer | Дубли заказов и списаний при at-least-once | event id / idempotency key и dedup store в 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ) |
| «CQRS ⇒ Event Sourcing» | Лишняя сложность без потребности в аудите | CQRS с read replica; ES — отдельное решение |
| Circuit Breaker только как «503» | Неопределённое поведение для бизнеса и UX | Сценарии деградации в Use Case / 02.04.02 Критерии приёмки (Acceptance Criteria) |
| Event Sourcing «статус = последнее событие» | Неверная модель для state machine | Редукция по типам; тест-кейсы на порядок событий — 08.03 Тестирование требований (статическое тестирование) |
| Gateway «обязателен от 5 сервисов» | Спорное решение без контекста | Критерии: клиенты, edge security, агрегация |
| Общая БД на несколько сервисов | Невозможны независимые Saga/Outbox/ES | Database per service в границах декомпозиции |
| Паттерн ради паттерна | Операционная сложность без выигрыша | ADR с отвергнутыми альтернативами и измеримым симптомом |
Пустые формулировки заменяйте вопросами: не «нужна зрелая DevOps-культура», а «кто владеет runbook оркестратора Saga, какой RTO, кто on-call». Не «продуманная стратегия компенсаций», а таблица шаг → компенсация → инициатор → идемпотентный ключ → действие при failed compensation.
Детали протоколов, Kafka, OpenAPI, rate limiting и балансировки сознательно не разворачиваются здесь — см. 03.05.01 Виды интеграции (точка-точка, ESB, API-led), 03.05.06 Event-Driven Architecture, 03.06.03 Rate Limiting — управление пропускной способностью, 03.06.04 Стратегии балансировки нагрузки.
Связи с другими разделами базы
- Архитектура и декомпозиция — 03.01.01 Понятие архитектуры ИС и её виды (бизнес-, системная, техническая), 03.01.03 Декомпозиция системы на подсистемы и модули, 03.01.04 Интерфейсы и интеграционные взаимодействия
- Интеграция и события — 03.05.06 Event-Driven Architecture, 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events), 03.05.02 Синхронные протоколы (REST, gRPC, GraphQL), 03.05.05 Безопасность API
- Соседние темы 03.06 — 03.06.02 Основы Domain-Driven Design, 03.06.03 Rate Limiting — управление пропускной способностью, 03.06.04 Стратегии балансировки нагрузки
- НФТ, приёмка и тестирование — 08.02 Нефункциональные требования (НФТ) — классификация и стандарты, 02.04.02 Критерии приёмки (Acceptance Criteria), 08.03 Тестирование требований (статическое тестирование), 08.07 Основы кибербезопасности для системного аналитика
- Платформа — 07.08 Контейнеризация и оркестрация (Docker, Kubernetes)
- Артефакты и кейсы — 10.01.13 Шаблон архитектурного решения (ADR), 10.01.06 Шаблон спецификации Producer (Kafka, RabbitMQ), 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ), 10.02 Разбор кейса "Миграция legacy-системы"
- Общие принципы решений — 01.06 Принципы и паттерны проектирования решений
Источники и дополнительные материалы
Книги и стандарты
- Chris Richardson — Microservices Patterns (Saga, Outbox, Gateway, CQRS, Event Sourcing)
- Sam Newman — Building Microservices (Gateway, Service Mesh, Strangler Fig)
- Martin Fowler — Patterns of Enterprise Application Architecture (базовые паттерны интеграции и persistence; CQRS и Event Sourcing у Fowler — в отдельных статьях, не как ядро POEAA)
Статьи и каталоги паттернов
- Chris Richardson — каталог паттернов: https://microservices.io/patterns/index.html
- Saga: https://microservices.io/patterns/data/saga.html
- Transactional Outbox: https://microservices.io/patterns/data/transactional-outbox.html
- Martin Fowler — CQRS: https://martinfowler.com/bliki/CQRS.html
- Martin Fowler — Event Sourcing: https://martinfowler.com/eaaDev/EventSourcing.html
- Martin Fowler — Circuit Breaker: https://martinfowler.com/bliki/CircuitBreaker.html
- AWS — Strangler Fig: https://docs.aws.amazon.com/prescriptive-guidance/latest/modernization-decomposing-monoliths/strangler-fig.html
- AWS Well-Architected — graceful degradation / circuit breaker: https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_mitigate_interaction_failure_graceful_degradation.html
- InfoQ — Sidecar и service mesh: https://www.infoq.com/articles/sidecar-container-service-mesh/
Спецификации и инструменты (реализации, не теория)
- Resilience4j — Circuit Breaker, Bulkhead: https://resilience4j.readme.io/docs
- Debezium — Outbox Event Router: https://debezium.io/documentation/reference/stable/transformations/outbox-event-router.html
- Istio — https://istio.io/latest/docs/
- Linkerd — https://linkerd.io/2/overview/