Otázka „mikroslužby alebo monolit?" padne skoro na každom kick-offe väčšieho projektu. Zvyčajne ju položí niekto, kto minulý týždeň čítal článok o tom, ako veľká technologická firma rozdelila svoj systém na dvesto služieb a zrýchlila vývoj. Problém je, že tá firma mala v čase rozdelenia niekoľko stoviek vývojárov, vlastný platformový tím a systém, ktorý sa už roky nedal nasadiť bez nočnej odstávky. Vaša firma má pravdepodobne päť až pätnásť ľudí a systém, ktorý ešte len vzniká.
Architektúra nie je súťaž v modernosti. Je to rozhodnutie o tom, kde budete platiť zložitosť — či vnútri jedného procesu, alebo medzi desiatkami procesov po sieti. Obidve možnosti majú svoju cenu a tá druhá je oveľa vyššia, než sa na prezentáciách priznáva. Tento článok je pokusom o triezve porovnanie, nie o obhajobu jednej strany.
Čo monolit a mikroslužby v skutočnosti znamenajú
Monolit je aplikácia, ktorá sa nasadzuje ako jeden celok. Jeden repozitár, jeden build, jedna databáza, jeden proces (prípadne niekoľko identických inštancií za load balancerom). Vnútri môže byť pokojne rozdelená na moduly, vrstvy a domény — z pohľadu nasadenia je to však jedna jednotka.
Mikroslužby sú opakom: systém rozdelený na samostatne nasaditeľné služby, z ktorých každá vlastní svoje dáta a komunikuje s ostatnými cez sieť — HTTP, gRPC alebo správy vo fronte. Kľúčové slovo je samostatne nasaditeľné. Ak musíte päť služieb nasadzovať naraz, pretože si navzájom menia rozhrania, nemáte mikroslužby. Máte distribuovaný monolit — teda najhoršiu možnú kombináciu: zložitosť siete bez výhody nezávislosti.
Toto rozlíšenie je dôležitejšie, než sa zdá. Väčšina systémov, ktoré sa označujú za mikroslužbové, sú v skutočnosti distribuované monolity. Vznikli tak, že niekto rozdelil kód podľa technických vrstiev alebo podľa databázových tabuliek namiesto podľa obchodných domén.
Ak sa dve služby musia nasadzovať spolu, nie sú to dve služby. Je to jedna služba s pomalým a nespoľahlivým volaním funkcie uprostred.
Prečo je monolit rozumné východisko
Pre väčšinu firemných systémov — interný systém, portál, e-shop na mieru, aplikácia pre správu agendy — je monolit správna voľba a zostáva ňou podstatne dlhšie, než sa bežne pripúšťa.
Dôvod je prozaický: na začiatku projektu neviete, kde majú hranice domén viesť. Zistíte to až po pár mesiacoch prevádzky, keď uvidíte, ktoré časti sa menia spolu a ktoré samostatne. V monolite je presunutie hranice otázkou refaktoringu — presuniete triedy, upravíte volania, spustíte testy. Keď je tá istá hranica zabetónovaná do sieťového rozhrania medzi dvoma nasadenými službami, jej posun znamená koordinovanú zmenu, migráciu dát a verzovanie API.
Monolit navyše dáva veci, ktoré v distribuovanom prostredí stoja veľa práce: transakcie v jednej databáze, ladenie s jedným stack trace, refaktoring naprieč celou doménou jedným príkazom v IDE, lokálne spustenie celého systému na notebooku. To nie sú maličkosti — to je väčšina každodennej práce vývojárskeho tímu. Ako sa jednotlivé fázy takéhoto projektu reálne vyvíjajú, sme podrobnejšie rozpísali v článku o priebehu vývoja softvéru na mieru.
Skutočná prevádzková cena mikroslužieb
Mikroslužby nezaplatíte v kóde. Zaplatíte ich v prevádzke a v ľuďoch. Tu je prehľad toho, čo sa reálne mení:
| Oblasť | Monolit | Mikroslužby |
|---|---|---|
| Nasadenie | Jedna pipeline, jeden artefakt | Pipeline na službu, verzovanie rozhraní, kompatibilita medzi verziami |
| Ladenie chyby | Jeden stack trace | Distribuované trasovanie naprieč službami, korelačné ID |
| Konzistencia dát | Databázová transakcia | Sagy, kompenzačné operácie, eventual consistency |
| Lokálny vývoj | docker compose up a hotovo | Mock služby alebo zdieľané vývojové prostredie |
| Testovanie | End-to-end testy nad jedným systémom | Kontraktové testy medzi službami + integračné prostredie |
| Potrebné role | Vývojári | Vývojári + niekto, kto vlastní platformu a infraštruktúru |
| Zlyhanie časti | Aplikácia beží alebo nebeží | Čiastočné zlyhania, timeouty, retry, circuit breakery |
Nasadzovanie a verzovanie
Nezávislé nasadzovanie znie ako výhoda, kým nezistíte, že znamená aj nezávislé verzovanie. Ak služba A volá službu B, musíte riešiť, čo sa stane, keď beží nová A proti starej B. To vedie k spätne kompatibilným zmenám rozhraní, k obdobiam, keď existujú dve verzie API naraz, a k disciplíne, ktorú v malom tíme nikto nemá čas držať. Princípy, ktoré tu pomáhajú, sme rozobrali pri API-first architektúre pre rastúce firmy.
Pozorovateľnosť
V monolite viete, prečo požiadavka zlyhala, z logu a stack trace. V distribuovanom systéme jedna používateľská akcia prejde piatimi službami a bez distribuovaného trasovania a korelačných identifikátorov je hľadanie príčiny hádanie. Pozorovateľnosť v mikroslužbách nie je príjemný doplnok — je to podmienka prevádzkyschopnosti. A treba ju postaviť a udržiavať skôr, než ju začnete potrebovať.
Konzistencia dát
Toto je najpodceňovanejšia položka. Keď každá služba vlastní svoje dáta, prestanú existovať transakcie naprieč doménami. „Vytvor objednávku, zníž stav skladu, rezervuj platbu" už nie je jedna atomická operácia, ale sekvencia krokov s kompenzáciami pre prípad, že tretí krok zlyhá. Kód, ktorý bol v monolite desať riadkov, sa zmení na stavový automat s obsluhou chýb. Podobné výzvy pri prepájaní systémov popisujeme aj v texte o systémovej integrácii ERP, CRM a e-shopu.
Conwayov zákon: architektúra kopíruje organizáciu
Melvin Conway sformuloval jednoduché pozorovanie: systémy, ktoré organizácia navrhne, kopírujú jej komunikačné štruktúry. V praxi to znamená, že architektúru nemôžete zvoliť nezávisle od toho, ako sú usporiadané tímy.
Mikroslužby fungujú vtedy, keď každú službu vlastní tím, ktorý ju vyvíja aj prevádzkuje, a keď tímy medzi sebou komunikujú cez rozhrania namiesto cez porady. Pri piatich ľuďoch takéto rozdelenie neexistuje — všetci robia na všetkom. Rozdelenie systému na osem služieb v päťčlennom tíme neprinesie nezávislosť. Prinesie osem pipeline, osem sád konfigurácie a jedného človeka, ktorý pri každom vydaní kontroluje, či sedia verzie.
Praktické pravidlo: počet služieb by nemal výrazne prevyšovať počet tímov, ktoré ich dokážu vlastniť. Ak máte jeden tím, majte jednu službu — prípadne dve, ak je pre druhú skutočný dôvod.
Modulárny monolit ako pragmatický stred
Modulárny monolit je odpoveď na väčšinu situácií, v ktorých firma zvažuje mikroslužby. Ide o jednu nasadzovaciu jednotku, ktorá je vnútri rozdelená na moduly podľa obchodných domén — objednávky, sklad, fakturácia, používatelia — s explicitnými hranicami.
V praxi to znamená:
- Modul vlastní svoje tabuľky. Iné moduly do nich nesiahajú priamo, ale cez verejné rozhranie modulu.
- Závislosti idú jedným smerom. Zakážete cyklické závislosti medzi modulmi a vynucujete to nástrojom, nie dohodou na porade.
- Komunikácia medzi modulmi je explicitná. Volanie rozhrania alebo doménová udalosť — nie priame volanie internej triedy.
- Testy sa dajú písať na úrovni modulu. Modul má vlastnú testovaciu sadu, ktorá nepotrebuje zvyšok systému.
Výsledok je systém, ktorý má hranice mikroslužieb, ale zatiaľ nemá sieť medzi nimi. Keď neskôr zistíte, že modul „fakturácia" naozaj potrebuje samostatný cyklus nasadenia, jeho vyčlenenie je práca na týždne, nie prepis systému. A ak to nikdy nezistíte, ušetrili ste roky prevádzkovej réžie.
Modulárny monolit sa dá aj slušne škálovať — horizontálne, replikami za load balancerom. Väčšinu problémov s výkonom firemných aplikácií nespôsobuje architektúra, ale databázové dotazy a chýbajúce indexy; k tomu sme napísali samostatný text o škálovaní webovej aplikácie s rastom firmy.
Konkrétne signály, ktoré ospravedlňujú vyčlenenie služby
Vyčleňujte jednotlivo a z dôvodu, ktorý viete pomenovať. Reálne signály:
- Odlišný profil záťaže. Generovanie PDF alebo spracovanie obrázkov si žiada iné zdroje a iné škálovanie než zvyšok aplikácie.
- Odlišný cyklus nasadenia. Komponent, ktorý sa mení denne, brzdí systém, ktorý sa vydáva raz za mesiac — a naopak.
- Iný vlastník. Časť systému preberá samostatný tím s vlastnou zodpovednosťou za prevádzku.
- Regulačná alebo bezpečnostná izolácia. Spracovanie citlivých údajov, ktoré má byť oddelené aj infraštruktúrne.
- Odlišná technológia. Časť problému sa reálne lepšie rieši v inom jazyku alebo runtime.
Čo signál nie je: „chceme byť škálovateľní", „mikroslužby sú moderné", „chceme na to použiť inú technológiu, lebo nás zaujíma".
Riziká migrácie a ako ich znížiť
Najdrahšia chyba nie je zvoliť monolit. Je ňou rozhodnutie prepísať fungujúci monolit na mikroslužby naraz — teda zastaviť vývoj funkcií na rok, postaviť nový systém a potom prepnúť. Takéto projekty sa spravidla predĺžia, medzitým sa mení pôvodný systém a nový nikdy nedobehne realitu.
Bezpečnejší postup je inkrementálny: najprv vyčistiť hranice modulov vnútri monolitu, potom vybrať jeden modul s najsilnejším dôvodom a vyviesť ho von, prevádzkovať oba systémy súbežne a až po overení presunúť dáta. Podobnú logiku postupných krokov popisujeme aj pri migrácii legacy systému na modernú platformu.
Zhrnutie
Mikroslužby riešia organizačný problém veľkých firiem: príliš veľa ľudí na jednu nasadzovaciu jednotku. Ak tento problém nemáte, riešite nimi niečo, čo vás netrápi, a platíte za to prevádzkovou zložitosťou, ktorú budete cítiť každý deň.
Pre stredne veľký firemný systém a tím do zhruba desiatich ľudí je správna odpoveď takmer vždy modulárny monolit s poctivo dodržanými hranicami domén. Z neho sa dá vyjsť smerom k službám vtedy, keď na to príde konkrétny dôvod — a vtedy to bude riadené vyčlenenie, nie skok do neznáma. Pri návrhu architektúry firemných systémov vo vývoji na mieru preto štandardne začíname otázkou, kde vedú hranice domén, nie otázkou, koľko bude služieb.
Ak zvažujete, či váš systém už dozrel na rozdelenie, alebo naopak plánujete nový a chcete sa vyhnúť predčasnej fragmentácii, ozvite sa nám a prejdeme si vašu situáciu konkrétne.