Modular Monolith: чому розробники втомилися від мікросервісів

Modular Monolith: чому розробники втомилися від мікросервісів

  • 11 серпня
  • читати 7 хв
Владислав Лещенко
Владислав Лещенко React Developer, Викладач Комп'ютерної школи Hillel.

Ще років сім тому мікросервіси подавались як must-have для будь-якого серйозного проєкту. «Моноліт» звучало майже як образа — застаріло, негнучко, не масштабується. Сьогодні маятник хитнувся назад. Все більше команд відкрито говорять: мікросервіси принесли їм не швидкість, а розподілений хаос. І дедалі частіше в цих розмовах з'являється термін modular monolith — модульний моноліт.

У цій статті розберемось, що це за архітектурний підхід, чому він повертається в моду і чи справді це «краще з двох світів», а не просто новий виток того самого циклу.

ЩО ПІШЛО НЕ ТАК З МІКРОСЕРВІСАМИ

Мікросервіси добре вирішують конкретні проблеми: масштабування окремих частин системи незалежно, ізоляцію збоїв, можливість різним командам працювати автономно. Але на практиці ці переваги отримали далеко не всі, хто на них перейшов.

Типові проблеми, з якими стикаються команди:

  • Розподілена складність замість вирішеної. Замість одного складного коду отримуємо десятки простих сервісів, але дуже складну мережу взаємодій між ними.
  • Затримки та мережеві збої. Виклик функції всередині монолiту — це мілісекунди. Виклик через мережу між сервісами — це вже інша категорія проблем: таймаути, retries, circuit breakers.
  • Складний дебаг. Один запит користувача може проходити через п'ять-сім сервісів. Трейсинг стає окремою дисципліною, а не опцією.
  • Дублювання інфраструктури. Кожен сервіс — це свій CI/CD, свій деплой, часто своя база даних, свій моніторинг. Для команди з 5–10 розробників це може виявитись важчою ношею, ніж сама бізнес-логіка.
  • Передчасна оптимізація. Багато команд впроваджували мікросервіси до того, як продукт взагалі довів свою життєздатність — платячи архітектурну ціну за масштаб, якого ще немає.

Підсумок простий: мікросервіси — не безкоштовні. Вони обмінюють одну складність (у коді) на іншу (в інфраструктурі та комунікації), і цей обмін вигідний далеко не завжди.

ЩО ТАКЕ MODULAR MONOLITH

Modular monolith — це один застосунок, що деплоїться як єдине ціле, але всередині чітко розділений на незалежні модулі з явними межами (bounded contexts) та мінімальними, контрольованими залежностями між ними.

Ключова відмінність від «звичайного» монолiту — не розмір, а дисципліна. У класичному «монолiті-балці» (big ball of mud) модулі непомітно перетворюються на купу взаємних залежностей, де будь-яка зміна в одному місці ламає інше. У модульному монолiті:

  • кожен модуль має чітко визначене публічне API, а решта коду прихована;
  • модулі не звертаються напряму до внутрішніх таблиць одне одного;
  • комунікація між модулями відбувається через явні інтерфейси або події, а не «як вийде»;
  • межі модулів здебільшого відповідають бізнес-доменам, а не технічним шарам.

По суті, це та сама доменна декомпозиція, яку обіцяють мікросервіси, — але без мережі, без розподілених транзакцій і без окремого деплою для кожного шматка логіки.

ЧОМУ ЦЕ КРАЩЕ З ДВОХ СВІТІВ — І ДЕ МЕЖА

Modular monolith намагається зберегти переваги обох підходів:

Від монолiту:

  • проста локальна розробка й дебаг;
  • один деплой, одна база даних (або чітко розділені схеми);
  • відсутність мережевих затримок між модулями;
  • простіший моніторинг і трейсинг.

Від мікросервісів:

  • чіткі межі відповідальності;
  • можливість команді працювати над модулем, не заважаючи іншим;
  • готовність до подальшого виділення модуля в окремий сервіс, якщо це справді знадобиться.

Останній пункт — важливий. Добре спроєктований модульний моноліт не заперечує мікросервіси назавжди, а відкладає це рішення до моменту, коли воно підкріплене реальними даними: конкретний модуль реально потребує незалежного масштабування, або його розробляє окрема команда з власним циклом релізів.

КОЛИ МОДУЛЬНИЙ МОНОЛІТ — НЕ НАЙКРАЩИЙ ВИБІР

Це не універсальне рішення. Modular monolith гірше підходить, якщо:

  • у вас справді різні профілі навантаження на різні частини системи (наприклад, один модуль обробляє мільйони подій на секунду, а інший — кілька запитів на хвилину);
  • команди фізично й організаційно повністю автономні й потребують незалежних релізних циклів;
  • потрібна технологічна гетерогенність — різні модулі мають сенс писати різними мовами чи на різних платформах;
  • система вже настільки велика, що навіть модульний моноліт стає завузьким для одного деплою.

У таких випадках виділені сервіси — виправдана, а не передчасна складність.

ПРАКТИЧНА ПОРАДА ДЛЯ ПЕРЕХОДУ

Якщо команда хоче спробувати modular monolith або навести лад у наявному коді:

  • Почніть із доменних меж, а не з технічних шарів. Групуйте код навколо бізнес-можливостей (замовлення, оплата, користувачі), а не навколо «контролери / сервіси / репозиторії».
  • Заборонте прямі звернення до внутрішніх даних інших модулів — тільки через публічне API або події.
  • Використовуйте статичний аналіз і лінтери архітектури, щоб межі модулів не розмивались з часом.
  • Проєктуйте так, ніби модуль може стати окремим сервісом — це дисциплінує код навіть якщо винесення так ніколи й не відбудеться.

Рекомендуємо курси по темі

ВИСНОВОК

Втома від мікросервісів — це не відмова від хороших інженерних практик, а тверезіший погляд на їхню ціну. Modular monolith пропонує компроміс: дисципліновану декомпозицію без розподіленої складності мережі. Для більшості продуктів — особливо на ранніх і середніх стадіях — це не крок назад, а більш зважений вибір архітектури, яка відповідає реальному масштабу команди й продукту, а не масштабу, про який мріють на конференціях.

Рекомендуємо публікації по темі