V předchozím článku jsme si řekli něco o softwarových architekturách a trošku nakousli pojem monorepa a polyrepa. Dnes se budu snažit objasnit, co to vlastně monorepa jsou, kdy jejich použití dává smysl, a naopak, kdy se jedná o zbytečnou komplexitu navíc.
Monorepozitář, zkráceně monorepo, je způsob organizace kódu, kdy jsou všechny projekty a knihovny organizace uloženy v jednom repozitáři. Zkuste si to představit jako velkou knihovnu, kde jsou všechny knihy (projekty) pečlivě uspořádány pod jednou střechou, místo toho, aby byly rozptýleny po různých budovách.
Dnes najdeme jejich využití téměř všude. Příkladem může být babel monorepo, react, remix nebo naše vlastní React UI knihovna Utima UI. Můžete si všimnout, že mimo jednotlivé knihovny zde často najdeme i eslint pluginy, nástroje pro bundlery a další.
Výhod monorep je hned několik a každý si na nich postupem času najde své. Mezi hlavní výhody patří:
Jednou z největších výhod monorepa je snadné sdílení kódu mezi projekty. Například vyvíjíte novou funkcionalitu pro jeden projekt a uvědomíte si, že by mohla být užitečná i jinde. V monorepu stačí kód přesunout do sdílené knihovny a okamžitě je dostupný pro všechny projekty.
Monorepo umožňuje provádět změny napříč několika projekty v rámci jednoho commitu. To je obzvláště užitečné při zavádění nové funkcionality, která vyžaduje úpravy napříč různými aplikacemi, nebo při zavádění breaking changes, které ovlivňují více projektů najednou. Zároveň jde o jednu z hlavních výhod, která u velkých, úzce provázaných projektů značně zvyšuje efektivitu vývojářů.
Odpadá nutnost řešit komplexní závislosti mezi různými verzemi vašich interních knihoven. Všechny projekty vždy používají nejnovější verzi sdílených komponent.
Přes všechny své výhody cesta monorepa není vždy procházka růžovou zahradou.
S rostoucím objemem kódu mohou běžné operace jako git clone nebo npm install trvat déle. To může být frustrující, zejména v CI/CD prostředí, kde výrazně narůstá čas trvání jednotlivých pipeline.
Dalším problémem, typicky u TypeScript projektů, jsou delší časy indexace a pomalejší výkon LSP. Je důležité být na tyto aspekty připraven a případně implementovat optimalizace.
Konfigurace CI/CD pro monorepo může být náročnější. Je třeba zajistit správné sestavení a nasazení jednotlivých částí aplikace, které často běží v různých prostředích. Naštěstí existují nástroje, které nám s tímto úkolem pomohou.
Naštěstí existuje řada nástrojů, které s těmito výzvami pomáhají a snaží se je řešit.
Volba monorepa závisí na mnoha faktorech - velikosti týmu, charakteru projektů a vašich dlouhodobých cílech. Pro menší, izolované projekty může být monorepo zbytečně komplexní, ale pro větší, vzájemně propojené projekty může přinést významné výhody v efektivitě a konzistenci.
Z našich zkušeností využíváme monorepo na většině projektů s dlouhodobější spoluprací, kde se jednoduchost sdílení kódu v rámci full stack JS vývoje opravdu vyplácí.
Pokud chcete s monorepy začít, čisté npm workspaces představují skvělý výchozí bod. Pokud byste se nyní pustili do čtení dokumentace Nx, mohlo by vás velké množství nových informací zahltit.
Jedním z prvních problémů, které se monorepa snaží řešit, je propojení závislostí mezi balíčky a jejich jednotná správa. Toho docílíme využitím npm workspaces.
Pokud nepoužíváte npm, workspaces protocol dnes podporují snad všechny moderní package managery – yarn, pnpm.
Vytvořte si následující adresářovou strukturu:
monorepo-root |_ package.json |_ packages |_ shared | |_ package.json | |_ src | |_ index.js | |_ web-app | |_ package.json | |_ src | |_ index.js | |_ api |_ package.json |_ src |_ index.js
Každý balíček v /packages
má svůj vlastní package.json
, což umožňuje nezávislou správu závislostí a skriptů.
V kořenovém package.json
aktivujte workspaces protokol:
{ "name": "my-monorepo", "private": true, "workspaces": [ "packages/*" ] }
Kořenový package.json
může dále obsahovat veškeré sdílené závislosti (často se jedná o většinu devDependencies), které můžete udržovat na jednom místě. Workspaces navíc zajistí, že se nainstalují pouze jednou, což zvyšuje rychlost instalace závislostí a šetří váš disk.
V našem příkladu můžeme vidět, že jsme si vytvořili balíček shared obsahující sdílený kód mezi API i webovou aplikací. Sdílený balíček můžeme použít v aplikacích api a web-app úpravou definice závislostí v jejich package.json
souborech:
{ "dependencies": { "@my-monorepo/shared": "*" } }
Workspaces tvoří základ, na kterém staví pokročilejší nástroje pro správu monorepa jako Nx, Lerna nebo Turborepo. Tyto nástroje rozšiřují funkcionalitu o pokročilé možnosti buildování, testování a publikování, ale všechny staví na konceptu workspaces.
Pochopení a implementace workspaces je tedy klíčovým prvním krokem při práci s monorepy. Ať už se rozhodnete zůstat u čistých workspaces nebo později přejít na sofistikovanější řešení, znalosti získané při jejich používání vám poskytnou solidní základ pro další rozvoj vašeho monorepa.
Monorepo není univerzálním řešením, ale pro správné týmy a projekty může být transformativním nástrojem. Jako u každé technologie je klíčem pečlivé zvážení vašich specifických potřeb a omezení. Se správnými nástroji a postupy může monorepo otevřít nové možnosti pro váš tým a projekty.
V budoucnu se k monorepům ještě vrátíme a podrobněji se zaměříme na jednotlivé nástroje pro jejich správu.
Pamatujte, že v monorepu, stejně jako v softwarovém vývoji obecně, je klíčem k úspěchu dobrá organizace a pravidelná údržba.