Коли моноліт стає в’язницею, або Чому міграція на мікросервіси — це не просто модний тренд

💡 Усі статті, обговорення, новини про DevOps — в одному місці. Приєднуйтесь до DevOps спільноти!

Привіт, DOU-ком’юніті. Ми — Сергій Сучок, Java-розробник і техлід у Ciklum, та Сергій Немчинський, засновник ІТ-школи FoxmindEd. За останні роки нам довелося пройти через справжню одіссею — міграцію десятирічного PHP-моноліту на Java-мікросервіси. І якщо ви думаєте, що це просто «розрізати великий застосунок на кілька маленьких», то ця історія для вас.

Коли код стає археологічним артефактом

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

Ми стикалися з методами на 500+ рядків, які виконували бізнес-логіку трьох різних доменів. Legacy-фреймворки, відсутність юніт-тестів, складні залежності між модулями, які навіть неможливо побачити без побудови спеціальних графів. Часто ми мусили просто зупинятись і будувати схему — що звідки викликається, як дані змінюються, і де це ще може вилізти боком.

Найбільшим викликом було те, що будь-яке нове завдання починалось з reverse engineering. Не можна просто «додати фічу», потрібно зрозуміти, як вона вплине на решту — і не лише в UI, а й на базу, кеш, інші частини системи. А якщо ще врахувати, що це все живе в одному великому PHP-файлі з десятками include’ів і без реальної модульності — стає зрозуміло, чому будь-який рефакторинг був майже неможливим.

Мікросервіси — не silver bullet, а інструмент хірурга

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

Ми бачили приклади, коли люди намагалися «розрізати моноліт» за технічним критерієм: окремий мікросервіс для користувачів, для замовлень, для платежів — але без розуміння доменної моделі. Наприклад, коли три сервіси одночасно мають доступ до однієї бази і взаємно оновлюють одні й ті самі таблиці — це вже не мікросервісна архітектура, а shared database monolith.

Ми також бачили мікросервіси, які викликали один одного ланцюжком: сервіс А викликає В, той — С, далі — D. У випадку помилки — ніякої ретрай логіки, відсутній outbox pattern, іноді навіть немає timeouts. Це не мікросервіси, а distributed spaghetti.

І головне — більшість проблем із міграцією виникають не через вибір технологій, а через спробу перенести ментальні моделі з моноліту. Люди шукають, як реалізувати транзакції на кілька баз даних, замість того, щоб подумати про eventual consistency. Вони шукають спосіб поділити Entity, а не домен.

Eventual consistency: коли ідеальність стає ворогом прогресу

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

Ми витратили багато часу на розробку процесів, які враховують eventual consistency: outbox pattern, ретрай логіка, ідемпотентність, компенсаційні транзакції. Наприклад, коли користувач оформлює замовлення — у нас не одна «велика» транзакція, а послідовність подій: замовлення створено → резерв товару → підтвердження платежу → відправка email → оновлення статусу.

Важливим моментом стала правильна організація логування й метрик: у світі eventual consistency ви повинні знати, що сталося з кожним кроком. Ви повинні мати можливість відновити подію, перевірити стан кожного мікросервісу та зрозуміти, що саме зламалось. Без observability — ваша система стає чорною скринькою.

Event-driven архітектура: коли сервіси навчаються розмовляти

Переходити до event-driven моделі означає змінити саму суть взаємодії сервісів. Раніше це були виклики API — синхронні, з жорсткою залежністю. Тепер — це події. У нашому випадку ми обрали Kafka, бо вона дозволяє побудувати scalable і fault-tolerant обмін повідомленнями.

Це дозволяє сервісам існувати незалежно. Якщо сервіс складу лежить, сервіс замовлень все одно працює — просто події потрапляють у чергу. Ми реалізували механізми dead-letter queue, з повторною доставкою подій, алертами, і навіть UI для відстеження затриманих повідомлень.

Це вимагає і іншого підходу до тестування. Ми використовуємо contract-тести (наприклад, Pact) для перевірки сумісності подій. Також впровадили CDC (change data capture), щоб перехід на події не порушував логіку інших систем.

Strangler Fig: як задушити моноліт з любов’ю

Повна міграція за одну ітерацію — це практично завжди провал. Тому ми свідомо обрали Strangler Fig підхід. Він передбачає поетапну міграцію частин системи з мінімальним впливом на існуючий функціонал.

Почали з каталогу товарів — мінімум залежностей. Створили окремий мікросервіс, обгорнули старий API Gateway, налаштували A/B маршрутизацію. Потім перенесли замовлення, далі — платежі, користувачів, email-сервіс. Кожен крок ретельно логувався, покривався моніторингом і мав fallback на старий код.

Важливо: Strangler Fig — це не лише технічний патерн, а й організаційний. Ми поступово готували команди до нових практик: DevOps, CI/CD, observability, документації API через OpenAPI. Це дозволило уникнути хаосу, коли всі «переписують одночасно».

Реальність мікросервісів: складність, яку варто приймати

Розробка мікросервісів — це завжди баланс між гнучкістю і складністю. Ви отримуєте:

  • Більшу автономність команд. Кожна команда відповідає за свій сервіс і не чекає, поки хтось «випустить реліз».
  • Технологічну гнучкість. У нас одні сервіси на Java, інші — на Node.js. Це ок, якщо є чіткі API-контракти.
  • Масштабованість. Ми окремо масштабуємо лише сервіс платежів у Black Friday, а не всю систему.
  • Fault tolerance. Один сервіс падає — інші працюють. Ви просто кешуєте дані або працюєте з degraded mode.

З іншого боку — це:

  • CI/CD пайплайни для кожного сервісу.
  • Мережеві затримки.
  • Розподілені транзакції.
  • Observability стек (Prometheus, Grafana, ELK, Jaeger).
    Dependency hell між API-версіями.

Це інший рівень складності, і він не кожному підходить. Але якщо ваша система росте — інакше не вийде.

Навички майбутнього, які потрібні сьогодні

Мікросервіси — це вже не хайп, це нова норма для складних і швидко зростаючих систем. Але ринок потребує не лише тих, хто знає терміни, а тих, хто розуміє принципи: domain-driven design, eventual consistency, resilient architecture, fault isolation.

Це вимагає зміни мислення: не «як швидше закомітити», а «як зробити так, щоб цей сервіс міг жити самостійно, збої не ламали систему, і його могли супроводжувати інші команди».

Тож, якщо ви тільки плануєте свій перехід на мікросервіси — пам’ятайте: це не про технології. Це про архітектуру, культуру, відповідальність і сталість. І так — це складно. Але це варто того.

Сподобалась стаття? Підписуйтесь на автора, щоб отримувати сповіщення про нові публікації на пошту.

👍ПодобаєтьсяСподобалось8
До обраногоВ обраному5
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

В 2017 році зробив термінову 1-сторінкову пре-МВПшку на кастом фреймворку,
плануючи нормальну вже робити на чомусь більш популярному, типу Люмена чи Ларавела,
але замовник мені так і не дав можливості перейти, вимагаючи термінові доробки до першої версії, так воно і залишилось, монолітне, недокументоване, і без можливості погуглити.

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

А ще через деякий час на МВП-кастом-моноліті появився перший клієнт.
На проекті фактично була вимушена пауза роки на 2-3, потім появився другий клієнт.
Прийшло розуміння, що під кожного клієнта необхідно запускати повністю ізольовані інстанси, з ізольованою БД. Ще й нещодавно це було підтверджено деякими прописаними вимогами.

От думаю: якби ми таки перейшли на мікросервісну архітектуру, все що можна і не можна порізали на мікросервіси, мали б мабуть з десяток їх на одного клієнта, то зараз би я деплой для кожного нового клієнта робив би не менше тижня.
10 клієнтів * 10 мікросервісів = 100 інстансів.

Єдиний, хто був би у виграші — Амазон.

Нагадало знайому, що одночасно працювала за 6-ма ноутами 🤣

Я не прихильник кругом пихати міксросервіси, але єдиний докер спокійно справляється і з 100 мікросервісами (якщо навантаження невелике). Free Tier, скоріш за все, буде замалий для цього. Але інстанс t3.medium/t3.large для популярних стеків й певних обмеженнях може бути й Ок (alpine etc). Тут може проситься k8s — але то вже інша історія.

і одна БД на кожну групу мікросервісів?
чим це відрізнятиметься від моноліту?

В одній БД можна мати дуже багато різних схем — це не проблема, колись на одному проекті архітектура була така, що під кожного клієна створювався повністю його БД й один сервак спокійно тягнув >1000 схем. Від моноліту мікросервіси багато чим відрізняються, але наврядчи основна відмінність у тому, що обовязково запускати на різних інстансах

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

відповідно те ж саме і з БД — спочатку героїчно розділити, щоб потім героїчно об’єднувати.

ну, простіше кажучи, такий підхід в нас з’їв купу грошей і так і не запрацював.

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

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

Просто не треба в крайності вдаватися.

Між монолітами і мікросервісами є така прослойка як модульний моноліт, моноліт з слабозв’язаними, ізольованими, компонентами. Так, що баг в одному компоненті не ламає всю систему, а рефакторинг одного компонента не вимагає переписування половини системи.

Вот таке потім можна легко делегувати і аутсорсити, а зараз ще й вайбкодити. Ніякий теперішній АІ не справиться з великим проектом. А вот з невеличким модулем в великому проекті — справляється на ура.

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

У вас 10 років не було культури чистого коду/архітектури, тепер буде те саме помножене на проблеми з депенденсіc/оркестрацією.
НМД легасі це не причина переходити на мікросервіси, правильний компонентно-апішний моноліт це мега-вєщь для стабільних задач що не потребують скейлінгу та слабе передбачуваної еластичності.

Погоджуюся: якщо 10 років не було дисципліни коду, то «розрізаний хаос» стане distributed хаосом. Модульний моноліт — чудовий вибір для стабільних продуктів із рівномірним темпом змін. У нашому кейсі причиною міграції був не сам факт легасі, а операційні вимоги: різні релізні цикли по доменах, ізоляція інцидентів і масштабування «гарячих» зон. Тому ми пішли не в «моду», а в Strangler-підхід із попереднім наведенням порядку: контракти, observability, CI/CD, ownership. Без цього мікросервіси й справді перетворюються на «distributed big ball of mud».

Повністю підтримую. Зі свого досвіду можу додати що з мікросервісами складність інрфи і взаємодії devops/QA/dev збільшується, що покладає більше навантаження на менеджмент і лідів, але складність коду падає і в команді можна мати більше мідлів і джунів.

Зі свого досвіду можу сказати що складність коду не зменшується, а складність кодової бази, метаархитектури, віртуальних чорних скринь без кордонів, магічних викликів, зростає в рази. І абсолютно точно не можна ніяких джинів і близько допускати до мікросервісів. Тільки для навчального додатку TODO

Класний, чесний фідбек — дякую! Дійсно без чітких кордонів мікросервіси швидко стають набором чорних скриньок. Але рішення — не «заборонити джунів», а дати їм безпечні рамки: шаблони сервісів, контракт-тести, спостережуваність за замовчуванням і код-рев’ю. Інакше ми просто міняємо хаос коду на хаос інфри.

абсолютно точно не можна ніяких джинів і близько допускати до мікросервісів

ШІ взагалі до жодного серйозного software development project не можна допускать, microservice or monolith

Дуже схоже на те коли організаційні проблеми не вирішені, а замість мікросервісів впевнено створюються макросервіси, тобто монолітів тупо стало 3-4 замість одного. Насправді щоб переходити ще команда має бути готова до переходу

Насправді щоб переходити ще команда має бути готова до переходу

Так-так. Це як зі скрамом (та іншими релігіями). «Ви просто не вмієте його готувати.»
Але чомусь прикладами успішного використання ніхто не ділиться. На папері можна все написати.

Так, мікросервіси зменшують локальну складність коду, але збільшують системну складність. Наш рецепт — стандартні «рейки» (шаблони сервісів, контракт-тести, спостережуваність за замовчуванням) + платформна команда. Тоді дійсно можна розширювати команду мідлами/джунами без зростання хаосу.

Тоді дійсно можна розширювати команду мідлами/джунами зі зростанням хаосу.

Виправив.

Привіт вам з 2015.

Так от оце:

Ми витратили багато часу на розробку процесів, які враховують eventual consistency: outbox pattern, ретрай логіка, ідемпотентність, компенсаційні транзакції. Наприклад, коли користувач оформлює замовлення — у нас не одна «велика» транзакція, а послідовність подій: замовлення створено → резерв товару → підтвердження платежу → відправка email → оновлення статусу.
Важливим моментом стала правильна організація логування й метрик: у світі eventual consistency ви повинні знати, що сталося з кожним кроком. Ви повинні мати можливість відновити подію, перевірити стан кожного мікросервісу та зрозуміти, що саме зламалось. Без observability — ваша система стає чорною скринькою.

Тільки при мені похоронило 3 стартапи. Ви самі пишете «витратили багато часу». З мікросервісами, те що займало в моноліті один день, тепер 2 тижні + купа багів + ніхєра не очевидний інтерфейс, дію виконав, а статус хєр пойми який, бо eventual consistancy.

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

Ну це вже класика. Ефект досягається не магію мікросервісів а тупо рефакторингом. В якості коду можна досягнути такого ж ефекту якщо моділити його на модулі/пекеджи/окремі папки/оберіть свій варіант. Зовсім не обовʼязоково для цього вводити HTTP/GRPC/Queue посередині.

А тепер повернемось в 2025. Мікросервіси це last resort. Й взагалі вам простіше буде з архітектурою якщо ви до будь якого рішення будете ставитись саме так. Рішення має бути продиктоване вимогами. Проаналізовані переваги й не доліки (якщо у рішення немає недоліків — звільняйтесь з позиції архітекта, ви ніхєра не шарите в цьому), й проаналізовано чому плюси нам важливі й чому ми можемо жити з недоліками. Інакше це просто hype-driven development, який не приніс успіху жодному продукту, тільки красиві строчки в резюме працівникам.

Мікросервіси — це вже не хайп, це нова норма для складних і швидко зростаючих систем.

Тезіс з 2015 року, лол. Як виявилось тезис, що це не хайп а реальність, виявився хайпом.

ну такє

ACID in full не всюди потрібний
Частини сервісу можуть бути реально loosely coupled, жодного сенсу тримать їх разом
Тощо, тощо

Для всього є місце, головне шоб в черепі пустого місця не було.

That said, намагатися відтворити ACID traits в принципово слабопов’язаному розподіленому і асинхронному середовищі — це, звісно, fucking hell.

Згоден з головним: мікросервіси — не «швидше/дешевше», а «дорожче/керованіше». Без рейок (платформа, контракти, спостережуваність, UX-патерни під eventual consistency) — це шлях у безодню. Ми не пропонуємо їх як дефолт. Модульний моноліт це чудове рішення, а мікросервіс лише там, де справді потрібні незалежні релізи/масштабування/ізоляція збоїв.

Тільки при мені похоронило 3 стартапи.

Співчуваю — ми теж бачили проєкти, які згоріли на хайпі. Мікросервіси мають високу ціну входу: outbox, ідемпотентність, DLQ, версіонування контрактів, CI/CD, моніторинг. Якщо runway короткий і потрібні швидкі півоти — модульний моноліт краще. Тут все так.

Ну це вже класика. Ефект досягається не магію мікросервісів а тупо рефакторингом.

Часто — так. Але в нашому кейсі були ще: різні ритми релізів по доменах, потреба в ізоляції збоїв та відокремленому масштабуванні гарячих зон, Conway-фактор (багато команд, що блокують одна одну), відсутні кордони в 10-річному коді (shared R/W БД).
Ми пішли Strangler-шляхом і «різали» лише там, де вимоги це виправдовували, мінімізуючи кількість сервісів.

Як виявилось тезис, що це не хайп а реальність, виявився хайпом.

Мікросервіси стали нормальною практикою для певного класу проблем, але не дефолтом. Взагалі немає універсальних рішень будь-яких проблем. Тому так, кожне рішення має свої недоліки і просто потрібно зважити все. Поруч з мікросервісами виросла й «норма 2025» — модульні моноліти з чіткими bounded contexts, контрактами та спостережуваністю. Тобто ринок навчився обирати, а не вірити в універсальні істини. Та все одно достатньо велика кількість проєктів зроблена саме на мікросервісній архітектурі — хтось піддався «хайпу», а для когось це був реальний вихід для розвʼязання поставленої задачі.

Саме так, просто архітектура ще тягне за собою залежності на людей. Мікросервіси вимагають більше від лідів/архітекторів/ядра, називайте як зручно. А моноліт вимагає значно менше, зато від всіх девелоперів.
А на рахунок дешевше — поріг входження мікросерсів нижче. В сенсі коли йде умовний сіньор і берете нового то за 3 місяці він вже щось корисне робить, а на моноліті то від 6 місяців до року.

Ми стикалися з методами на 500+ рядків, які виконували бізнес-логіку трьох різних доменів. Legacy-фреймворки, відсутність юніт-тестів, складні залежності між модулями, які навіть неможливо побачити без побудови спеціальних графів.

Тобто у вас був доволі невеличкий проект, з проміжними ознаками застарівання але ви чомусь вирішили зробити стрибок у прірву?
Оце от

Розподілені транзакції.

напишіть максимально великими літерами на футболках. Бо це буде визначати все ваше наступне життя у цій прірві.

Розподілені транзакції.

Аж ПТСР ловлю від цього.

Ми реалізували механізми dead-letter queue, з повторною доставкою подій, алертами, і навіть UI для відстеження затриманих повідомлень.

А отут аж подумав звільнятись, потім загадав що в мене зараз на проекті немає мікросевісів (ну майже), й видохнув.

Тобто у вас був доволі невеличкий проект

Не зовсім. Це 10+ років історії, кілька доменів і команд, спільна БД, транзитивні залежності — тобто не «космос», але й точно не «маленька апка».

напишіть максимально великими літерами на футболках. Бо це буде визначати все ваше наступне життя у цій прірві.

Ми 2PC не робимо принципово: тільки локальна ACID у межах сервісу, міжсервісно — SAGA/події, outbox+ідемпотентність. І так, ми не стрибали в прірву: спочатку Strangler, кордони доменів, контракти й спостережуваність. Якщо готувати правильно, то все чудово працює.

Наче останні років 5 вже знову хайпують модульні моноліти

Так, мікро сервіси вже не модний і не тренд. Але мабуть ще був коли вони тільки починали переписувати.

З того, що я бачу по вакансіям — достатньо багато вимагають досвіду роботи з мікросервісами. Можливо через 5 років буде те саме з модульними монолітами і повернення хайпу з мікросервісами. Ринок достатньо циклічний. В будь-якому випадку, гарного розробника чи архитектора перш за все визначає знання різних підходів, їх плюсів-мінусів, та можливості зробити вибір і взяти на себе відповідальність за цей вибір.

Підписатись на коментарі