Начать проект

Обсудим проект?

Нажимая на кнопку, вы даете согласие на обработку персональных данных и соглашаетесь с политикой конфиденциальности.

Отправлено!

Спасибо за заявку, мы свяжемся с вами в ближайшее время!

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

Монолит и микросервисы: два подхода к архитектуре системы

Разговор обычно начинается с фразы «нам нужны микросервисы, у нас всё тормозит и релизы долгие». Через полгода после разделения релизы иногда становятся ещё дольше — потому что причина была не в архитектуре. Мы в Code Pilots развиваем и переделываем системы заказчиков, и решение о структуре всегда упирается в два вопроса: что именно мешает сегодня и кто будет это эксплуатировать.

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

Между ними есть третий вариант, о котором почему-то говорят реже всего: модульный монолит, где границы уже проведены, а разворачивается всё по-прежнему вместе.

Монолит и микросервисы

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

Микросервисы — набор сервисов, каждый из которых отвечает за свою часть бизнеса, имеет собственные данные, разворачивается независимо и общается с соседями по сети. Ключевое слово здесь — «независимо»: если два сервиса нельзя выкатить по отдельности, это распределённый монолит, худший из вариантов.

Что не является причиной выбора: язык, фреймворк, модно или не модно. Как устроена серверная часть в принципе, разобрано в материале про бэкенд — здесь речь о том, как эту часть нарезать.

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

Настоящие причины разделения

Причин, которые действительно оправдывают распределённую систему, немного.

Независимые релизы. Пять команд ждут общего релизного поезда, любое изменение проходит общую регрессию. Если это главная боль — разделение помогает.

Разные требования к масштабированию. Поиск и каталог держат тысячи запросов в секунду, а бухгалтерский контур — десятки. Масштабировать всё приложение ради одной части дорого.

Разная критичность. Падение модуля рекомендаций не должно ронять оформление заказа. Изоляция отказов — весомый аргумент.

Разные технологии по существу задачи. Обработка изображений, машинное обучение, реалтайм — там, где выбор стека продиктован задачей, а не вкусом.

Границы ответственности. Когда за части системы отвечают разные команды с разным темпом, общая кодовая база превращается в постоянные конфликты.

А вот «тормозит» само по себе причиной не является: медленные запросы к базе после разделения останутся медленными. Сначала выясняется, где именно узкое место — этим занимаются профилирование и нагрузочные прогоны, о которых мы писали в материале про высоконагруженные системы.

Цена в эксплуатации

Микросервисы — это не разовые затраты на разделение, а постоянная строка расходов.

Что появляется Что это значит на практике
Инфраструктура Оркестрация контейнеров, реестры образов, сети, секреты — отдельный слой, который нужно поддерживать
Наблюдаемость Логи, метрики и трассировка по всем сервисам; без этого инцидент разбирается сутками
Конвейеры сборки Свой конвейер у каждого сервиса, версии контрактов, совместимость
Тестирование Сквозные сценарии на нескольких сервисах, контрактные тесты, тестовые окружения
Дежурства Кто-то должен отвечать ночью за каждый критичный сервис
Время на изменения Правка, затрагивающая три сервиса, требует согласования контрактов и трёх релизов
Люди DevOps-компетенции обязательны, а не желательны

Сумма этих пунктов — обычно один-два человека в постоянной эксплуатации плюс инфраструктурный бюджет. Для крупной компании это шум, для команды из восьми человек — треть ресурса разработки.

Микросервисы не делают разработку быстрее сами по себе. Они переносят сложность из кода в эксплуатацию, и за неё платят каждый месяц.

Модульный монолит

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

Что это даёт. Границы проведены, значит, код не превращается в клубок. Разработка остаётся простой: один репозиторий, одно окружение, обычная отладка. Транзакции работают штатно, без распределённых сценариев. А когда какой-то модуль действительно потребует независимого релиза или масштабирования, он выносится в отдельный сервис за недели, а не за год.

Практическое правило: начинать со структурированного монолита и выносить сервисы по мере появления доказанной необходимости. Обратный путь — собирать распределённую систему сразу «на вырост» — почти всегда дороже и медленнее.

Границы сервисов

Самое сложное решение проекта, и техническое чутьё здесь помогает меньше, чем знание бизнеса.

Границы проводятся по бизнес-возможностям, а не по слоям и не по таблицам: «заказы», «склад», «оплаты», «уведомления», а не «сервис базы», «сервис интерфейса». Проверка простая: если изменение одного бизнес-правила требует правки в трёх сервисах, границы проведены неверно.

Полезный ориентир — как устроены команды и как меняется бизнес. Части, которые меняются вместе и одними людьми, должны жить вместе. Части, которые эволюционируют независимо и в разном темпе, разумно разделять.

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

Разберём вашу архитектуру

Посмотрим на релизный цикл, нагрузку и связи в коде, покажем, где реально болит и что стоит выносить первым — если выносить вообще.

Обсудить проект

Данные

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

Как только два сервиса читают и пишут одну таблицу, независимость исчезает: любое изменение схемы затрагивает обоих, а релизы приходится согласовывать. Формально это микросервисы, фактически — монолит с сетевыми задержками.

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

Практический вывод: если бизнес-процесс требует строгой одномоментной согласованности, его части лучше оставить в одном сервисе.

Пока два сервиса ходят в одну таблицу, у вас не микросервисы, а монолит с сетевыми задержками и тремя конвейерами сборки.

Связь между сервисами

Два способа, и оба обычно нужны.

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

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

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

Наблюдаемость

В монолите ошибка видна в одном логе. В распределённой системе запрос проходит через пять сервисов, и без специальной подготовки понять, где он застрял, невозможно.

Минимальный набор: сквозной идентификатор запроса, который передаётся между всеми сервисами; централизованный сбор логов; метрики по каждому сервису (время ответа, ошибки, очереди); трассировка, показывающая путь запроса и время на каждом шаге; оповещения по отклонениям.

Это не роскошь и не «сделаем потом»: без наблюдаемости распределённая система становится неуправляемой на второй месяц эксплуатации. Закладывать её нужно в тот же спринт, что и первый вынесенный сервис.

Как проходит разделение

Разделение никогда не делается разом. Рабочая последовательность выглядит так.

  • 1. Диагностика. Что именно болит: релизы, нагрузка, отказы, конфликты команд. На этом шаге часто выясняется, что проблема решается парой индексов и разделением репозиториев. Диагностику удобно делать как аудит кода и архитектуры — с цифрами, а не по ощущениям.
  • 2. Наведение порядка внутри. Модули, явные границы, чистка зависимостей. Это полезно само по себе и является подготовкой к выносу.
  • 3. Первый сервис. Выносится часть с наименьшим числом связей и понятной ценностью: уведомления, отчёты, интеграции, поиск.
  • 4. Инфраструктура и наблюдаемость. До выноса второго сервиса, а не после пятого.
  • 5. Постепенное вытеснение. Новый функционал пишется в сервисах, старый переносится по мере необходимости. Монолит какое-то время живёт рядом — это нормальное состояние на годы.
  • 6. Данные. Разделение схем — самая долгая часть, обычно последняя.
Порядок разделения: диагностика, наведение порядка внутри, первый вынесенный сервис, инфраструктура и наблюдаемость

Когда микросервисы не нужны

Признак Что это значит
Команда до 8–10 человек Все и так релизят вместе; независимость не даст выигрыша
Один релизный цикл Разделение не ускорит то, что и так выходит одним пакетом
Нагрузка укладывается в вертикальное масштабирование Дешевле добавить ресурсов, чем строить распределённую систему
Нет DevOps-компетенций Инфраструктуру будет некому эксплуатировать
Продукт ещё ищет форму Границы будут меняться, а менять их между сервисами дорого
Главная боль — качество кода Микросервисы не лечат код, они распределяют его по сети

Отдельно про стартапы и новые продукты: пока гипотезы не проверены, распределённая архитектура замедляет проверку идей и съедает бюджет, которого хватило бы на несколько итераций продукта.

Команда из семи человек и десять сервисов — это не архитектура, а способ занять двух разработчиков поддержкой инфраструктуры.

Команда и инфраструктура

Что должно быть на месте до перехода: люди, умеющие эксплуатировать контейнерную платформу; практика автоматических сборок и развёртывания; налаженные логи и метрики; дисциплина работы с версиями контрактов; готовность держать дежурства.

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

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

Сколько стоит

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

По нашим работам: аудит кода и архитектуры с планом изменений — от 200 до 600 тыс. ₽, работы по инфраструктуре и конвейерам отдельной услугой — от 200 тыс. ₽, развитие и переделка системы в составе проекта — от 1,7 млн ₽, разработка высоконагруженного продукта — от 5 млн ₽. Точная цифра зависит от размера системы и состояния кода: оценка проекта бесплатная.

Прикинуть бюджет можно заранее — бесплатный калькулятор оценки собирает вилку по составу работ за несколько минут.

Посчитаем миграцию

Расскажите про систему и команду — вернём план по этапам, порядок сумм и честную оценку рисков на каждом шаге.

Получить консультацию

Ошибки

Разделили без диагностики. Проблема была в запросах к базе, а получили те же запросы, только через сеть.

Оставили общую базу. Сервисы независимы на схеме и связаны в реальности; релизы по-прежнему согласуются.

Нарезали слишком мелко. Сервис на каждую сущность превращает простую операцию в десяток сетевых вызовов.

Не сделали наблюдаемость. Первый серьёзный инцидент разбирается двое суток, и команда возвращается к идее «собрать всё обратно».

Не посчитали эксплуатацию. Инфраструктура и дежурства съедают ресурс, которого не заложили в план.

С чего начать

Первое — сформулировать боль в измеримом виде: сколько времени проходит от готовности функции до продакшена, сколько релизов в неделю, что падает и как часто. Без этих цифр архитектурное решение принимается по ощущениям.

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

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

С чего начать: измерить боль в цифрах, проверить дешёвые гипотезы, выбрать один сервис для выноса

Обсудим вашу систему?

Посмотрим на цифры релизов и точки отказов, назовём первый шаг и оценим объём работ. Оценка бесплатная.

Обсудить проект

Часто задаваемые вопросы

Монолит — одно приложение с общей кодовой базой, общим развёртыванием и, как правило, общей базой данных. Микросервисы — набор самостоятельных сервисов, каждый со своими данными и собственным релизным циклом, общающихся по сети. Главный практический критерий: если сервисы нельзя выкатывать независимо друг от друга, это не микросервисы, а распределённый монолит — с недостатками обоих подходов.

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

Одно разворачиваемое приложение с явными внутренними границами: у каждого модуля своя зона ответственности, свои таблицы и запрет обращаться к внутренностям соседей напрямую. Даёт большую часть организационных преимуществ разделения без стоимости распределённой эксплуатации и оставляет возможность вынести любой модуль в отдельный сервис, когда это станет нужно.

Помимо разработки появляется постоянная стоимость: контейнерная платформа, конвейеры сборки для каждого сервиса, централизованные логи, метрики и трассировка, тестовые окружения, дежурства. На практике это обычно один-два выделенных человека и заметный инфраструктурный бюджет. Для команды из восьми разработчиков такая нагрузка съедает существенную часть ресурса.

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

Разработка с Code Pilots

Code Pilots — студия заказной разработки полного цикла: веб- и мобильные продукты со сложной бизнес-логикой, интеграциями и высокими нагрузками. По архитектуре мы делаем полезную часть работы: диагностику с цифрами, план изменений, наведение порядка в модулях, вынос сервисов по одному, инфраструктуру и наблюдаемость.

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

Внутри — backend-разработчики, DevOps, аналитики и QA, middle+ и senior. Назовите три цифры: сколько релизов в неделю, сколько времени от готовности до продакшена и что падает чаще всего. По ним уже видно, нужна ли вам другая архитектура. Обсудить проект →

Обсудим проект?

Заполните форму или напишите нам
на airmail@code-pilots.ru
Нажимая на кнопку, вы даете согласие на обработку персональных данных и соглашаетесь с политикой конфиденциальности.

Спасибо!

Спасибо за сообщение, мы свяжемся
с вами в ближайшее время!
success