{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [".env.local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"]
},
"lint": {},
"dev": {
"cache": false,
"persistent": true
}
}
}M-am lovit de dilema asta acum vreo opt luni, când a trebuit să refacem arhitectura pentru o platformă SaaS destul de mărișoară. Aveam trei aplicații Next.js: un portal pentru clienți, un dashboard de admin și un tool de onboarding. Toate foloseau același design system, aceleași butoane, formulare și layout-uri, adică undeva la 80% componente comune.
La început am vrut să scăpăm ieftin, dar ne-am dat seama rapid că decizia asta te poate costa zeci de ore pierdute în build-uri blocate pe CI. Hai să-ți zic cum am pus în balanță opțiunile și unde am dat cu capul de pragul de sus.
Varianta 1: Workspaces simple (npm/pnpm/yarn)
Am plecat la drum cu simple pnpm workspaces. Am zis că nu ne trebuie rachete și configurări complicate. Structura era clasică: un folder apps cu cele trei proiecte Next.js și un folder packages/ui pentru chestiile comune.
De ce a fost nasol: Zero inteligență la build. Când modificam o singură linie dintr-o componentă de buton din packages/ui, CI-ul (foloseam GitHub Actions) trebuia să reconstruiască toate cele trei aplicații de la zero. Build-urile durau cam 14 minute. În plus, gestionarea task-urilor în paralel era un coșmar de scripturi bash scrise pe genunchi.
Varianta 2: Nx (OZN-ul din industrie)
Am zis ok, hai să trecem pe Nx pentru că toată lumea îl laudă pentru monorepouri mari. Am generat workspace-ul, am importat aplicațiile și am rămas mască de câte fișiere de configurare mi-a trântit în root.
Trade-off-ul sincer: Nx este incredibil de puternic. Are grafic de dependențe genial și știe exact ce s-a modificat. Dar pentru o echipă de 4 oameni, a fost prea mult. Parcă învățam un framework nou doar ca să rulăm niște aplicații de React. Te lovești de generatoare, de structuri rigide și de o barieră de adopție destul de mare pentru colegii mai juniori. Am renunțat după trei zile de teste.
Varianta 3: Turborepo (Calea de mijloc care a câștigat)
Fiind deja în ecosistemul Vercel cu Next.js, Turborepo a venit natural. Configurarea a durat literalmente 15 minute. Am creat un fișier turbo.json în root și i-am spus ce task-uri depind de altele.
Am economisit cam 35% la build time pe CI încă din prima zi, doar din caching local și remote. Dacă modificam ceva doar în aplicația de admin, portalul de clienți își lua build-ul direct din cache (durata: 2 secunde în loc de 4 minute).
Capcana celor 80% componente comune
Aici e de fapt marea problemă, indiferent de tool-ul ales. Dacă share-uiești 80% din componente într-un pachet comun (să-i zicem packages/ui), ai grijă cum le imporți.
Dacă faci export global (un singur index.ts care dă export la tot), Next.js s-ar putea să aibă probleme cu tree-shaking-ul în development și te vei trezi cu timpi de reîncărcare uriași la salvare (Fast Refresh de 5-10 secunde). Soluția a fost să folosim exporturi dedicate pe componente sau să configurăm corect transpilePackages în next.config.js.
Totodată, ai grijă la Tailwind. Dacă aplicațiile au teme diferite, e mai safe să exporți doar componentele brute din pachetul comun și să lași fiecare aplicație Next.js să-și injecteze clasa de temă în root.
Voi cum vă gestionați monorepourile de Next? Ați rămas pe workspaces simple sau ați trecut la tool-uri dedicate?