Монолитная архитектура
Что такое монолит
Монолитное приложение — это единый развёртываемый артефакт (один процесс, одна кодовая база и, как правило, один пакет поставки), в котором все подсистемы (интерфейс, доменная логика, интеграции, доступ к данным) размещены вместе. Монолит может быть примитивным «всё-под-одной-крышей» или дисциплинированным модульным монолитом с чёткими границами модулей.
Сильные стороны
Простота разработки и отладки. Одна кодовая база, единый рантайм и стек. Локальный запуск полностью имитирует продакшн-контекст.
Согласованность данных по умолчанию. Границы транзакций легко провести в рамках одной БД, проще реализовать инварианты.
Простая поставка. Один артефакт — меньше движущихся частей в CI/CD, стандартные стратегии релизов (blue/green, canary) применяются тривиально.
Более низкие накладные расходы. Нет межсервисной сетевой латентности и сериализации — меньше расходов на инфраструктуру и эксплуатацию.
Слабые стороны
Риск эрозии границ. При слабой дисциплине модули «протекают», образуя «большой ком грязи».
Масштабирование целиком. Горизонтальное масштабирование зачастую вынуждено тащить весь код, даже если горячая только малая часть.
Замедление сборки и тестов. Рост кодовой базы увеличивает время сборки, запуска тестов и обратной связи.
Сложности параллельной работы больших команд. Высокие риски конфликтов при мерджах и каскадных изменений.
Основные подходы к проектированию монолита
Слоистая архитектура (Layered)
Классическая стратификация: Presentation
→ Application
→ Domain
→ Infrastructure
. Слои формируют «вертикальные правила зависимости»: верхние зависят от нижних, но не наоборот.
Модульный монолит (Modular Monolith)
Монолит разделён на изолированные модули с публичными контрактами. Внутренности модулей закрыты, межмодульная коммуникация идёт через фасады, команды/запросы или доменные события (в памяти).
Вертикальные срезы (Feature Slices)
Структурирование по фичам/поддоменам: каждая фича содержит свой ui
, application
, domain
, infrastructure
. Минимизируется кросс-зависимость между фичами, команды работают автономно.
Чистая архитектура / Гексагональная архитектура
Домен в центре, вокруг — порты/адаптеры. Инфраструктура подключается «снаружи» и подменяема. Это усиливает тестируемость и независимость домена.
Ключевые архитектурные решения в монолите
Границы модулей. Определяйте инварианты и агрегаты. Что должно меняться транзакционно вместе — в одном модуле.
Политика транзакций. Явно описывайте границы ACID-согласованности. Сквозные транзакции через всё приложение — антипаттерн.
Доступ к данным. Избегайте «общей» БД как интеграционного слоя: модуль не должен читать таблицы другого модуля напрямую.
Коммуникация модулей. Команды/запросы и внутрипроцессные события лучше прямых импортов. Это снижает сцепление.
Наблюдаемость. Трейсы на уровень модулей/юзкейсов, метрики по горячим путям, структурированные логи.
Эволюция. План миграций схемы, фича-флаги, «двойная запись» при рефакторинге.
Использование КОП для разбиения разработки на команды
КОП (Компонентно-ориентированное программирование) — дисциплина проектирования, где система строится из независимых компонентов с чёткими интерфейсами и контрактами. В монолите это обеспечивает командную автономию без разделения на микросервисы.
Компонент → команда. Каждому компоненту назначается компонентная команда (ownership кода, сборки, инцидентов).
Контракты вместо общих утилит. Публичная поверхность компонента — фасады/порты; общий «утилитарный» слой — только для кросс-срезов (логирование, авторизация).
Изоляция хранилища. Таблицы компонента отделены (схема/префикс). Доступ — только через порт компонента.
Внутренние события. Компоненты публикуют доменные события (в памяти) и подписываются на них — без прямых импортов реализаций.
Границы изменения. Изменение инварианта одного компонента не должно требовать каскада в остальных.
Практики для монолита в продакшне
Статические правила импорта. Линт-правила/арх-линтеры запрещают пересечение модульных границ.
Контрактные тесты. Тесты проверяют публичные фасады модулей и события, а не приватные детали.
Миграции с обратной совместимостью. Схема меняется в несколько шагов: расширение → двойная запись → переключение → очистка.
Версионирование контрактов. Переход через
v1
/v2
фасадов, период совместимости.Стратегии развертывания. Blue/Green, Canary, быстрый откат. Единый артефакт упрощает управление риском.
Типичные анти-паттерны и как их избегать
Big Ball of Mud. Лечится модульными границами, контрактами и архитектурным линкующим контролем.
Общая БД как интеграционный слой. Вместо прямого чтения чужих таблиц — порт/события.
God Object / God Service. Делите по инвариантам и агрегатам, а не по слоям контроллер/сервис/репозиторий механически.
Сквозные транзакции. Явно обозначайте границы. Межмодульные операции — через «сага»-подобные процессы внутри одного процесса.
Масштабирование и эволюция монолита
Scale-up & горизонтальное масштабирование процесса. Реплики приложения за балансировщиком, sticky-sessions при необходимости.
Разделение нагрузки. Вынесение фоновых работ в очереди/воркеры, read-replicas для отчётности.
Стратегия «Strangler Fig». Когда появится потребность — выносите самый горячий компонент в отдельный сервис, не ломая контракты.
Мини-шаблоны структуры репозитория (пример)
Пример инварианта на уровне БД
Даже в монолите инварианты лучше дублировать: в домене и в БД.
Чек-лист готовности
Определены публичные фасады модулей; приватное не импортируется снаружи.
Покрыты контрактные тесты и тесты инвариантов.
Наблюдаемость: метрики по модулю и ключевым юзкейсам, трассировка.
Схема БД разделена по модулям (схемы/префиксы), миграции обратносуместимы.
Релизная стратегия документирована, откат проверен.