lenec ru

← все посты

Модульный монолит против микросервисов: критерии перехода без религиозных войн

18K

«Микросервисы» в современных командах часто превращаются в религиозный термин. На стартапе из пяти человек поднимают пятнадцать сервисов с своими репозиториями, своими CI и своими очередями, потому что «так делают взрослые». Через год команда тратит больше времени на инфраструктуру и интеграцию, чем на продуктовую разработку. С другой стороны, в зрелой компании на сорок инженеров одна большая монолитная кодовая база с десятками команд тоже превращает любую релизную пятницу в стресс.

Истина — где-то посередине, и это «где-то» зависит от вашего конкретного контекста. Расскажу, как я обычно выбираю между модульным монолитом и микросервисами, и какие критерии работают лучше «у нас же масштаб».

Что такое модульный монолит на самом деле

Модульный монолит — не «большой бесформенный комок кода». Это одно приложение, в котором внутренние границы проведены явно: модули с собственной структурой, своими доменными моделями, своими портами наружу. Внутри одного процесса, в одной базе или с разными схемами в одной БД.

Ключевое отличие от микросервисов: компонент развертывается как единое целое и общается внутри без сетевых вызовов. Ключевое отличие от «классического монолита»: у каждого модуля есть владелец, граница и контракт.

myapp/
  modules/
    catalog/        # своя модель, свои таблицы
      domain/
      api/          # публичные интерфейсы для других модулей
      internal/     # детали реализации, недоступны извне
    orders/
    billing/
    notifications/
  shared/
    eventbus/
    persistence/

В Java/Kotlin это естественно делается через Gradle/Maven-модули с jigsaw или явными правилами. В Go — через директории и go-imports. В .NET — через проекты в одном решении.

Что вы получаете от микросервисов и за какую цену

Микросервисы дают:

  • Независимый деплой: каждая команда катит свой сервис когда хочет.
  • Изоляцию по отказам и нагрузке: один тяжёлый сервис не валит остальных.
  • Технологический выбор: разные стеки в разных сервисах, если действительно нужно.
  • Масштабируемость по командам: сорок человек не толпятся в одном репозитории.

Платите вы за это:

  • Сетью между сервисами: timeout-ы, retry, circuit breakers.
  • Распределёнными транзакциями: saga, outbox, eventual consistency.
  • Эксплуатацией: десять Docker-образов, десять деплоев, десять наборов метрик.
  • Сложностью отладки: трейсинг, корреляция логов, воспроизведение бага в локальной среде.
  • Стоимостью разработки: каждое изменение, проходящее через несколько сервисов, требует согласования.

Если перечисленные выгоды для вас не критичны прямо сейчас — вы платите цену, не получая отдачи.

Когда модульный монолит — нормальный выбор

  • Команда меньше 20 человек. Разделение на 3–5 команд внутри одного монолита с чёткими модулями — рабочий вариант. Релизный конфликт легко разруливается, миграции БД — линейные.
  • Один продукт, понятный домен. Если все ваши сервисы говорят про один и тот же бизнес (один e-commerce, один SaaS-продукт), монолит логичен.
  • Релиз раз в день или реже. Независимый деплой не критичен, если вы и так релизитесь по графику.
  • Нет принципиально разной нагрузки между модулями. Если катаете всё одним пакетом и всё держит нагрузку, нет смысла раскидывать.

Я наблюдал успешные модульные монолиты, обслуживающие десятки тысяч RPS, в нагруженных проектах. Это не «мелкие пет-проекты» — это просто другой способ организации.

Когда пора резать на сервисы

Главный сигнал — не «технический», а организационный: команды начинают мешать друг другу. Конкретно это видно по:

  • Релизные конфликты. Команда A не может выкатить фичу, потому что команда B не закончила свою. Каждую неделю — встреча по координации релиза.
  • Конфликт миграций БД. Несколько команд одновременно меняют схему, и кто-то всегда последний.
  • Разная нагрузка. Один модуль обрабатывает миллионы запросов в день, другой — тысячи. Ресурсы тратятся неравномерно, масштабировать всё разом дорого.
  • Разные требования к доступности. Биллинг должен быть доступен 99,99%, отчёты — 99%. В одном процессе это сложно обеспечить.
  • Разные релизные циклы. Каталог обновляется каждый день, биллинг — раз в месяц после долгого тестирования. Совмещать в одном процессе неудобно.

Когда два-три из этих признаков совпадают — пора начинать. Не сразу резать всё, а выделить один-два самых проблемных модуля.

Постепенный переход

Я ни разу не видел успешной «большой переписки монолита на микросервисы за квартал». Видел много неуспешных. Рабочий путь — постепенный, шаг за шагом.

Шаг 1: Привести монолит в форму

Если кодовая база — каша без модулей, начинать с микросервисов бессмысленно: вы получите кашу с сетью внутри. Сначала наведите порядок: выделите модули, проведите границы, ограничьте обращения через явные интерфейсы.

На этом этапе хороший показатель — модуль orders не должен делать SELECT из таблиц модуля catalog. Только через интерфейс. Если этого добились — у вас готов фундамент для разрезания.

Шаг 2: Выделить первый сервис

Берите модуль с самой явной болью (релизные конфликты, разная нагрузка) и выделяйте в отдельный сервис. Связь с остальным монолитом — через HTTP или события. Одна команда отвечает за выделение, монолит остаётся.

На этом этапе появляется первая инфраструктура: API gateway или внутренний роутинг, мониторинг, контракты. Это разовая работа, которую вы делаете для первого сервиса и потом переиспользуете.

Шаг 3: Жить так какое-то время

После выделения первого сервиса — пауза. Поймите, как с ним жить: как мониторить, как деплоить, как восстанавливаться при инцидентах. Если на этом этапе становится больно — стоп, дальше не идём, исправляем процессы.

Если всё нормально — выделяем следующий по приоритету. Тоже один. Так появляется ритм: 2–4 сервиса в год, не двадцать за квартал.

Антипаттерны

Распределённый монолит

Самое популярное недоразумение. У вас десять «микросервисов», но релизятся они всегда вместе, потому что любое изменение требует синхронных правок в нескольких. Это монолит, разделённый по сети, и он наследует все недостатки монолита плюс все недостатки распределённой системы.

Признак: чтобы выкатить одну фичу, нужно скоординированно обновить три-пять сервисов. Лекарство: пересмотреть границы. Возможно, эти сервисы должны быть одним.

Микросервисы по техническим слоям

«Сервис аутентификации», «сервис нотификаций», «сервис orchestration». Если такие имена соседствуют с «сервис заказов», у вас смешаны два принципа разделения: бизнесовый и технический. Это ведёт к тому, что каждое бизнес-действие проходит через два-три технических сервиса, и трассировка превращается в детектив.

Лучше — разделение по бизнес-доменам. Аутентификация и нотификации — это библиотеки или инфраструктурные сервисы, не бизнес-сервисы.

Сервисы из-за «команды этой страны»

Иногда сервисы создаются по организационной причине: «эта команда сидит в другом офисе, проще дать им свой сервис». Иногда это нормально, иногда — повод сделать модуль с чётким владением, не отдельный сервис. Если бизнес-домен не отличается, отдельный сервис тут — лишний накладной расход.

Сравнительные критерии в одном месте

  • Команда: до 20 — обычно монолит хватает; больше — задумайтесь.
  • Релизы: один общий темп — монолит; разные темпы — сервисы.
  • Нагрузка: однородная — монолит; неоднородная по модулям — повод выделить нагруженные.
  • Доступность: единый SLA — монолит; разные требования — повод разделить.
  • Технологии: одна — монолит; реальная (не прихоть) потребность в разных стеках — сервисы.
  • Эксплуатационная зрелость: нет SRE/DevOps — оставайтесь в монолите дольше; есть — раскрывает возможности микросервисов.

Что запомнить

Модульный монолит и микросервисы — это не лестница, а спектр. На разных этапах жизни системы оптимум разный, и зрелые команды двигаются по этому спектру в обе стороны: иногда выделяют сервис, иногда сливают два обратно. Выбор стоит делать по реальным проблемам, а не по образу «правильной архитектуры». Если у вас сейчас нет конкретной проблемы, которую микросервисы решают — оставайтесь в монолите и наводите в нём порядок. Это сэкономит вам месяцы инфраструктурной работы и позволит сосредоточиться на продукте.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.