Otázka „mikroslužby, nebo monolit?" padne skoro na každém kick-offu většího projektu. Obvykle ji položí někdo, kdo minulý týden četl článek o tom, jak velká technologická firma rozdělila svůj systém na dvě stě služeb a zrychlila vývoj. Problém je, že ta firma měla v době rozdělení několik set vývojářů, vlastní platformový tým a systém, který se už roky nedal nasadit bez noční odstávky. Vaše firma má pravděpodobně pět až patnáct lidí a systém, který teprve vzniká.
Architektura není soutěž v modernosti. Je to rozhodnutí o tom, kde budete platit složitost — jestli uvnitř jednoho procesu, nebo mezi desítkami procesů po síti. Obě možnosti mají svou cenu a ta druhá je mnohem vyšší, než se na prezentacích přiznává. Tento článek je pokusem o střízlivé srovnání, ne o obhajobu jedné strany.
Co monolit a mikroslužby ve skutečnosti znamenají
Monolit je aplikace, která se nasazuje jako jeden celek. Jeden repozitář, jeden build, jedna databáze, jeden proces (případně několik identických instancí za load balancerem). Uvnitř může být klidně rozdělená na moduly, vrstvy a domény — z pohledu nasazení je to však jedna jednotka.
Mikroslužby jsou opakem: systém rozdělený na samostatně nasaditelné služby, z nichž každá vlastní svá data a komunikuje s ostatními po síti — HTTP, gRPC nebo zprávy ve frontě. Klíčové slovo je samostatně nasaditelné. Pokud musíte pět služeb nasazovat najednou, protože si navzájem mění rozhraní, nemáte mikroslužby. Máte distribuovaný monolit — tedy nejhorší možnou kombinaci: složitost sítě bez výhody nezávislosti.
Toto rozlišení je důležitější, než se zdá. Většina systémů, které se označují za mikroslužbové, jsou ve skutečnosti distribuované monolity. Vznikly tak, že někdo rozdělil kód podle technických vrstev nebo podle databázových tabulek místo podle obchodních domén.
Pokud se dvě služby musí nasazovat společně, nejsou to dvě služby. Je to jedna služba s pomalým a nespolehlivým voláním funkce uprostřed.
Proč je monolit rozumné východisko
Pro většinu firemních systémů — interní systém, portál, e-shop na míru, aplikace pro správu agendy — je monolit správná volba a zůstává jí podstatně déle, než se běžně připouští.
Důvod je prozaický: na začátku projektu nevíte, kudy mají hranice domén vést. Zjistíte to až po pár měsících provozu, když uvidíte, které části se mění společně a které samostatně. V monolitu je posunutí hranice otázkou refaktoringu — přesunete třídy, upravíte volání, spustíte testy. Když je tatáž hranice zabetonovaná do síťového rozhraní mezi dvěma nasazenými službami, její posun znamená koordinovanou změnu, migraci dat a verzování API.
Monolit navíc dává věci, které v distribuovaném prostředí stojí hodně práce: transakce v jedné databázi, ladění s jedním stack trace, refaktoring napříč celou doménou jedním příkazem v IDE, lokální spuštění celého systému na notebooku. To nejsou maličkosti — to je většina každodenní práce vývojářského týmu. Jak se jednotlivé fáze takového projektu reálně vyvíjejí, jsme podrobněji rozepsali v článku o průběhu vývoje softwaru na míru.
Skutečná provozní cena mikroslužeb
Mikroslužby nezaplatíte v kódu. Zaplatíte je v provozu a v lidech. Zde je přehled toho, co se reálně mění:
| Oblast | Monolit | Mikroslužby |
|---|---|---|
| Nasazení | Jedna pipeline, jeden artefakt | Pipeline na službu, verzování rozhraní, kompatibilita mezi verzemi |
| Ladění chyby | Jeden stack trace | Distribuované trasování napříč službami, korelační ID |
| Konzistence dat | Databázová transakce | Ságy, kompenzační operace, eventual consistency |
| Lokální vývoj | docker compose up a hotovo | Mock služby nebo sdílené vývojové prostředí |
| Testování | End-to-end testy nad jedním systémem | Kontraktové testy mezi službami plus integrační prostředí |
| Potřebné role | Vývojáři | Vývojáři plus někdo, kdo vlastní platformu a infrastrukturu |
| Selhání části | Aplikace běží, nebo neběží | Částečná selhání, timeouty, retry, circuit breakery |
Nasazování a verzování
Nezávislé nasazování zní jako výhoda, dokud nezjistíte, že znamená i nezávislé verzování. Pokud služba A volá službu B, musíte řešit, co se stane, když běží nová A proti staré B. To vede ke zpětně kompatibilním změnám rozhraní, k obdobím, kdy existují dvě verze API najednou, a k disciplíně, kterou v malém týmu nikdo nemá čas držet. Principy, které tu pomáhají, jsme rozebrali u API-first architektury pro rostoucí firmy.
Pozorovatelnost
V monolitu víte, proč požadavek selhal, z logu a stack trace. V distribuovaném systému projde jedna uživatelská akce pěti službami a bez distribuovaného trasování a korelačních identifikátorů je hledání příčiny hádání. Pozorovatelnost v mikroslužbách není příjemný doplněk — je to podmínka provozuschopnosti. A je třeba ji postavit a udržovat dřív, než ji začnete potřebovat.
Konzistence dat
Toto je nejpodceňovanější položka. Když každá služba vlastní svá data, přestanou existovat transakce napříč doménami. „Vytvoř objednávku, sniž stav skladu, rezervuj platbu" už není jedna atomická operace, ale sekvence kroků s kompenzacemi pro případ, že třetí krok selže. Kód, který byl v monolitu deset řádků, se změní na stavový automat s obsluhou chyb. Podobné výzvy při propojování systémů popisujeme i v textu o systémové integraci ERP, CRM a e-shopu.
Conwayův zákon: architektura kopíruje organizaci
Melvin Conway zformuloval jednoduché pozorování: systémy, které organizace navrhne, kopírují její komunikační struktury. V praxi to znamená, že architekturu nemůžete zvolit nezávisle na tom, jak jsou uspořádané týmy.
Mikroslužby fungují tehdy, když každou službu vlastní tým, který ji vyvíjí i provozuje, a když týmy mezi sebou komunikují přes rozhraní místo přes porady. U pěti lidí takové rozdělení neexistuje — všichni dělají na všem. Rozdělení systému na osm služeb v pětičlenném týmu nepřinese nezávislost. Přinese osm pipeline, osm sad konfigurace a jednoho člověka, který při každém vydání kontroluje, jestli sedí verze.
Praktické pravidlo: počet služeb by neměl výrazně převyšovat počet týmů, které je dokážou vlastnit. Pokud máte jeden tým, mějte jednu službu — případně dvě, pokud je pro druhou skutečný důvod.
Modulární monolit jako pragmatický střed
Modulární monolit je odpovědí na většinu situací, ve kterých firma zvažuje mikroslužby. Jde o jednu nasazovací jednotku, která je uvnitř rozdělená na moduly podle obchodních domén — objednávky, sklad, fakturace, uživatelé — s explicitními hranicemi.
V praxi to znamená:
- Modul vlastní své tabulky. Jiné moduly do nich nesahají přímo, ale přes veřejné rozhraní modulu.
- Závislosti jdou jedním směrem. Zakážete cyklické závislosti mezi moduly a vynucujete to nástrojem, ne dohodou na poradě.
- Komunikace mezi moduly je explicitní. Volání rozhraní nebo doménová událost — ne přímé volání interní třídy.
- Testy se dají psát na úrovni modulu. Modul má vlastní testovací sadu, která nepotřebuje zbytek systému.
Výsledkem je systém, který má hranice mikroslužeb, ale zatím nemá síť mezi nimi. Když později zjistíte, že modul „fakturace" opravdu potřebuje samostatný cyklus nasazení, jeho vyčlenění je práce na týdny, ne přepis systému. A pokud to nikdy nezjistíte, ušetřili jste roky provozní režie.
Modulární monolit se dá také slušně škálovat — horizontálně, replikami za load balancerem. Většinu problémů s výkonem firemních aplikací nezpůsobuje architektura, ale databázové dotazy a chybějící indexy; k tomu jsme napsali samostatný text o škálování webové aplikace s růstem firmy.
Konkrétní signály, které ospravedlňují vyčlenění služby
Vyčleňujte jednotlivě a z důvodu, který umíte pojmenovat. Reálné signály:
- Odlišný profil zátěže. Generování PDF nebo zpracování obrázků si žádá jiné zdroje a jiné škálování než zbytek aplikace.
- Odlišný cyklus nasazení. Komponenta, která se mění denně, brzdí systém vydávaný jednou za měsíc — a naopak.
- Jiný vlastník. Část systému přebírá samostatný tým s vlastní odpovědností za provoz.
- Regulační nebo bezpečnostní izolace. Zpracování citlivých údajů, které má být oddělené i infrastrukturně.
- Odlišná technologie. Část problému se reálně lépe řeší v jiném jazyce nebo runtime.
Co signál není: „chceme být škálovatelní", „mikroslužby jsou moderní", „chceme na to použít jinou technologii, protože nás zajímá".
Rizika migrace a jak je snížit
Nejdražší chyba není zvolit monolit. Je jí rozhodnutí přepsat fungující monolit na mikroslužby najednou — tedy zastavit vývoj funkcí na rok, postavit nový systém a pak přepnout. Takové projekty se zpravidla protáhnou, mezitím se mění původní systém a nový nikdy nedožene realitu.
Bezpečnější postup je inkrementální: nejprve vyčistit hranice modulů uvnitř monolitu, pak vybrat jeden modul s nejsilnějším důvodem a vyvést ho ven, provozovat oba systémy souběžně a až po ověření přesunout data. Podobnou logiku postupných kroků popisujeme i u migrace legacy systému na moderní platformu.
Shrnutí
Mikroslužby řeší organizační problém velkých firem: příliš mnoho lidí na jednu nasazovací jednotku. Pokud tento problém nemáte, řešíte jimi něco, co vás netrápí, a platíte za to provozní složitostí, kterou budete cítit každý den.
Pro středně velký firemní systém a tým do zhruba deseti lidí je správnou odpovědí téměř vždy modulární monolit s poctivě dodrženými hranicemi domén. Z něj se dá vyjít směrem ke službám tehdy, když pro to přijde konkrétní důvod — a tehdy to bude řízené vyčlenění, ne skok do neznáma. Při návrhu architektury firemních systémů ve vývoji na míru proto standardně začínáme otázkou, kudy vedou hranice domén, ne otázkou, kolik bude služeb.
Pokud zvažujete, jestli váš systém už dozrál na rozdělení, nebo naopak plánujete nový a chcete se vyhnout předčasné fragmentaci, ozvěte se nám a projdeme si vaši situaci konkrétně.