РозробкаSenior

Моноліт → мікросервіси

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

Патерн
Strangler fig (Фаулер, 2004)
Одиниця роботи
Одне виділення зі шляхом назад на кожному кроці
Найскладніше
Поділ даних

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

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

#КрокЗроблено, колиШлях назад
1Назвати обмеження й виміряти його — «платежі чекають 21 день на чужі релізи»Число, ціль і дату записаноСкасовувати нічого. Якщо числа немає, зупиніться тут: робота — модулі, а не сервіси
2Обрати можливість для виділенняЇї роботу можна описати одним реченням без «і», і її викликає мало модулівОбрати іншу — ще нічого не перенесено
3Підняти мінімум платформи: конвеєр, трасування, логи, алерти, графік чергуваньПорожній сервіс доходить до продакшну через конвеєр і з’являється в трасіДля користувачів нічого не змінилося
4Поставити перед монолітом фасад, що пересилає все без змінУвесь трафік іде через нього, а помилки й затримка не змінилисяСпрямувати трафік назад на моноліт
5Провести межу модуля всередині моноліту: один публічний інтерфейсПеревірка в CI падає на будь-якому новому імпорті нутрощів модуляRevert — це досі одна одиниця деплою
6Дати модулю власну схему: жодних JOIN і зовнішніх ключів через межуПеревірка запитів не знаходить SQL, що перетинає межуRevert — це досі одна база
7Пустити кожне звернення через інтерфейс, зокрема звіти й пакетні задачіНіщо поза модулем не торкається його таблицьRevert
8Домогтися, щоб тести модуля запускалися окремоЙого тести проходять без жодного іншого завантаженого кодуСкасовувати нічого
9Збудувати сервіс із власним сховищем; заповнити його й тримати синхронним із журналу бази монолітуЗадача звірки тиждень не показує розходженьВимкнути синхронізацію — копію ще ніхто не читає
10Паралельний запуск: надсилати читання в обидва, віддавати відповідь моноліту, логувати кожне розходженняЖодного розходження за повний бізнес-цикл — тиждень, кінець місяцяЗупинити експеримент
11Перевести читання на сервіс — 1%, 10%, 50%, 100% — під прапорцемПомилки й затримка лишаються на базовому рівні, коли всі читання йдуть на сервісПеремкнути прапорець — дані моноліту досі актуальні
12Перевести запис: сервіс стає джерелом істини й синхронізує зміни назад у старі таблиціКожен запис іде через сервіс, а зворотна синхронізація не показує розходженьПеремкнути запис назад — старі таблиці актуальні, тож моноліт може знову взяти все на себе
13Перевести кожного читача, що лишився, — звіти, експорти, інші модулі — на сервіс чи аналітичну копіюЖурнали запитів місяць не показують читань старих таблицьПовернути читача назад
14Зупинити зворотну синхронізацію й видалити старі таблиці — після резервної копіїТаблиць немає, і нічого не зламалосяВідновити з резервної копії — перший крок без швидкого шляху назад
15Видалити код модуля з моноліту й старий маршрут із фасадуКоду немаєRevert і повторний деплой — уже не прапорець
16Виміряти обмеження з кроку 1 відносно базового рівня й вирішити, що даліЧисло опубліковано, і наступне виділення обрано — або міграція зупиняється—
Фасад ставлять першим, і він ніколи не змінює форми — змінюється лише його таблиця маршрутів. За ним функціональність переходить зі старої системи в нові сервіси по одному маршруту за раз. Кожен проміжний стан є робочою системою, придатною до поставки, і кожен крок скасовується поверненням одного маршруту назад.

До першого виділення: причина й кандидат

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

Названа причинаЧи виправдовує це сервіс?Що перевірити спершу
«Код — безлад»Ні: поділ піде по зв’язності, що вже існуєМежі модулів — рядки 1–7 на сторінці про моноліт
«Деплої повільні й страшні»Ні: частота деплою — не властивість архітектуриКонвеєр, менші партії, feature flags — рядки 8–12 там само
«Одній частині треба значно більше CPU чи пам’яті»ІнодіОкремий пул процесів, запущений з тієї самої збірки
«Одній частині потрібна інша мова чи рантайм»Так, якщо виграш виміряний, а не бажанийБібліотека або невеликий допоміжний процес
«Дані карток мають вийти з-під меж аудиту»ТакЧи погодиться аудитор натомість на окремий процес і базу
«Команди володіють чистими модулями й досі блокують одна одну на релізі»Так — саме для цього випадку стиль і існуєРозмір партій і прапорці — щоб переконатися, що очікування не зводиться до великих релізів
«Платформа під нами доживає віку»Це змушує переїхати, але не диктує архітектуруЧи не дешевше перенести все всередині моноліту
«У всіх уже мікросервіси»НіЯке обмеження виміряно

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

КандидатХто викликаєНаскільки заплутані даніЯкщо зламаєтьсяВердикт
СповіщенняБагато модулів, в один бікВласні шаблони й журнал надсиланьПовідомлення приходять пізнішеПершим — класична репетиція
Обробка зображеньЛише завантаженняФайли, а не таблиціЗавантаження повільнішіПершим — часто це ще й причина масштабування
Генерація PDF і звітівКілька екранівЧитає дані інших модулівДокументи приходять пізнішеРано — щойно читає через інтерфейси
ПошукБільшість екранівКопія чужих даних, яку вже живлять зміниРезультати застаріваютьРано — це вже модель для читання
АвтентифікаціяКожен запитТаблиця користувачів — у центріНіхто не може увійтиПізніше — коли платформа себе довела
ПлатежіОформлення замовлення й поверненняГроші зі суворою узгодженістюГроші загублено або списано двічіПізніше — часто заради відповідності вимогам
Замовлення, ядро предметної областіУсеЗ’єднані з усімБізнес зупиняєтьсяОстаннім або ніколи

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

Шість способів переносити код, не зупиняючи систему

Найвідоміший патерн має ботанічну назву. Під час відпустки в дощових лісах Квінсленду 2001 року Мартін Фаулер побачив фікуси-душителі (strangler fig). Фікус починається як насінина в заглибині дерева-господаря, росте вниз до землі й угору до світла і зрештою може стояти сам там, де було дерево. Через пару років він використав це як образ того, як його колеги замінювали застарілі системи, у короткому дописі «Strangler Application». Згодом він перейменував його на «Strangler Fig Application», бо люди запам’ятовували жорстоке слово й забували рослину. У серпні 2024 року він переписав допис, і нова версія називає чотири види діяльності, які Ян Картрайт, Роб Горн і Джеймс Льюїс визначили для будь-якої поступової заміни. Зрозуміти, яких результатів ви хочете. Вирішити, як розбити проблему на менші частини. Успішно поставити ці частини. І змінити організацію, щоб так могло тривати й далі.

Той самий допис наводить аргумент, якому команди часто опираються. Поступова заміна потребує перехідної архітектури: фасадів, задач синхронізації й перемикачів, що існують лише поки старе й нове працюють поруч, а потім їх видаляють. Це схоже на марнування. Відповідь Фаулера: менший ризик і раніша цінність переважують цю ціну. Кроки 4, 9 і 12 — саме такий код. Закладайте його в бюджет, а не обурюйтеся ним.

Фасад ставлять першим, ще до будь-якого виділення (крок 4). Із фасадом на місці перше виділення є зміною маршрутизації, і його відкат — теж. Без нього перше виділення — це зміна в кожному викликачі, а відкат — ще одна зміна в кожному викликачі. Книжка Сема Ньюмана «Monolith to Microservices» (2019) збирає strangler fig і ще п’ять споріднених патернів для перенесення коду, і кожен пасує до своєї ситуації:

ПатернЯк працюєКоли застосовуватиЧого коштує
Strangler fig applicationФасад надсилає кожен маршрут або в моноліт, або в новий сервісДо можливості звертаються ззовні — URL, черга, виклик APIПроксі, який треба експлуатувати; він бачить лише те, що перетинає край
Композиція інтерфейсуЦілі сторінки чи частини сторінки віддає новий сервісМожливість має власні екраниДва фронтенд-стеки, поки це триває
Branch by abstractionУсередині моноліту інтерфейс дозволяє старій і новій реалізації співіснувати за перемикачемМожливість викликають із глибини кодуШар абстракції, який треба додати, а потім прибрати
Паралельний запускОбидві реалізації обробляють той самий запит; віддають стару відповідь, а дві порівнюютьКоректність важлива, а описати її важкоПодвійна робота, а побічні ефекти треба тримати поза новим шляхом
Decorating collaboratorПроксі передає запит монолітові без змін, а потім за відповіддю запускає нову поведінку в сервісіНову поведінку додають, не торкаючись моноліту, — бали за кожне замовленняПрацює лише тоді, коли відповідь несе достатньо даних
Change data captureНовий сервіс реагує на зміни, прочитані з журналу бази монолітуУ моноліті нічого не можна ні змінити, ні викликатиПрив’язує сервіс до схеми моноліту

Branch by abstraction: шов усередині коду

Фасад бачить лише те, що перетинає край системи. Багато з того, що ви хочете виділити, викликають зсередини — функцію ціноутворення, якою користуються оформлення замовлення, рахунки й адмінка. Для цього випадку є branch by abstraction. Назву техніці дав Пол Геммант, коли обстоював розробку в trunk, а саму ідею він приписує Стейсі Керлу; кроки Фаулер описав у січні 2014 року. Поставити інтерфейс перед старим кодом. Перевести на інтерфейс кожного викликача, по одному. Збудувати нову реалізацію за тим самим інтерфейсом — тут це клієнт, що викликає новий сервіс. Перемикати викликачів прапорцем. Коли старою реалізацією вже ніхто не користується, видалити її, а згодом і інтерфейс, якщо він більше не виправдовує себе. Кожен коміт на цьому шляху лишає систему такою, що збирається й готова до релізу, тож жодну довгоживучу гілку зливати не доводиться.

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

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

Переписаний код вартий рівно стільки, наскільки точно він повторює стару поведінку, а її рідко хтось записував. Паралельний запуск (крок 10) вимірює її замість того, щоб вгадувати. Кожен запит або вибірка запитів іде в обидві реалізації. Користувач отримує стару відповідь. Нову порівнюють із нею, і кожну різницю записують із достатнім контекстом для налагодження. GitHub збудував для цього бібліотеку Scientist і описав, як із нею замінював код, що зливає . Після чотирьох днів виправлення того, що знайшов експеримент, — зокрема функції, якої в новому рушії злиття не було взагалі, — новий код 24 години обробляв 100% запитів без жодного розходження й жодного повільного випадку. До вимкнення старого шляху перевірили десятки мільйонів злиттів. Khan Academy робив те саме на краю своєї системи й називав це тестуванням пліч-о-пліч: шлюз надсилав більшість трафіку запиту в моноліт, а частину — в нові сервіси, і результати порівнювали, доки не виправили кожну різницю.

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

Поділ даних — сама міграція

Виділити код — тиждень роботи. Виділити дані — це і є міграція. Саме тут команди, що пропустили цей розділ, опиняються з двома сервісами на одній базі — тим результатом, якого вся вправа мала уникнути. Ньюман описує три порядки: спершу розділити базу, спершу розділити код або все одразу. Від «усе одразу» він застерігає. З його досвіду, найпоширеніший — спершу код, бо він раніше дає результат. Його звичний провал на практиці — зупинитися на цьому, коли новий сервіс досі читає таблиці моноліту.

Шістнадцять кроків оминають обидві пастки, переносячи по одній речі за раз. Поділ починається всередині моноліту, де він дешевий (кроки 6–7). Розділіть таблиці модуля логічно: та сама база, власна схема, жодних JOIN і зовнішніх ключів через межу. Уже це виявляє кожну приховану залежність, бо кожен перетин тепер валить перевірку, і кожен виправляють, поки стара система працює. Потім дані копіюють і тримають синхронними (9), переводять читання (10–11), переводять запис (12), переводять останніх читачів (13) — і лише тоді щось видаляють (14).

GitHub зробив саме це у великому масштабі між 2019 і 2021 роками — на кластері бази, де жили користувачі, репозиторії, issues і pull request. Спершу з’явилися віртуальні розділи. Таблиці, що належать разом, згрупували в домени схеми, перелічені в простому YAML-файлі в застосунку на Rails. Групи забезпечували два лінтери: один валив будь-який запит, що звертався до таблиць двох доменів, інший — будь-яку транзакцію, що їх охоплювала. Наявні порушення позначили як винятки, і це перетворило їх на видимий беклог. Домен, у якому не лишилося винятків, був готовий до переїзду. Фізичне перенесення робили через Vitess або через власне перемикання на реплікації MySQL, що переводило запис за кілька десятків мілісекунд у найтихішу годину доби; одне таке перемикання перенесло 130 найнавантаженіших таблиць одразу. 2019 року кластер відповідав на 950 000 запитів на секунду. 2021 року ті самі таблиці, розподілені між кількома кластерами, відповідали на 1 200 000 — а середнє навантаження на кожен хост зменшилося вдвічі.

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

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

ПатернЩо робитьКоли пасуєПідступ
Подання бази (database view)Новий сервіс читає таблиці моноліту через подання, що ховає їхню структуруПотреба лише читати, на ранньому етапіДосі прив’язаний до бази моноліту
Сервіс-обгортка для базиТонкий сервіс перед таблицями, які ще ніхто не може розплутатиВикликачі мають перестати лізти в таблиці напрямуБезлад сховано, а не прибрано
Зміна власника данихВласником стає сервіс; моноліт викликає його замість таблицьМожливість переїхала, і її дані мають іти слідом (крок 12)Кожен запит моноліту до цих таблиць треба переписати
Синхронізація даних у застосункуЗастосунок пише в обидва сховища, поки триває переїздНемає журналу бази, з якого можна синхронізуватисяДва записи — не транзакція: звіряти або розходитися
Tracer writeНове сховище стає джерелом істини для одного зрізу даних за разДаних забагато або вони надто навантажені, щоб перенести за один крокДва джерела істини, доки не переїде останній зріз
Поділ таблиціТаблиця, чиї стовпці належать двом можливостям, стає двома таблицямиОдна таблиця «customers» тримає і дані акаунта, і дані доставкиЗмінюється кожен запит, що читав обидві половини
Перенести зв’язок зовнішнього ключа в кодJOIN стає зверненням до іншого сервісу; цілісність перевіряють у кодіКожен ключ через межу, знайдений на кроці 6Повільніші читання й «сироти», яким база більше не запобігає

Саме на звітах зазвичай з’ясовується, що поділ даних не завершено (крок 13). Звіт, що поєднує таблиці шести модулів, роками працював, бо все лежало в одній базі. Після поділу чесних відповідей дві. Або звіт викликає сервіси, що володіють даними, або читає аналітичну копію, яку живлять події чи журнали бази. Ньюман радить окрему схему, збудовану для звітності, а не запити до робочих сховищ. Хай там як, знайдіть кожного читача до кроку 14. Той один щомісячний експорт, про який ніхто не пам’ятав, — класична причина відкату першого числа.

Модель і команди: прошарок проти псування й закон Конвея

Новому сервісу знадобляться дані старої системи, а модель старої системи зазвичай сформована п’ятнадцятьма роками випадковостей. Якщо новий сервіс говорить цією моделлю, він успадковує її назавжди, і виділяти його майже не було сенсу. Ерік Еванс назвав захист від цього в книжці «Domain-Driven Design» 2003 року: прошарок проти псування (anti-corruption layer). Це межа перекладу — тонкий шматок коду, що з одного боку говорить старою моделлю, а з іншого — моделлю нового сервісу, і належить новому сервісу. Він навмисно потворний, навмисно ізольований і навмисно тимчасовий. Коли старе джерело нарешті зникне, ви видалите один компонент, а не виколупуватимете модель із кожного файлу. Ось що він зазвичай перекладає:

У старій моделіЩо робить прошарокУ новому сервісі
Стовпець status з одинадцятьма значеннями, частину з яких ставить лише одна пакетна задачаЗіставляє кожен код з іменованим станом і відхиляє коди, яких не розумієЯвні стани з дозволеними переходами між ними
Запис клієнта, що водночас є адресою доставкиРозділяє його на два поняттяКлієнт і адреси, на які він замовляє доставку
Час, збережений рядками в місцевому часіРозбирає їх з урахуванням історичного часового поясу сервераМітки часу в UTC
Суми, збережені як числа з рухомою комоюОкруглює один раз, явно, і логує, що саме округлилоЦілі копійки плюс код валюти

Мелвін Конвей 1968 року помітив, що структура системи зрештою віддзеркалює структуру комунікації організації, яка її будувала. У міграції це не курйоз, а найсильніша сила в кімнаті. Якщо виділеним сервісом спільно володіють чотири команди, у ньому виростуть чотири внутрішні фракції й координаційна нарада, і він не деплоїтиметься незалежно, хоч би що казала схема. Застосована свідомо, та сама сила стає інструментом. Джонні Лерой і Метт Сімонс 2010 року назвали це зворотним маневром Конвея (inverse Conway maneuver): змініть межі команд на ті, які ви хочете бачити в сервісів, і архітектура піде слідом.

Він також задає чесну верхню межу міграції. Не можна отримати більше сервісів, ніж є команд, щоб ними володіти, а сервіс, яким не володіє ніхто, гниє в розподіленій системі швидше, ніж будь-коли гнив у моноліті. Тому крок 3 свідомо містить графік чергувань. Команда-власник експлуатує новий сервіс із дня, коли він отримав перший запит, — правило Amazon, словами Вернера Фогельса: «You build it, you run it».

Як вести міграцію: потужність, темп і коли зупинитися

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

Не давайте старому коду рости, поки ви його переносите. Правило Khan Academy під час переписування: кожну нову фічу в старому коді на Python мала особисто погодити людина, що очолювала інженерію, бо кожен новий рядок — це ще один рядок для перенесення. Той самий проєкт міряв прогрес тим, що переїхало, а не тим, що збудовано: часткою трафіку, яку обслуговує новий бекенд, кількістю готових полів та обсягом, що зростав, коли роботу краще розуміли. Таблиця перелічує заміри, що відрізняють робочу міграцію від тієї, що застрягла.

ЗамірЗдорова ознакаТривожна ознака
Обмеження з кроку 1Зрушує за кілька тижнів після кожного виділенняНе змінилося після третього виділення
Час виконання в зоні виділенняКоротший, ніж ранішеДовший — зміни тепер чекають на два конвеєри
Фічі, що зачіпають моноліт і сервіс одночасноРідкісні й стають ще рідшимиБільшість фіч
Частка трафіку на новому шляхуЗростає крок за крокомМісяцями стоїть на тій самій частці
Сервісів на команду-власникаКількаСервісів більше, ніж людей
Інциденти, що перетинають межуМеншаютьБільшають із кожним виділенням

Ніщо в цьому патерні не вимагає, щоб моноліт зник, а перетворення його зникнення на мету — це те, як міграція переживає власне обґрунтування. Кожне виділення обґрунтовувалося обмеженням. Коли решта частин нічого не блокує, за рештою виділень немає жодного аргументу, крім симетрії. Постійний гібрид — кілька сервісів навколо добре модуляризованого ядра, у якому досі лежить більша частина предметної області, — поширений і цілком гідний кінцевий стан. У більшості організацій це ще й точка найкращої віддачі: частини зі справжньою потребою в незалежності її мають, а решта не платить за мережу, яка їй ніколи не була потрібна. Інженери SoundCloud 2015 року сказали те саме: дещо з того, що робив моноліт, було таким малим і стабільним, що його, можливо, дешевше лишити там назавжди.

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

П’ять кейсів і те, чого в кожному бракувало

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

КейсЩо сталосяЩо це показує
Amazon, 2001–2006Моноліт Obidos більше не масштабувався; спільні бази не лишили ні ізоляції, ні чіткого власникаПравило, що з цього вийшло, стосувалося даних і власності, а не розміру
Netflix, 2008–2016Пошкодження бази на три дні зупинило відправлення DVD; через сім років вимкнули останні частини дата-центруБілінг і дані клієнтів переїхали останніми, і самі вони забрали роки (кроки 12–14)
SoundCloud, 2012–2015Карта потоку цінності показала, що фічі йшли два місяці, переважно в очікуванні; нові фічі пішли в сервіси, а старі лишилися повільнимиСпершу виміряти обмеження (крок 1) — і те, що нові сервіси самі старого коду не лагодять
GitHub, 2015–2021Переписаний код доводили паралельними запусками; головну базу розділили доменами схеми й лінтерами, а потім перенеслиЛогічний поділ до фізичного (кроки 6, 10)
Khan Academy, 2019–2021Моноліт на Python 2 перенесли на 26 сервісів на Go; до 30 серпня 2021 року 95% трафіку йшло на новий бекендВимушений переїзд, зроблений поступово, з тестуванням пліч-о-пліч і забороною на зростання старого коду

Історія Amazon — та, з якої взявся сам словник. В інтерв’ю 2006 року Вернер Фогельс описав Obidos — застосунок, що від заснування компанії тримав усю бізнес-логіку й логіку відображення Amazon. Роками зусилля з масштабування йшли в бази за ним, доки 2001 року не перестав масштабуватися сам фронтенд. Глибша проблема була в спільності. Бази ділили багато команд і процесів, тож ніщо не мало ізоляції й ніщо не мало чіткого власника, і застрягли і фронтенд, і бекенд. Відповіддю стала сервісно-орієнтована архітектура з одним правилом над усіма іншими: дані інкапсульовано разом із логікою, що з ними працює, доступ до них — лише через опублікований інтерфейс сервісу, без прямого доступу до бази ззовні. Вона розрослася до сотень сервісів. Перечитайте це правило й зверніть увагу, про що воно мовчить. Воно нічого не каже про те, яким малим має бути сервіс. Обмеженням була власність — і виправленням теж.

Netflix почав у серпні 2008 року, коли серйозне пошкодження бази на три дні позбавило його змоги відправляти DVD. Урок, який він виніс, — піти від вертикально масштабованих єдиних точок відмови, як-от однієї реляційної бази у власному дата-центрі. Він вирішив не переносити наявні системи в хмару без змін, бо так переїхали б і всі старі проблеми. Натомість перебудував: від монолітного застосунку до сотень мікросервісів, з денормалізованою моделлю даних на -сховищах. Усі системи, що працюють із клієнтами, переїхали до 2015 року. Білінг і дані клієнтів забрали час після цього, а останні частини дата-центру вимкнули на початку січня 2016 року — через сім років після старту. Найдовше тривали не сервіси. Найдовше тривали дані, до яких прив’язані найсуворіші правила.

Де це вироджується

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

  • Повне переписування поруч із живою системою: увесь ризик приходить в один день, а стара система тим часом рухається далі.
  • Немає названого обмеження, тож успіх рахують створеними сервісами (крок 1).
  • Код виділено, а базу лишено спільною — розподілений моноліт із додатковими кроками (кроки 9–14 пропущено).
  • Почати з найскладнішої центральної можливості — команда вчиться механіки там, де помилки коштують найдорожче (крок 2).
  • Немає фасаду, тож кожне виділення й кожен відкат — це зміна в кожному викликачі (крок 4).
  • Виділення, що тягнуться місяцями й перестають бути зворотними, бо моноліт під ними їде вперед.
  • Подвійний запис без звірки — сховища тихо розходяться.
  • Паралельний запуск, який ще й виконує побічні ефекти, подвоюючи те, що мав спостерігати (крок 10).
  • Новий сервіс говорить старою моделлю даних — і завозить у себе той безлад, від якого тікав.
  • Старі таблиці видалено до того, як переїхав кожен читач, — і це виявляє щомісячний звіт (кроки 13–14).
  • Нові фічі досі приземляються в старому коді, тож обсяг перенесення росте швидше за саме перенесення.

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

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

Коли це застосовувати

Застосовуйте, коли

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

Уникайте, коли

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

Джерела й метод

На чому стоїть ця стаття

  • 14 джерел
  • Перевірено 13 жовтня 2026 р.

Дослідження з Claude. Пошук і звірку першоджерел зроблено з Claude (Anthropic). Відбір, текст і перевірку фактів — редакція Anatomy of IT Product.

Було корисно?

Поділіться з тим, хто працює над тією ж задачею.