Архитектурные стили и декомпозиция на компоненты
Как эта тема связана с предыдущими
В прошлых статьях мы разобрали:
постановку задачи (зачем система нужна, границы, сценарии, критерии успеха)
требования (функциональные, нефункциональные) и ограничения (технологии, сроки, бюджет, регуляторика)Архитектурный стиль и декомпозиция — это следующий шаг: мы превращаем требования в структуру системы. Хорошая архитектура:
делает ключевые сценарии простыми в реализации
обеспечивает нужные нефункциональные свойства (надёжность, масштабирование, безопасность)
учитывает ограничения (команда, инфраструктура, сроки)Базовые понятия: стиль, компонент, граница
Архитектурный стиль
Архитектурный стиль — это набор принципов организации системы: какие есть типы компонентов, как они взаимодействуют, где хранятся данные, как достигаются свойства вроде надёжности и масштабирования.
Важно: стиль — не “модная технология”. Это выбранный набор компромиссов.
Компонент
Компонент — часть системы с чёткой ответственностью и контрактом взаимодействия.
Компонентом может быть:
модуль внутри одного приложения
отдельный сервис
очередь сообщений
база данных как самостоятельный элемент дизайнаГраница (boundary)
Граница — место, где меняются правила и ответственность:
другой владелец (команда)
другой жизненный цикл релизов
другое хранилище данных
другой протокол (HTTP вместо вызова функции)Чем больше границ, тем выше накладные расходы на интеграцию, тестирование и эксплуатацию.
Зачем нужна декомпозиция
Декомпозиция отвечает на практичные вопросы:
какие части системы можно разрабатывать параллельно
где изолировать изменения, чтобы они не ломали всё вокруг
где нужны отдельные масштабирование и отказоустойчивость
как управлять сложностью (чтобы код и процессы оставались поддерживаемыми)Цель декомпозиции — не “порезать на микросервисы”, а сделать систему управляемой при ваших требованиях и ограничениях.
!Диаграмма показывает, что стиль и декомпозиция являются следствием требований и ограничений
Основные архитектурные стили
Ниже — стили, которые чаще всего встречаются в реальных IT-проектах. Их можно комбинировать, но полезно понимать “центр тяжести” решения.
Монолит
Монолит — единое приложение (часто единый процесс и деплой), где модули взаимодействуют через вызовы функций, а не через сеть.
Когда подходит:
небольшой или средний продукт, важна скорость разработки
команда небольшая, нет выделенной SRE/платформы
требования по независимым релизам частей системы пока не критичныРиски:
сложнее масштабировать разные части по-разному
со временем растёт связность, релизы становятся “тяжёлыми”Полезный ориентир: часто лучше начать с монолита, но сразу держать модульные границы. См. MonolithFirst.
Модульный монолит
Модульный монолит — монолит, в котором архитектурно и организационно закреплены границы модулей:
явные зависимости между модулями
минимизация общего “глобального состояния”
контрактные интерфейсы между частямиКогда подходит:
хотите скорость монолита, но готовитесь к росту и усложнению
есть риск, что система станет “спагетти”, если не ввести границы заранееКлючевая идея: сначала научиться декомпозировать внутри процесса, и только потом переносить границы “через сеть”, если это действительно нужно.
Микросервисы
Микросервисы — набор автономных сервисов, которые разворачиваются независимо и взаимодействуют по сети.
Плюсы:
независимые релизы и изоляция изменений
независимое масштабирование “горячих” компонентов
ограничение размера контекста для командМинусы (часто недооценённые):
распределённые сбои и сложная отладка
накладные расходы на инфраструктуру, наблюдаемость, контракты
сложнее обеспечить согласованность данныхМикросервисы обычно оправданы, когда нефункциональные требования и организация (много команд, независимые релизы, высокая нагрузка на отдельные части) “окупают” сложность. Практический ввод — Microservices.
Событийно-ориентированная архитектура (Event-Driven)
Event-Driven подход строится вокруг событий (фактов), которые публикуются и потребляются компонентами асинхронно.
Когда подходит:
много интеграций и реакций на изменения состояния
нагрузка “скачет”, и важно сглаживать пики очередями
важна слабая связность между производителями и потребителямиТипичные риски:
сложнее трассировать бизнес-процесс “сквозь систему”
появляются вопросы идемпотентности, порядка событий, повторной обработкиСобытийность хорошо сочетается и с монолитом (внутренние события), и с микросервисами (брокер сообщений).
Слоистая архитектура (Layered)
Layered architecture — разделение на слои, например:
API/контроллеры
бизнес-логика
доступ к даннымКогда подходит:
типичные CRUD-сценарии
нужны понятные правила структуры кодаРиск:
слой “бизнес-логики” может превратиться в большой общий комСлои — полезный инструмент, но сами по себе не решают проблему границ домена.
Гексагональная архитектура (Ports and Adapters)
Гексагональная архитектура (она же Ports and Adapters) отделяет доменную логику от внешних деталей (БД, HTTP, очереди):
домен общается с внешним миром через “порты” (интерфейсы)
конкретные интеграции реализуются “адаптерами”Когда подходит:
важна тестируемость и заменяемость инфраструктуры
много интеграций, которые меняютсяХороший обзор — Hexagonal Architecture.
CQRS (разделение чтения и записи)
CQRS — разделение моделей и путей обработки команд (запись) и запросов (чтение).
Когда подходит:
чтение и запись имеют разные требования по производительности
нужны разные представления данных для разных сценариевРиск:
усложнение данных и консистентностиCQRS не обязательно означает Event Sourcing и не обязан быть “везде”. Часто это точечная оптимизация для узких мест. См. CQRS.
Serverless (FaaS + managed-сервисы)
Serverless — стиль, где значимая часть инфраструктуры скрыта провайдером, а логика реализуется функциями и управляемыми сервисами.
Когда подходит:
нерегулярная нагрузка, важна оплата “по факту”
нужен быстрый time-to-market
команда не хочет (или не может) содержать инфраструктуруОграничения:
vendor lock-in
холодные старты и лимиты выполнения
сложнее тестировать end-to-end без эмуляторовКак выбирать стиль: связь с требованиями
Полезно не “голосовать за стиль”, а проверять его через требования.
Быстрый чеклист вопросов
Какие 2–3 сценария самые критичные и частые?
Где ожидаются пиковые нагрузки и рост?
Нужны ли независимые релизы частей системы?
Какие требования к доступности (например, 99.9%) и восстановлению?
Насколько критична консистентность данных?
Какой уровень зрелости эксплуатации доступен (наблюдаемость, on-call, SRE)?
Какие есть жёсткие ограничения по технологиям и инфраструктуре?Таблица: стили и типичные компромиссы
| Стиль | Сильные стороны | Цена/риски | Частые триггеры выбора |
|---|---|---|---|
| Монолит | Скорость разработки, простая отладка | Сложнее независимые релизы и изоляция масштабирования | MVP, небольшая команда, ограниченный бюджет |
| Модульный монолит | Контроль сложности без распределённости | Требует дисциплины границ | Рост функционала, подготовка к масштабированию команд |
| Микросервисы | Автономность, независимые релизы | Высокая сложность эксплуатации | Много команд, разные профили нагрузки, нужна изоляция отказов |
| Event-Driven | Слабая связность, асинхронность | Труднее трассировка и консистентность | Много реакций на события, интеграции, пики нагрузки |
| Hexagonal | Тестируемость, сменяемость интеграций | Дисциплина и больше “слоёв” в коде | Интеграционно насыщенные домены |
| CQRS | Оптимизация чтения/записи | Усложнение данных | Сильно отличается профиль чтения и записи |
| Serverless | Быстрый старт, меньше ops | Lock-in, лимиты | Нерегулярная нагрузка, небольшой ops-ресурс |
Декомпозиция: как разрезать систему на компоненты
Принципы хорошей декомпозиции
Высокая связность внутри компонента: всё, что относится к одной ответственности, находится рядом.
Низкая связность между компонентами: изменения в одном компоненте минимально затрагивают другие.
Явные контракты: API, события, схемы данных, версии.
Явное владение данными: понятно, кто “источник истины” для каждой сущности.Типовые подходы к разрезанию
#### По слоям (техническая декомпозиция)
Пример: отдельно “UI”, отдельно “Backend”, отдельно “DB”.
Плюсы:
понятно, как начинать
совпадает с навыками многих командМинусы:
бизнес-изменение затрагивает все слои
легко получить “общую бизнес-логику”, которую никто не контролирует#### По доменам и сценариям (вертикальные срезы)
Пример: “Заказы”, “Оплата”, “Доставка”, “Каталог”.
Плюсы:
бизнес-изменения локализуются
проще выделять владение и ответственностьМинусы:
сложнее в начале, нужны обсуждения границПрактический ориентир для доменных границ — bounded context из DDD. Обзорный материал: Domain-Driven Design.
Алгоритм декомпозиции (практика)
Выпишите ключевые сценарии (из функциональных требований).
Выделите основные сущности и их жизненный цикл.
Сгруппируйте сущности и сценарии по “одной причине для изменения”.
Проведите границы владения данными: у каждой сущности должен быть “хозяин”.
Опишите взаимодействия: синхронные API и асинхронные события.
Проверьте НФТ: где нужен кэш, очередь, репликация, rate limiting.
Проверьте ограничения: что команда реально может поддерживать.!Пример декомпозиции на доменные компоненты и способы взаимодействия
Контракты взаимодействия: API и события
Синхронные вызовы (API)
Синхронное взаимодействие (например, HTTP/gRPC) удобно, когда:
нужен мгновенный ответ пользователю
операция короткая и надёжность канала высокаяРиски:
каскадные отказы (если зависимый сервис медленный или недоступен)
рост латентности из-за цепочек вызововПрактики защиты:
таймауты и лимиты
ретраи только там, где это безопасно
circuit breaker
деградация (fallback)Асинхронные события
События подходят, когда:
можно обработать действие “в фоне”
важно развязать компоненты по времени
нужно подключать новых потребителей без изменения отправителяКритические понятия, которые стоит зафиксировать в дизайне:
идемпотентность: повтор события не должен ломать состояние
гарантии доставки: “хотя бы один раз” чаще реалистичнее, чем “ровно один раз”
схема события и версионирование: потребители должны переживать изменения форматаДанные и границы: кто владеет источником истины
Одна из самых частых причин проблем в системах — неясное владение данными.
Практичное правило:
у каждой сущности есть один компонент-владелец, который принимает решения об изменениях
другие компоненты получают данные через API владельца или через реплики (read model), но не “пишут напрямую”Антипаттерн:
“общая база на всех сервисов” как быстрый старт микросервисовЭто может быть временно допустимо как миграционный шаг, но тогда границы сервисов остаются условными: изменения схемы и транзакции превращаются в общий риск.
Частые ошибки при выборе стиля и декомпозиции
Выбор микросервисов без готовности к эксплуатации: нет наблюдаемости, нет контрактного тестирования, нет практик релизов.
Декомпозиция по технологиям вместо домена, из-за чего любое бизнес-изменение требует синхронизации нескольких команд.
Неявные контракты: нет версионирования API/событий, изменения ломают потребителей.
Нет владельца данных: несколько компонентов “думают”, что они источники истины.
Границы не соответствуют требованиям: например, выделили сервис, который всегда должен вызываться синхронно в критической цепочке, и получили лишнюю латентность.Что фиксировать в артефактах System Design
Минимальный набор, который помогает команде реализовать и сопровождать архитектуру:
Диаграмма контекста: внешние системы и границы ответственности.
Диаграмма компонентов/контейнеров: основные части системы и связи.
Контракты: ключевые API, события, схемы данных и версия.
Владение данными: таблица “сущность → владелец”.
Ключевые архитектурные решения и их причины (ADR-подход полезен). См. Documenting Architecture Decisions.Связь с дальнейшими темами курса
Дальше, опираясь на выбранный стиль и декомпозицию, мы будем углубляться в конкретику:
проектирование API и потоков данных
выбор хранилищ, консистентность и транзакционные границы
масштабирование, отказоустойчивость и деградация
наблюдаемость и эксплуатационные практикиПолезные источники
Martin Fowler: Microservices
Martin Fowler: MonolithFirst
Alistair Cockburn: Hexagonal Architecture
Martin Fowler: CQRS
Cognitect: Documenting Architecture Decisions