коротко

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

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

Что такое монолит

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

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

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

Микросервисы — это когда систему разрезали на много мелких самостоятельных сервисов. Каждый отвечает за свой узкий кусок (сервис заказов, сервис оплат, сервис уведомлений), у каждого, как правило, своя собственная база, свой репозиторий, свой деплой и своя команда-владелец. А поскольку они теперь отдельные программы, общаются они не вызовом функции, а по сети — обычно через REST или через очереди.

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

flowchart TB
  subgraph M["Монолит: одно приложение, одна база"]
    direction LR
    m1["Заказы"] --- m2["Оплата"] --- m3["Склад"]
    m1 --- DB[("Одна БД")]
    m2 --- DB
    m3 --- DB
  end
  subgraph MS["Микросервисы: каждый сам по себе"]
    direction LR
    s1["Заказы"] -->|"по сети"| s2["Оплата"]
    s1 -->|"по сети"| s3["Склад"]
    s1 --- d1[("БД заказов")]
    s2 --- d2[("БД оплат")]
    s3 --- d3[("БД склада")]
  end

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

Плюсы и минусы: честный размен

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

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

Скрытая цена микросервисов — эксплуатация

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

Правило «монолит сначала»

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

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

Что меняется для аналитика

Для вас переход к микросервисам — это не «то же самое, только в облаке». Меняется три вещи в вашей повседневной работе.

Граница сервиса = граница ответственности. Каждый сервис кому-то принадлежит. Когда вы пишете фичу, которая задевает три сервиса, вы на самом деле договариваетесь с тремя владельцами, и ваша задача — чётко сказать, чей это кусок. «Где живёт логика расчёта скидки — в заказах или в оплате?» в монолите вопрос вкуса, в микросервисах вопрос того, кто это будет делать и поддерживать.

Контракты между сервисами становятся вашим продуктом. Раз сервисы общаются по сети, между ними есть контракты API, и кто-то должен их описать и согласовать: какие поля, какие ошибки, что при сбое. В монолите этого слоя почти нет; в микросервисах это центральная часть спеки.

Транзакция становится распределённой. «Создать заказ и списать оплату» в монолите гарантирует одна база: либо оба, либо ни одного. В микросервисах заказы и оплата — это две разные базы, и единой гарантии больше нет. Что делать, если заказ создался, а оплата не прошла? Кто откатывает? Это уже не техническая мелочь, а сценарий, который должны продумать именно вы. Распределённые транзакции — отдельная большая тема, но первый звонок здесь зашит уже в самом выборе архитектуры.

Откуда это взялось

До микросервисов была SOA (service-oriented architecture) — сервис-ориентированный подход 2000-х, который тоже резал систему на сервисы, но обвешивал их тяжёлой шиной (ESB) и церемониями. Сам термин microservices закрепился около 2011–2014 годов; широко его разнесла статья Джеймса Льюиса и Мартина Фаулера (2014), которая описала стиль «мелкие сервисы вокруг бизнес-возможностей, общаются по простым протоколам». Дальше был маятник: индустрия с энтузиазмом разрезала всё подряд на микросервисы, набила шишек на эксплуатации и распределённых данных — и к концу 2010-х качнулась обратно, к «monolith first» и «правильно нарезанному монолиту» (модульный монолит). Поэтому не ведитесь на моду: и монолит, и микросервисы — инструменты, а не уровни зрелости.

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

Чем монолит отличается от микросервисов?

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

Что выбрать — монолит или микросервисы?

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

Что меняется для аналитика при микросервисах?

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