Представьте: у вас три сервиса, которые общаются между собой по сети. Каждый живёт в своём репозитории, деплоится независимо, написан на своём стеке. Звучит красиво — пока вы не посчитаете, сколько это стоит и сколько боли приносит. Микросервисная архитектура — это не цель, а инструмент. И у этого инструмента есть конкретная цена, которую нужно уметь считать.
Эта статья — о том, когда разделение сервисов реально окупается, а когда вы просто платите за сложность. И наоборот — когда пора объединять то, что расползлось слишком далеко.
- Почему вообще возникает вопрос
- Сколько стоит один микросервис на самом деле
- Когда разделять — и это окупается
- Разные требования к масштабированию
- Разные циклы изменений
- Разные требования к доступности
- Разные технологические стеки
- Когда объединять — и это выгоднее
- Сервисы всегда меняются вместе
- Слишком мелкое разделение
- Распределённые транзакции стали нормой
- Сетевые проблемы стали частыми
- Сравнительная таблица: разделение vs объединение
- Как принять решение: практический фреймворк
- Сценарии из практики
- Ситуация 1: стартап, 3 разработчика
- Ситуация 2: компания среднего размера, 3 команды
- Ситуация 3: крупная платформа, 15+ сервисов
- Частые ошибки
- Как лучше сделать: рекомендации
- Итог
Почему вообще возникает вопрос
Микросервизацию продавали как серебряную пулю. Netflix, Amazon, Spotify — все перешли, значит, и нам надо. Но за кадром осталось, что у этих компаний тысячи инженеров и бюджеты, которые позволяют содержать целые команды на каждый сервис.
В реальности большинство проектов проходят одну и ту же траекторию:
- Начинаем с монолита — всё в одном репозитории, деплой одним куском.
- Сталкиваемся с проблемами масштабирования или командной работы.
- Разрезаем на микросервисы — на бумаге всё выглядит логично.
- Через полгода понимаем, что сервисов стало слишком много, и каждый требует внимания.
- Начинаем думать, что объединить обратно.
Вопрос не в том, «микросервисы или монолит». Вопрос в том, какую экономическую ценность несёт каждое разделение и каждое объединение.
Сколько стоит один микросервис на самом деле
Когда команда говорит «давайте вынесем это в отдельный сервис», обычно учитывают только выгоду: независимый деплой, отдельное масштабирование, чистые границы. Но стоимость владения редко считается честно.
Вот реальные статьи расходов, которые появляются с каждым новым сервисом:
- Инфраструктура. Каждый сервис — это минимум один под в Kubernetes, свой ресурсный профиль, свои лимиты. Даже если сервис простой, он потребляет CPU и память на поддержание рантайма.
- Наблюдаемость. Логи, метрики, трейсы — всё нужно настраивать отдельно. Если у вас 15 сервисов, вы потратите дни только на то, чтобы мониторинг работал корректно для каждого.
- CI/CD. Каждый пайплайн нужно создать, поддерживать, обновлять. Это не разовая работа — зависимости меняются, сборщики ломаются, секреты обновляются.
- Межсервисное взаимодействие. gRPC, REST, очереди сообщений — каждый канал связи нужно проектировать, тестировать, мониторить. Сетевые задержки, ретраи, идемпотентность — всё это код, который кто-то должен написать и поддерживать.
- Когнитивная нагрузка на команду. Инженер должен держать в голове контекст нескольких сервисов, понимать их контракты, знать, где что лежит. Это не измеряется строчками кода, но напрямую влияет на скорость разработки.
Грубая оценка: для небольшой команды (5–10 человек) каждый дополнительный сервис добавляет 10–20% времени на инфраструктурные задачи. То есть, если у вас 10 сервисов, пара инженеров может быть занята только тем, что всё это держит на плаву.
Когда разделять — и это окупается
Есть ситуации, когда микросервисная архитектура даёт реальный экономический эффект. Они связаны не с модой, а с конкретными ограничениями вашей системы.
Разные требования к масштабированию
Если один компонент системы обрабатывает 10 000 запросов в секунду, а другой — 10, держать их в одном процессе нерационально. Вы будете масштабировать всё целиком, переплачивая за ресурсы там, где они не нужны.
Пример: у вас есть сервис генерации отчётов (тяжёлый, CPU-bound) и сервис аутентификации (лёгкий, I/O-bound). Если они в одном монолите, вам придётся запускать 10 реплик всего приложения, чтобы справиться с нагрузкой на отчёты — хотя аутентификация справляется и с одной репликой. Разделив их, вы запускаете 1 реплику аутентификации и 10 реплик отчётов. Экономия на инфраструктуре может быть кратной.
Разные циклы изменений
Если один кусок кода меняется каждый день, а другой — раз в квартал, их разделение снижает риски. В монолите каждый деплой несёт риск сломать стабильную часть. В микросервисах вы деплоите только то, что изменилось.
Это особенно ценно, когда разные команды работают над разными частями продукта. Не нужно согласовать релизы, ждать друг друга, разбираться, чей код сломал сборку.
Разные требования к доступности
Критичный сервис (например, приём платежей) и некритичный (например, рекомендации) имеют разные SLA. Если рекомендации упадут, пользователь не сможет купить — и это катастрофа, если всё в одном процессе. Разделив их, вы изолируете сбои.
Разные технологические стеки
Иногда это оправдано: ML-пайплайн на Python, высоконагруженный API на Go, внутренний инструмент на Node.js. Но здесь важно не переборщить — каждый новый стек в команде увеличивает порог входа для новых людей и усложняет ротацию.
Когда объединять — и это выгоднее
Объединение сервисов — это не признак поражения. Это рациональное решение, когда цена разделения превышает выгоду.
Сервисы всегда меняются вместе
Если при каждом фиче вы меняете три сервиса и катите их синхронно — скорее всего, граница проведена неправильно. Независимый деплой превращается в иллюзию: вы всё равно координируете релизы, только теперь с сетевыми вызовами посередине.
Практический признак: если больше 70% фич затрагивают два и более сервиса одновременно — задумайтесь об объединении.
Слишком мелкое разделение
Сервис, который содержит 500 строк кода и обслуживает одну таблицу в базе — это не микросервис, это оверхед. Вы платите за сетевое взаимодействие, деплой, мониторинг ради кода, который можно держать как модуль внутри более крупного сервиса.
Хорошее правило: сервис должен быть достаточно большим, чтобы им занималась отдельная команда (или хотя бы один человек мог полностью его поддерживать). Если сервис настолько мал, что его никто не «владеет» по-настоящему — он лишний.
Распределённые транзакции стали нормой
Если у вас постоянно возникает необходимость координировать изменения между сервисами через Saga, двухфазный коммит или паттерн Outbox — вы платите конскую цену за консистентность. Иногда проще вернуть одну транзакцию в одном процессе, чем поддерживать распределённую консистентность через пять сервисов.
Сетевые проблемы стали частыми
Каждый сетевой вызов — это потенциальный отказ. Если у вас цепочка из четырёх сервисов для обработки одного запроса, и каждый доступен с вероятностью 99,5%, то общая доступность всей цепочки — около 98%. Это не теория — это реальность, с которой сталкиваются команды, которые слишком увлеклись разделением.
Сравнительная таблица: разделение vs объединение
| Критерий | Разделять имеет смысл | Объединять выгоднее |
|---|---|---|
| Частота изменений | Сервисы меняются с разной скоростью | Сервисы меняются синхронно в 70%+ случаев |
| Масштабирование | Разные профили нагрузки | Нагрузка распределена равномерно |
| Размер команды | Есть отдельные команды под каждый сервис | Одна команда может удерживать несколько сервисов |
| Связанность доменов | Границы бизнес-доменов чёткие и стабильные | Домены пересекаются и часто пересматриваются |
| Требования к доступности | Разные SLA для разных компонентов | Одинаковые требования к надёжности |
| Сложность транзакций | Большинство операций локальны внутри сервиса | Много кросс-сервисных транзакций |
| Зрелость команды | Команда имеет опыт эксплуатации распределённых систем | Команда тратит больше времени на дебаг сетевых проблем, чем на фичи |
Как принять решение: практический фреймворк
Вот последовательность вопросов, которую можно применить к любому сервису или группе сервисов:
- Какой бизнес-ограничение мы решаем разделением? Если ответ «просто так модно» или «на будущее» — разделение преждевременно.
- Сколько времени уходит на поддержку инфраструктуры этого сервиса? Если инфраструктурные задачи съедают больше времени, чем разработка фич — сервис, скорее всего, слишком мелкий.
- Можем ли мы чётко описать контракт сервиса одним абзацем? Если контракт размыт и постоянно нарушается — граница неправильная.
- Что произойдёт, если этот сервис упадёт? Если ответ «ничего страшного, основной флоу не сломается» — возможно, он не нужен как отдельный сервис.
- Сколько строк кода в сервисе? Это грубый, но показательный маркер. Сервис меньше 2000 строк — подозрительно мал для отдельного деплоя.
Сценарии из практики
Ситуация 1: стартап, 3 разработчика
У вас интернет-магазин: каталог, корзина, оплата. Всё это можно смело держать в монолите. Разделение на этом этапе убьёт вашу скорость разработки. Исключение: если платёжный шлюз требует изоляции по PCI DSS — тогда да, выделяем платежи.
Ситуация 2: компания среднего размера, 3 команды
У вас есть ядро продукта, аналитика и админка. Ядро — монолит, аналитика — отдельный сервис (потому что там своя база данных и свои запросы к данным), админка — отдельный сервис (потому что её делает отдельная команда и она меняется редко). Это разумный минимум.
Ситуация 3: крупная платформа, 15+ сервисов
Если у вас 15+ сервисов и вы замечаете, что каждая фича требует изменений в 3–5 сервисах — пора провести аудит. Скорее всего, часть сервисов можно объединить без потери качества. Начните с тех пар, которые чаще всего меняются вместе.
Частые ошибки
- Разделение по техническому признаку, а не по бизнес-домену. Сервис «аутентификация», сервис «авторизация», сервис «профили» — это не микросервисы, это слои одного сервиса, разнесённые по разным процессам. Разделяйте по бизнес-возможностям: «управление пользователями» — один сервис.
- Преждевременная микросервизация. Разрезать монолит до того, как вы понимаете реальные границы доменов — значит получить распределённый монолит. Это худший из миров: и сложность микросервисов, и связанность монолита.
- Игнорирование стоимости эксплуатации. Каждый сервис нужно мониторить, обновлять, патчить, масштабировать. Если у вас нет выделенной платформенной команды — умножайте количество сервисов на 0.5 инженера, который будет всё это поддерживать.
- Копирование чужой архитектуры. То, что работает в Uber, не работает в компании из 20 человек. Архитектура должна расти из ваших реальных ограничений, а не из хайповых докладов.
- Отсутствие механизма отката. Если вы разрезали монолит на сервисы, но не продумали, как откатить изменения — вы создали систему, которую нельзя безопасно обновлять. Это не архитектура, это долг.
Как лучше сделать: рекомендации
- Начинайте с монолита и выделяйте сервисы по мере необходимости. Не разрезайте заранее. Выделяйте, когда появилось реальное ограничение: нагрузка, безопасность, независимый деплой.
- Считайте стоимость владения. Перед тем как создать новый сервис, оцените: сколько времени уйдёт на его поддержку в месяц. Если ответ «больше, чем на разработку фич» — не создавайте.
- Используйте модульный монолит как промежуточный этап. Чёткие границы модулей внутри одного процесса дают 80% преимуществ микросервисов без эксплуатационной сложности. А когда настанет момент — модуль легко вынести в отдельный сервис.
- Объединяйте без стыда. Если два сервиса всегда деплоятся вместе и меняются синхронно — объедините их. Это не шаг назад, это рациональное упрощение.
- Внедряйте observability до разделения. Если вы не можете отследить запрос через три сервиса — не разрезайте на пять. Сначала настройте трейсинг, централизованные логи, метрики. Иначе дебаг превратится в ад.
Итог
Микросервисы — это не про технологии, это про экономику. Каждый сервис должен оправдывать своё существование конкретной выгодой: независимое масштабирование, изоляция сбоев, автономия команд. Если этой выгоды нет — вы просто усложняете систему и замедляете разработку.
Главное правило: разделяйте, когда есть реальное ограничение, которое разделение снимает. Объединяйте, когда стоимость разделения превышает выгоду. Не ищите идеальную архитектуру — ищите достаточную для вашего масштаба, вашей команды и вашего этапа.
Если сомневаетесь — не разделяйте. Объединить обратно всегда проще, чем распилить монолит, который вырос без чётких границ.
