Содержание
На старте почти всегда стоит выбирать монолит — лучше модульный, с чёткими границами внутри одного приложения. Микросервисы решают проблемы больших команд и неравномерной нагрузки, которых у нового продукта ещё нет, а платить за них приходится сразу: инфраструктурой, сложностью отладки и скоростью разработки. Делить систему на сервисы разумно, когда появилась конкретная причина.
Разберём, чем на практике отличаются подходы, когда микросервисы оправданы и как построить монолит, который потом можно разделить.
Чем монолит отличается от микросервисов?
Монолит — это одно приложение с общей кодовой базой и обычно одной базой данных, которое собирается и выкладывается целиком. Микросервисы — набор независимых приложений, каждое со своей зоной ответственности, своими данными и своим циклом выкладки; общаются они по сети через API или очереди сообщений.
Разница не в размере, а в том, где проходят границы. В монолите модули вызывают друг друга как обычные функции — быстро и в одной транзакции. В микросервисах каждый вызов — сетевой запрос, который может упасть, задержаться или прийти дважды.
| Критерий | Монолит | Микросервисы |
|---|---|---|
| Скорость старта | Высокая | Низкая: нужна инфраструктура с первого дня |
| Выкладка | Всё приложение целиком | Каждый сервис отдельно |
| Транзакции | Обычные, в одной базе | Распределённые: саги, компенсации |
| Отладка | Один лог, один стектрейс | Трассировка запросов между сервисами |
| Масштабирование | Всё приложение сразу | Только нагруженные сервисы |
| Независимость команд | Общий код и релиз | Каждая команда владеет своими сервисами |
| Стоимость инфраструктуры | Минимальная | Выше: оркестрация, мониторинг, сеть |
Почему микросервисы на старте замедляют продукт?
Микросервисы на старте замедляют продукт, потому что границы между сервисами приходится проводить до того, как понятна предметная область. Новый продукт меняется каждую неделю, и граница, проведённая неправильно, превращается в постоянные согласованные изменения в нескольких сервисах сразу.
Что ещё появляется с первого дня:
- контракты между сервисами и их версионирование;
- обработка сетевых ошибок, повторов и таймаутов;
- согласованность данных без общей транзакции;
- отдельные пайплайны сборки и выкладки для каждого сервиса;
- централизованные логи, метрики и трассировка, без которых ошибку не найти.
Всё это нужно, чтобы система вообще работала, но ничего из этого не приближает продукт к пользователю. Тот же вывод есть и в разборе выбора бэкенда для MVP: микросервисы на старте — одна из самых дорогих ошибок.
Фото: Unsplash
Когда микросервисы действительно оправданы?
Микросервисы оправданы, когда у системы появились реальные причины для разделения: несколько команд мешают друг другу в одном коде, части продукта требуют разного масштабирования или разного уровня надёжности. Причина должна быть конкретной, а не «на будущее».
Признаки, что отдельный сервис окупится:
- Над продуктом работают несколько команд, и общий релиз стал узким местом.
- Одна часть системы нагружена на порядок сильнее остальных — например, обработка медиа или расчёт рекомендаций.
- У части системы другие требования к надёжности или безопасности — платёжный контур, обработка персональных данных.
- Часть логики удобнее писать на другом стеке: машинное обучение на Python рядом с бэкендом на PHP.
Даже тогда выносят не всё сразу, а один сервис с понятной границей, и смотрят, окупается ли это.
Что такое модульный монолит и почему он хорош на старте?
Модульный монолит — это одно приложение, разделённое на модули с явными границами: у каждого модуля свои данные, публичный интерфейс и запрет на обращения к внутренностям соседей. Он даёт скорость разработки монолита и при этом готовит почву для выделения сервисов, если это когда-нибудь понадобится.
Пример структуры бэкенда, где каждый модуль отвечает за свою область:
app/
Modules/
Catalog/ — товары, категории, цены
Contracts/ — публичный интерфейс модуля
Models/
Services/
Orders/ — корзина, заказы, статусы
Payments/ — платежи, вебхуки, возвраты
Users/ — регистрация, профили, роли
Правила, которые держат границы
Граница модуля держится, только если её проверяют автоматически. Договорённость «не лезть в чужие модели» нарушится при первом дедлайне.
- модули общаются через интерфейсы или события, а не через чужие модели и таблицы;
- у каждой таблицы один модуль-владелец, остальные читают данные через его API;
- зависимости между модулями проверяются в CI архитектурными тестами;
- события внутри монолита оформляются так же, как будут выглядеть сообщения в очереди.
// Заказ не лезет в таблицу платежей, а спрашивает модуль платежей через контракт
final class OrderService
{
public function __construct(private PaymentGateway $payments) {}
public function pay(Order $order): PaymentStatus
{
return $this->payments->charge($order->id, $order->total);
}
}
Если позже модуль платежей станет отдельным сервисом, реализация PaymentGateway сменится с вызова функции на HTTP-запрос, а код заказов останется прежним.
Как понять, что пора разделять монолит?
Пора разделять монолит, когда вы можете назвать конкретную проблему, которую решит выделение сервиса, и измерить её. «Код стал большим» — не причина: большой код лечится модулями и рефакторингом.
Что обычно становится поводом:
- выкладка одной команды регулярно блокирует другую;
- тяжёлый модуль тормозит всё приложение, и отдельное масштабирование дешевле, чем масштабировать всё;
- сбой одной части регулярно роняет систему целиком;
- требования аудита или безопасности требуют изолировать часть данных.
Выделяют сервис постепенно: сначала модуль доводят до чистых границ внутри монолита, затем выносят его в отдельное приложение, а старый код переключают на сетевой вызов. Такой подход иногда называют «удушающим» (strangler fig): новая система постепенно замещает старую без большого переписывания.
Коротко
- Для нового продукта выбирайте монолит: он быстрее, дешевле и проще в отладке.
- Стройте монолит модульным: границы, владельцы данных, проверка зависимостей в CI.
- Микросервисы решают проблемы больших команд и неравномерной нагрузки — дождитесь, пока они появятся.
- Выносите сервисы по одному и только под конкретную измеримую причину.
Частые вопросы
Выдержит ли монолит большую нагрузку?
Да, монолит масштабируется горизонтально: несколько копий приложения за балансировщиком. Узким местом обычно становится база данных, а её нагрузку снижают индексы, кеш и реплики на чтение — независимо от архитектуры.
Нужен ли Kubernetes для микросервисов?
Не обязательно: несколько сервисов можно запустить через Docker Compose или управляемые облачные сервисы. Kubernetes оправдан, когда сервисов много и нужны автоматическое масштабирование и самовосстановление.
Сколько сервисов должно быть в микросервисной архитектуре?
Столько, сколько у системы независимых областей с разными командами или требованиями. Правильного числа нет; слишком мелкие сервисы, которые всегда меняются вместе, — признак неудачных границ.
Можно ли разделить базу данных монолита постепенно?
Можно. Сначала у каждой таблицы назначают модуль-владельца и убирают прямые обращения к чужим таблицам, затем переносят таблицы модуля в отдельную базу. Без первого шага второй превращается в переписывание.



