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

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

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

Что мы называем монолитом и микросервисами

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

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

ВариантКак устроеноГде граница модулейКак выпускается
Классический монолитОдин код, одна база, модули связаны напрямуюУсловная, держится на договорённостяхВсё вместе, одним релизом
Модульный монолитОдин код и одна база, но у модулей явные интерфейсы и свои таблицыПроверяется в сборке: чужие таблицы напрямую читать нельзяВсё вместе, но тесты показывают, что именно изменилось
Сервисы по границамНесколько приложений, у выделенных частей своя базаСетевой контракт с версиямиРаздельно для выделенных частей, остальное остаётся вместе
Много мелких сервисовДесятки приложений, у каждого своя команда или владелецСетевой контракт и платформа для всех сервисовКаждый сервис сам по себе

Дальше сравниваем в основном второй и третий варианты: между ними реально выбирают для большинства бизнес-систем.

Что платите за распределённость

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

Что меняетсяВ монолитеВ сервисахЧто нужно, чтобы справиться
Вызов между частямиВызов функции: быстрый и либо удался, либо нетЗапрос по сети: может быть медленным, потеряться или дойти дваждыТайм-ауты, повторы с паузой, защита от дублей
Согласованность данныхОдна транзакция в одной базеДанные разнесены, единой транзакции нетСобытийная модель, компенсирующие действия, «исходящий ящик»
ОтладкаОдна трассировка стекаЗапрос проходит через несколько сервисовСквозной идентификатор запроса, журналы и трассировка в одном месте
ТестированиеЗапустил приложение и проверилНужны проверки контрактов и стенд со всеми сервисамиТесты на контрактах между сервисами, тестовое окружение
РазвёртываниеОдно приложениеКаждый сервис со своей сборкой, версией и откатомАвтоматическая сборка и выкатка, мониторинг каждого сервиса
Изменение интерфейсаМеняете вызов и все места его использованияСтарые потребители ещё работают на прежней версииВерсии контрактов, период совместимости

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

Когда достаточно монолита

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

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

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

Признаки того, что пора выделять сервис

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

ПричинаКак выглядитЧто выделяемЧто проверить до выделения
Разная нагрузкаОдна часть работает в десятки раз интенсивнее остальных, и её нужно масштабировать отдельноПриём событий от оборудования, обработка файлов, поискЧто именно упирается, и нельзя ли решить копированием всего приложения
Разная надёжностьСбой второстепенной части не должен останавливать основнуюУведомления, отчёты, интеграция с нестабильной внешней системойУмеет ли основная часть жить без выделенной
Разная скорость измененийОдну часть меняют каждую неделю, другую раз в полгодаЧасто меняющийся интерфейс, правила расчётаЧто будет при несовпадении версий
Разные командыДве и более группы мешают друг другу в одном репозитории и в одном релизеОбласть, которой полностью владеет одна командаЧётко ли разделены данные и обязанности

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

Как разрезать: по понятиям, а не по слоям

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

Для каждой границы проверьте три вещи:

  1. Владение данными. У понятия есть собственная база или схема, и остальные читают её только через интерфейс.
  2. Самостоятельность. Большинство операций выполняется внутри границы, а не требует вызова пяти соседей.
  3. Причина меняться вместе. То, что меняется вместе, лежит внутри одной границы.

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

Типичные ошибки

  • Распределённый монолит. Сервисы выделены, но общая база осталась, а релизы приходится выкатывать вместе. Все расходы микросервисов, ни одной выгоды.
  • Слишком мелкие сервисы. Каждый маленький и прост, но связей между ними много, и понять систему можно только целиком.
  • Синхронная цепочка. Запрос проходит через пять сервисов подряд, и доступность всей цепочки падает ниже доступности каждого звена. Подробнее об этом расчёте в статье про доступность «три девятки».
  • Нет наблюдаемости. Сервисы запущены, но журналов и трассировки нет, и причину сбоя ищут вручную в нескольких местах.
  • Общая библиотека с логикой. Правила бизнеса лежат в общей библиотеке, и любое изменение требует обновить все сервисы одновременно.
  • Переход «сразу всё». Остановка разработки на переписывание. Безопаснее постепенная замена с откатом на каждом шаге.

Как перейти без остановки бизнеса

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

  1. Тесты на текущее поведение. Без них вы не узнаете, что сломали при выносе.
  2. Границы внутри монолита. Сначала модули, потом сервис. Если граница не держится внутри одного приложения, она не удержится по сети.
  3. Первая часть — самая независимая. Выделяем и запускаем параллельно со старым путём, сравниваем результаты.
  4. Данные отдельно. Перенос данных идёт отдельным шагом, с проверкой совпадения и возможностью вернуть.
  5. Откат на каждом шаге. Пока старый путь жив, вернуться можно за минуты.

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

Вопросы подрядчику, который предлагает микросервисы

Если вам предлагают микросервисы, попросите ответить письменно. Ответы показывают, подумал ли подрядчик над вашей системой или повторяет популярное решение.

ВопросХороший ответТревожный ответ
Какую проблему это решает у насНазвана конкретная причина: нагрузка, надёжность, скорость изменений или команда, с замеромОбщие слова про современность и масштабируемость
Что остаётся в монолитеЕсть список того, что не выделяем, и причиныВыделяется всё сразу
Как устроены данныеУ каждого сервиса своя схема, описано, как собираются сводные отчётыБаза общая «пока», срок раздела не назван
Кто будет это эксплуатироватьНазваны люди, мониторинг и порядок разбора сбоев после сдачиЭксплуатация не обсуждалась
Как откатимся, если не получитсяСтарый путь живёт параллельно, откат на каждом шагеПереход одним релизом
Сколько это стоит в годНазваны расходы на серверы, мониторинг и сопровождение, без выдуманных суммОценена только разработка

Общий список вопросов подрядчику собран в статье «Как выбрать подрядчика на разработку ПО». Стоимость владения считается отдельно, см. статью о стоимости владения ИТ-системой.

Что делаем мы и с чего начать

Начинаем с обследования: 200 000 ₽ и 2–3 недели за один контур, 350 000 ₽ и 3–4 недели, если вокруг несколько систем, 500 000 ₽ и 4–6 недель для контура с оборудованием или жёсткими требованиями. На выходе нагрузочная модель, карта границ, рекомендация с причинами (в том числе «оставить монолитом») и вилка бюджета. Стоимость вычитается из первого этапа, если продолжаете с нами, а документом можно пользоваться и без нас.

Дальше, если нужно, пилот одного шага: лёгкий — 150 000–400 000 ₽ за 2–8 недель, тяжёлый — 400 000–1 200 000 ₽ за 2–3 месяца. На любой ступени можно остановиться. Мы работаем над одним контуром, а не строим платформы на сотни сервисов, и не дежурим круглосуточно. Исходники лежат в вашем репозитории с первого коммита. Цены и сроки — на странице «Тарифы», описание услуги — на странице «Микросервисы и высоконагруженные системы».

Частые вопросы

Что лучше: микросервисы или монолит?

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

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

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

Как понять, что пора переходить на микросервисы?

Когда у конкретной части есть сильная причина жить отдельно: другая нагрузка, другие требования к надёжности, другая скорость изменений или собственная команда. Сигнал «код большой» сам по себе причиной не является: сначала наводят порядок внутри монолита.

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

Не сами по себе. Вызов по сети медленнее вызова функции. Выигрыш бывает в том, что нагруженную часть можно масштабировать отдельно, а не копировать всё приложение. Если тормозит один запрос, быстрее и дешевле его исправить.

Нужен ли Kubernetes для микросервисов?

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

Можно ли вернуться с микросервисов к монолиту?

Можно, и такие случаи встречаются, когда сервисов оказалось слишком много. Объединять проще, если данные не разошлись по разным базам. Решение принимается по тем же критериям, что и выделение.

Сколько стоит проектирование архитектуры?

Обследование стоит 200 000 ₽ (один контур), 350 000 ₽ (несколько систем) или 500 000 ₽ (контур с оборудованием). Пилот одного шага — 150 000–400 000 ₽, несколько частей — 400 000–1 200 000 ₽. Вывод в эксплуатацию от 1 500 000 ₽ этапами по 300 000–700 000 ₽.

Вы дежурите круглосуточно?

Нет, круглосуточной дежурной смены у нас нет. Сопровождение помесячное: 20 000, 60 000 или 120 000 ₽ в месяц на 4, 12 или 25 часов, сверх пакета 5 000 ₽ в час. Если ваша система должна быть доступна круглосуточно, это обсуждается до договора.