Монолит или микросервисы?
Монолит или микросервисы?
Почему вопрос архитектуры нельзя решить одним универсальным правилом
Когда компания начинает разработку новой системы, вопрос архитектуры часто звучит почти автоматически:
«Делаем монолит или сразу микросервисы?»
Проблема в том, что сама постановка вопроса уже немного неправильная.
Монолит и микросервисы — это не соревнование двух технологий, где одна современная, а другая устаревшая. Это два разных способа распределить ответственность внутри системы. И оба могут быть правильными.
- › Для небольшой или средней системы хороший структурированный монолит нередко оказывается значительно более разумным и экономным решением, чем десятки отдельных распределенных сервисов.
- › Для большой платформы попытка искусственно удержать все бизнес-процессы в рамках одного приложения может, наоборот, превратиться в серьезное инфраструктурное ограничение.
Поэтому архитектуру имеет смысл выбирать не по текущей моде и не по красивой абстрактной схеме на презентации. Необходимо детально оценивать следующие факторы:
- › общий физический размер системы;
- › количество задействованных команд разработки;
- › характер и пики входящей нагрузки;
- › жесткие требования к отказоустойчивости;
- › целевую скорость внесения изменений;
- › конечную стоимость эксплуатации инфраструктуры.
Сначала представим обычную систему
Допустим, компания заказывает разработку собственной информационной системы.
В ней нативно присутствуют пользователи, заявки, документы, статусы, уведомления, аналитические отчёты и интеграции с другими внешними сервисами.
Весь основной технический функционал находится и исполняется внутри одного приложения. Это абсолютно нормальная, проверенная временем рабочая архитектура.
- › Услышав термин «монолит», многие ошибочно представляют исключительно устаревшую, неудобную в поддержке и принципиально не масштабируемую программу.
- › Но монолитная архитектура сама по себе ничего подобного не означает. Правильно спроектированный монолит имеет строгие изолированные модули, понятные границы ответственности, раздельные бизнес-сущности и аккуратную структуру кодовой базы.
Инженерная деградация проекта начинается далеко не тогда, когда ядро приложения классифицируется как монолит:
> Проблема лавинообразно начинается тогда, когда внутри кодовой базы со временем полностью перестают существовать и контролироваться нормальные логические границы.
А что меняется с микросервисами?
При микросервисном подходе систему полностью разбивают на несколько обособленных и самостоятельных сервисов.
Теперь разные части единого некогда ИТ-ландшафта можно развивать, масштабировать и разворачивать на серверах изолированно друг от друга. Звучит гораздо лучше.
Но вместе с независимостью в архитектуре неизбежно появляется кое-что ещё:
> Тяжелая дополнительная обвязка обеспечивающих систем. И далеко не одна.
Микросервисы добавляют не только возможности
Каждый новый изолированный сервис — это отдельный процесс, который необходимо независимо запускать, непрерывно мониторить, обновлять и диагностировать.
- › В ландшафте появляются постоянные сетевые взаимодействия;
- › Один сервис вынужден постоянно вызывать по API другой;
- › Целевой компонент системы может быть временно недоступен;
- › Любой межсервисный запрос может завершиться сетевой ошибкой;
- › Асинхронное сообщение рискует зависнуть или застрять в очереди;
- › Одна простая бизнес-операция затрагивает несколько сервисов сразу.
И внезапно ключевой вопрос архитектуры смещается и звучит совершенно иначе:
— «Как разбить монолитное приложение на несколько изолированных частей?»
Вопрос переходит на следующий уровень:
— «Как обеспечить предсказуемую работу сложной распределённой системы, в которой эти части непрерывно разговаривают друг с другом по сети?»
Это принципиально другая, гораздо более сложная инженерная задача.
Монолит проще — но не всегда
У монолита есть очень практичное преимущество: простота взаимодействия между внутренними компонентами.
Если модуль работы с заявками должен обратиться к модулю пользователей, это происходит в оперативной памяти внутри одного приложения:
- › Не нужно поднимать и сериализовать тяжелый сетевой запрос;
- › Не нужно думать о таймаутах на транспортном уровне;
- › Не нужно решать сложную задачу изоляции сбоев при недоступности второго сервиса;
- › Не нужен отдельный кастомный механизм повторных запросов (retries);
- › Не требуется сквозная распределённая трассировка (tracing) только ради того, чтобы понять, в каком именно месте сети потерялся вызов метода.
- › Для информационной системы, которую полноценно обслуживает всего одна команда и которой пользуются несколько сотен или тысяч сотрудников, это становится колоссальным преимуществом.
- › При таком раскладе разработчики тратят драгоценное рабочее время не на инфраструктурное обслуживание распределенной сложной архитектуры, а на прямое развитие продуктовых функций бизнеса.
Но у монолита тоже есть предел
Представим другую архитектурную ситуацию, характерную для крупной компании.
Несколько независимых продуктовых команд одновременно и параллельно развивают совершенно разные функциональные направления единой платформы:
- › Отдельно развивается биллинг;
- › Отдельно — товарный каталог;
- › Отдельно — складская логистика;
- › Отдельно — сервис авторизации;
- › Отдельно — сквозная аналитика.
При этом изолированные компоненты имеют кардинально разную входящую нагрузку. Например, каталог обязан стабильно обслуживать тысячи запросов в секунду, в то время как внутренний модуль корпоративного документооборота задействуется сотрудниками относительно редко.
> В таком сценарии заставлять всю монолитную систему масштабироваться абсолютно одинаково становится инфраструктурно нерационально.
- › Распределение изолированных компонентов по самостоятельным сервисам начинает приносить реальную пользу бизнесу: каждый элемент можно гибко масштабировать в строгом соответствии с его собственной текущей нагрузкой.
Вот в этой точке микросервисная архитектура полностью перестаёт быть просто абстрактным модным словом и становится полноценным, прагматичным инженерным инструментом.
Самая дорогая часть микросервисов — не серверы
Иногда микросервисы активно продают на презентациях как безальтернативный способ «масштабировать систему».
Но закупка дополнительных вычислительных ресурсов — далеко не единственная и не самая большая цена. Гораздо дороже в конечном счете может оказаться лавинообразно растущая операционная сложность.
- › Если у вас один сервер приложения: Локализовать, воспроизвести и оперативно разобраться с возникшей технической проблемой в кодовой базе относительно просто.
- › Если у вас двадцать сервисов: Развернута асинхронная очередь сообщений, несколько разнородных баз данных, API Gateway, балансировщики нагрузок и десятки скрытых сетевых взаимодействий — банальный поиск первопричины ошибки превращается в отдельную комплексную задачу.
На интерфейсе конечный пользователь видит лишь сухую абстрактную плашку:
При этом дежурный инженер техподдержки обязан по цепочке логов выяснить массу неочевидных вопросов:
- › дошёл ли исходный HTTP-запрос от клиента до AI Gateway;
- › успешно ли Gateway проксировал и передал его сервису A;
- › смог ли сервис A корректно и без таймаутов вызвать B;
- › успел ли сервис B успешно записать событие в брокер;
- › приняла ли асинхронная очередь это сообщение без потерь;
- › вычитал ли и получил ли сервис C данную задачу;
- › была ли целевая база данных доступна в момент транзакции;
- › упала ли операция на внутреннем жестком бизнес-правиле;
- › критическая ошибка произошла сразу или после серии повторных автоматических попыток (retries).
Именно поэтому распределённая микросервисная архитектура требует от команды принципиально другого, зрелого уровня инженерной культуры, мониторинга и сквозной трассировки.
Есть ещё одна важная вещь — данные
Управление базами данных в монолитных и распределенных экосистемах подчиняется разным архитектурным законам.
Несколько изолированных модулей могут нативно работать с одной базой данных. Это максимально удобно на старте, но одновременно создаёт жесткую инфраструктурную связанность (tight coupling).
⚠️ Любое изменение схемы или структуры таблиц одним модулем рискует мгновенно сломать логику сразу нескольких других частей приложения.
Каждый независимый сервис полностью и единолично владеет своими данными. Такой подход радикально уменьшает связанность кода, но порождает серьезную распределенную проблему.
➔ Становится физически невозможно выполнить обычный SQL-оператор JOIN между таблицами двух разных подсистем.
Чтобы связать информацию из разных баз, инженерам приходится с нуля проектировать и настраивать сложную логику регулярного обмена данными между сервисами. В контур добавляются:
- › программные вызовы межсервисных API;
- › асинхронные бизнес-события;
- › распределенные очереди сообщений;
- › фоновые трансляторы и репликаторы;
- › концепция согласованности в конечном счёте (eventual consistency).
- › Микросервисы никогда не убирают и не уменьшают общую инженерную сложность.
- › Они лишь полностью перемещают её изнутри изолированного кода приложения наружу — на уровень сетевого взаимодействия и интеграции между распределенными системами.
Это одна из самых важных вещей, которую руководству компании стоит детально понимать задолго до принятия решения о переходе на такую архитектуру.
Почему «сразу микросервисы» часто оказывается ошибкой
Существует крайне популярная, но опасная логика на начальном этапе планирования ИТ-ландшафта:
«Мы планируем большую и нагруженную систему, поэтому сразу построим её на микросервисах. Так её потом будет намного проще масштабировать».
На реальной практике вся проблема кроется именно в слове «планируем».
На старте разработки продукта инженерам и бизнесу практически никогда не понятно, какие доменные границы системы в реальности окажутся правильными. Сегодня кажется логичным выделить изолированный сервис заказов, а через год выясняется, что половина его внутренней логики намертво связана с клиентами и эквайрингом. Границы сущностей начинают размываться.
- › В результате такой поспешной декомпозиции получается не набор независимых, слабосвязанных модулей, а **тяжелый распределённый монолит**. Только теперь его куски искусственно разнесены по разным физическим серверам.
- › Формально приложения и базы данных разные. Фактически — без постоянного синхронного взаимодействия друг с другом по сети и ожидания ответов они физически не способны работать.
Это один из самых болезненных, дорогих и неприятных вариантов архитектуры в современной веб-разработке.
Распределённый монолит — худшее из двух миров
При таком подходе компания берет на себя абсолютно все накладные расходы микросервисной среды, но лишается её преимуществ.
- › постоянные сетевые вызовы между узлами вместо быстрой связи в памяти;
- › сложные изолированные деплои и версионирование контрактов;
- › необходимость разворачивать тяжелый сквозной мониторинг и трассировку;
- › управление распределенными очередями сообщений и брокерами;
- › содержание избыточной и сложной серверной инфраструктуры;
- › каскадный эффект: аварийный отказ одного компонента мгновенно рушит другой;
- › крайне долгая, запутанная и дорогая диагностика возникающих ошибок.
И самое критичное: при наличии всей этой технической обвязки, реальной доменной независимости, ради которой всё это изначально создавалось, у компонентов практически нет.
Поэтому хороший, правильный микросервис — это не просто случайный кусок программного кода, искусственно вынесенный в отдельный Docker-контейнер. У него обязана присутствовать весомая и реальная причина существовать автономно:
- › изолированный, собственный жизненный цикл;
- › выделенная под этот домен отдельная инженерная команда;
- › специфическая, асимметричная нагрузка на бэкенд;
- › потребность в точечном независимом масштабировании;
- › уникальные, повышенные требования к отказоустойчивости узла;
- › полностью самостоятельная бизнес-ответственность направления.
Если ни одной явной и взвешенной причины из этого списка на текущем этапе проекта нет, выделять данный функционал в отдельный сервис пока категорически не стоит.
Когда монолит выглядит разумнее
На инженерной практике монолитная архитектура идеально подходит под следующий пул условий:
- › информационная система создаётся с полного нуля (Greenfield);
- › штатная команда внутренней разработки относительно небольшая;
- › базовая бизнес-логика и сущности ещё активно и часто меняются;
- › количество активных пользователей не измеряется миллионами;
- › входящая нагрузка на серверы относительно предсказуема;
- › подавляющее большинство функций и таблиц тесно логически связано между собой;
- › нет жесткой технологической необходимости независимо масштабировать отдельные узлы;
- › целевая ИТ-инфраструктура должна оставаться максимально простой и дешевой в поддержке.
Особенно остро это актуально для внутренних корпоративных систем автоматизации.
Например, если крупной компании требуется собственная закрытая система оперативной обработки заявок, специализированное рабочее место диспетчера, личный кабинет сотрудников, цифровые документы и всего несколько внешних интеграций, совершенно необязательно и вредно искусственно начинать проектирование с пятнадцати микросервисов.
Один грамотно спроектированный модульный монолит решит эту бизнес-задачу на порядок быстрее, стабильнее и дешевле.
А когда микросервисы действительно оправданы?
Здесь уже совершенно другая история. Микросервисная архитектура приобретает практический смысл тогда, когда система объективно переросла рамки одного приложения.
- › Разные крупные части платформы развиваются и релизится абсолютно независимо;
- › В проекте задействовано несколько отдельных, изолированных инженерных команд;
- › Функциональные компоненты испытывают существенно разную по характеру нагрузку;
- › Отдельные высоконагруженные части необходимо масштабировать точечно и независимо;
- › К разным узлам предъявляются принципиально несвязанные требования к доступности (SLA);
- › Изолированные компоненты имеют свой собственный, обособленный жизненный цикл;
- › Система одновременно разворачивается и работает в нескольких независимых инфраструктурных контурах;
- › Полный аварийный отказ одного периферийного компонента ни при каких условиях не должен останавливать работу всех остальных.
При оценке целесообразности важен далеко не сам физический размер кодовой базы:
— Намного важнее реальная организационная и техническая сложность системы.
Именно поэтому стабильное работающее приложение на миллион строк кода может долгие годы оставаться вполне нормальным, чистым модульным монолитом. А в другой ситуации — относительно небольшая по коду, но сильно фрагментированная платформа уже на старте требует жесткого сервисного разделения.
Есть и третий вариант
Между крайностями «всё в одном» и «десятки микросервисов» существует вполне здравый и прагматичный промежуточный подход.
Речь идет о концепции модульного монолита. Внешне на уровне инфраструктуры это остается одним единым приложением. Но внутри его кодовая база строго разделена на независимые и хорошо определённые области:
- › Такой прагматичный подход позволяет полностью сохранить дешевизну и простоту эксплуатации единого сервера, но при этом не превращать программный код продукта в хаотичную бесформенную массу.
- › Если через несколько лет активного развития один из этих внутренних модулей действительно перерастет рамки и станет очевидным кандидатом на автономное масштабирование, его можно будет чисто вынести в отдельный микросервис.
То есть ИТ-архитектура предприятия получает возможность плавно и предсказуемо эволюционировать вместе с ростом реальных потребностей самой системы.
Архитектуру нужно выбирать по реальным ограничениям
Перед тем как принимать поспешное решение в плоскости «монолит или микросервисы», гораздо полезнее дать честные ответы на базовые вопросы:
- › Кто будет развивать систему?
Это будет делать одна небольшая команда из пяти человек или над платформой параллельно разворачивается работа двадцати независимых команд? - › Что именно будет масштабироваться?
Вся кодовая база и сервисы нагружаются примерно одинаково или один конкретный компонент забирает на себя 95% всей входящей нагрузки? - › Насколько независимы бизнес-функции?
Можно ли физически и безболезненно отделить один бизнес-процесс от другого или они намертво связаны единой сквозной СУБД-транзакцией? - › Что произойдёт при отказе одного компонента?
Является ли это критичным для всего ландшафта или вполне допустимо, чтобы система продолжала стабильно функционировать частично? - › Насколько сложной может быть эксплуатация?
Есть ли в штате выделенная команда, способная профессионально заниматься распределенным мониторингом, логированием, инфраструктурой и трассировкой сбоев? - › Что произойдёт с продуктом через два года?
Это самый важный вопрос проектирования, поскольку выбранная архитектура обязана учитывать не только текущую конфигурацию, но и разумный сценарий её долгосрочного развития.
Самая дорогая архитектура — не обязательно самая сложная
Иногда компании думают, что действительно серьёзная система обязательно должна выглядеть технически перегруженной.
На архитектурной диаграмме для презентаций всё это выглядит крайне впечатляюще:
- › множество изолированных сервисов;
- › десятки контейнеров;
- › распределенные очереди сообщений;
- › несколько независимых баз данных;
- › отдельные прокси-шлюзы API Gateway;
- › перегруженная схема инфраструктуры.
Но если исходная бизнес-задача спокойно решается одним хорошо спроектированным приложением, вся эта конструкция мгновенно становится чистым дополнительным расходом компании.
Каждый искусственно добавленный элемент влечет за собой долгосрочные обязательства: инфраструктуру необходимо непрерывно обслуживать, возникающие ошибки — долго искать по логам, сервисы — версионировать и обновлять, данные — реплицировать и синхронизировать, а сетевые коммуникации — постоянно мониторить.
- › **Каждый отдельный элемент архитектуры обязан иметь жесткую причину существовать.**
- › Не потому, что «так сейчас принято» в индустрии, не потому, что об этом написано в очередной статье на Хабре, и не потому, что слово «микросервисы» на текущий момент звучит более современно для руководства.
Что мы обычно считаем правильным подходом
При разработке и аудите корпоративных систем мы принципиально никогда не начинаем проектирование с выбора модного архитектурного шаблона.
Сначала мы досконально и пошагово разбираем сам бизнес-процесс:
- › Какие типы и массивы данных физически существуют в компании;
- › Какие именно операции ежедневно выполняются пользователями и кодом;
- › Какие именно системы должны нативно обмениваться информацией;
- › На каких участках критически необходима мгновенная синхронная работа;
- › Где технически и логически допустима асинхронная очередь сообщений;
- › Что обязано продолжать работать при аварийном отказе отдельного компонента;
- › Какие конкретно части системы будут меняться и дорабатываться чаще остальных.
И только после сбора этих метрик определяются жесткие программные границы компонентов:
- › Иногда оптимальным результатом становится классический монолит.
- › Иногда — модульный монолит со штучными внешними сервисами под узкие задачи.
- › Иногда проекту действительно необходима полноценная микросервисная архитектура.
> Архитектура обязана обслуживать систему, а не система — обслуживать архитектуру. И, пожалуй, это главное незыблемое инженерное правило.
Итог
Монолит не является заведомо плохой архитектурой. Он предсказуемо выигрывает простотой взаимодействия компонентов, высокой скоростью разработки и относительной дешевизной эксплуатации.
Микросервисы не являются автоматически хорошей архитектурой. Они дают независимость релизов, гибкое масштабирование и возможность разделить огромную систему между изолированными командами.
- › Однако вместе с независимостью всегда появляется жесткая инфраструктурная цена: постоянная сеть, усложненная инфраструктура, распределенный мониторинг, обеспечение отказоустойчивости, синхронизация и согласованность данных, а также значительно более запутанная диагностика возникающих ошибок.
Поэтому зрелый архитектурный вопрос обязан стоять не в плоскости:
— «Что лучше — монолит или микросервисы?»
А в плоскости:
— «Какую степень технической сложности наша ИТ-служба действительно готова себе позволить?»
- › Если система небольшая или средняя — не нужно искусственно и насильно делать её распределённой на серверах.
- › Если система объективно большая — не нужно пытаться удержать все процессы внутри одного монолитного приложения только лишь ради мнимой простоты.
- › Хорошая архитектура никогда не находится слепо между двумя крайностями индустрии. Она находится строго там, где сложность выбранного технического решения полностью соответствует реальной сложности и масштабу самого бизнеса.
API, Webhook или очередь сообщений?
Когда API есть, а интеграция всё равно не работает
Как соединить производство и корпоративные системы
Все инженерные материалы и экспертные статьи
Полный алфавитный справочник ИТ-терминов
🔒 Данные и доступ к системам заказчика обрабатываются в соответствии с согласованными требованиями к безопасности и конфиденциальности. Условия доступа, хранения и обработки данных определяются архитектурой проекта и договором.
Сайт носит исключительно информационный характер и не является публичной офертой в соответствии со статьёй 437 Гражданского кодекса РФ. Цены, состав и условия предоставления услуг уточняются при проектировании решения и фиксируются в договоре. Условия технического сопровождения и уровень сервиса могут дополнительно определяться отдельным соглашением (SLA).
Автоматизация бизнеса и ИИ-решения под ключ
Самозанятый Скопец Антон Викторович ИНН 741709260870