Arkitektur & frontend
Hvis I søger efter split frontend, eller efter microfrontends hvornår, står I typisk ét sted: appen er blevet stor, merge-køen er lang, og nogen har sagt at I 'skal splitte monolitten'. Nogen opretter tre Next.js-apps og et fælles pakke-monorepo og kalder det færdigt. Nogen skærer marketing-sitet fra appen og opdager at headeren nu er to sandheder. Nogen køber en microfrontend-runtime, fordi det ligner det, de store har. Nogen nægter at røre noget, indtil alt er ét build — også det, der deployer hver uge, og det, der deployer to gange om året.
Designsystem-indlægget handler om tokens og ejerskab af komponenter. Legacy-indlægget handler om hvornår et helt system skal skiftes. Den her handler om frontend-appen som deployable: ét repo, ét build, ét hostname — eller flere. En ærlig split er ikke et diagram med pile. Det er to cadence'er, I kan pege på, eller to blast radii, I ikke længere vil dele. Uden den sætning har I flere mapper. I har ikke flere produkter.
TL;DR
De vigtigste pointer
- En frontend-monolit er ét deployable. Den er ikke et skældsord. De fleste Next.js-sites skal blive der.
- Split når cadence, ejerskab eller blast radius faktisk er to jobs — ikke når mappetætheden føles pinlig.
- Microfrontends er ofte en organisationsløsning, I køber som arkitektur. Hvis I ikke har to teams, har I ikke det problem.
- Et delt designsystem eller en delt pakke kræver ikke to apps. Det kræver ét sted, der ejer tokenet.
- En split uden modulgrænser først er en distribueret monolit: I har stadig ét release, bare med netværk imellem.
- Kald ikke to hostnames for en split, hvis I stadig merger i samme kø og ruller dem ud samme aften.
Hvorfor det her overhovedet er vigtigt
En split I ikke kan forklare, koster uger I kalder 'uafhængighed'. Folk opretter et andet app-directory, fordi merge-køen gør ondt, og opdager at de stadig venter på den fælles layout-pakke, den fælles auth, den fælles feature-flag, og at headeren nu divergerer. I tror I har to produkter. I har to steder, I skal huske at patche. Ritualet er dyrt, fordi det giver jer tilladelse til ikke at rydde op i monolitten, I faktisk har.
Teams arver ordet microfrontends fra en anden slags software. I en bank med tyve frontend-teams og ét legacy-shell er runtime-composition en overlevelse. På et Next.js-site med ét produktteam og et marketing-site er det ofte teater: I betaler for at holde to builds, to preview-miljøer, to sæt environment-variabler og en kontrakt mellem dem, som ingen ejer. Monolitten løste at I kunne se hele siden. Splittet skal løse noget monolitten ikke kan.
Det er også et tillids- og SEO-spørgsmål. Hvis marketing-sitet og appen splitter, og I glemmer redirects, sitemap og den delte footer, har I to sites Google behandler som halve. Hvis I splitter 'for performance' uden at måle hvad der faktisk var tungt, har I flyttet JS fra ét bundle til to downloads plus en orchestration. Hvis ingen ejer snittet, divergerer det — og så er den grønne 'vi er decouplede' en løgn, I skriver i et arkitektur-slide.
Vi skriver det, fordi 'vi skal splitte frontend' er blevet et køb, I tager før I har en sætning om hvilken kø, hvilket team eller hvilken radius I vil skille. Et ærligt setup er kedeligt: ét Next.js-repo så længe cadence er den samme, tydelige mapper når koden er stor, og to apps kun når I kan pege på det job, det andet build skal holde alene. Uden den sætning har I et ekstra package.json. I har ikke en arkitektur.
Hvad en frontend-monolit faktisk er — og hvad et split ikke er
En frontend-monolit er ét deployable I ruller ud som én sandhed: samme build, samme release, samme hostname eller samme app-shell. Den må gerne være stor. Den må gerne have mapper for marketing, app, admin. Den er en monolit, fordi I ikke kan sende marketing live uden også at sende den kode, I ikke rørte. Det er et træk, ikke en diagnose. De fleste sites, vi ser, burde være præcis det.
Det er ikke 'vi har mange filer'. En stor App Router-træ med tydelige routes er stadig ét produkt. Modulgrænser — `app/(marketing)`, `app/(app)`, et `ui`-pakke I ejer — er oprydning. De er ikke et split. Hvis I kan ændre forsiden uden at læse billing-koden, har I allerede det, folk tror de køber med et andet repo. I har bare ikke betalt netværket for det.
Et split er to (eller flere) deployables med hver deres cadence eller hver deres blast radius. Marketing på Vercel, der kan gå live når copy er klar. Appen, der venter på en migrering. En admin, I ikke vil lægge i det offentlige bundles' angrebsflade. Det kræver et snit I kan pege på: et hostname, et auth-cookie-domain, en pakke-kontrakt, en redirect-liste. Uden snittet er det to mapper, I later som om er uafhængige.
Og det er ikke microfrontends som default. Runtime-sammensætning — web components i et shell, module federation, et iframe-hotel — er et værktøj til når to teams skal shippe til samme skærm uden at dele et build. Hvis I er ét team, er det et ekstra runtime I skal debugge klokken tre. Byg grænser i koden først. Split deployet når køen eller radiusen tvinger jer. Ikke omvendt.
Det der typisk går galt, når nogen siger vi har splittet frontend
Den første fejl er at splitte på filer, ikke på jobs. I skærer `app/blog` ud, fordi mappen er stor, og opdager at bloggen stadig deler auth, i18n, preview og den samme release-manager. I har flyttet kode. I har ikke flyttet cadence. Et ærligt split starter med sætningen 'den her del skal kunne gå live om onsdagen, selvom appen er frosset'. Uden den sætning er udtrækket indretning.
Den anden fejl er det delte, I ikke ejede. Header, footer, cookie-banner, feature-flags, feature-gate til chat. I to apps bliver det to sandheder på tre uger, medmindre én pakke har en ejer og en release. Et designsystem-indlæg er det rigtige sted for tokens. Her er pligten enklere: hvis I splitter, så skriv hvad der stadig er ét, og hvem der må ændre det. Ellers får I to forsider, I kalder brand.
Den tredje fejl er SEO og URL-kontrakten. Marketing på `www`, app på `app.`, uden redirects, uden én sitemap-strategi, uden at beslutte hvilket hostname der ejer `/priser` og `/login`. Google ser to sites. Brugeren ser en footer der peger forkert. Relancering-indlægget handler om cutover. Her dør I i det små: I splittede deployet og glemte at I stadig har ét webarbejde over for indekset.
Den fjerde fejl er at købe runtime før I har teams. Module federation, et shell, et iframe rundt om checkout. Det kan være rigtigt, når to organisationer skal mødes på én side. Det er forkert, når fem udviklere vil 'være klar til at skalere'. I får den dyre vane at debugge to builds og et load-order-problem, I ikke havde, da det var ét `next build`. Hold det kedeligt, indtil køen har et navn.
Én app vs. et ærligt split — to deployables, to slags sandhed
Den ærlige frontend-monolit
Ét build, ét release, tydelige mapper. Cadence er den samme. I kan se hele siden.
- Én preview per pull request — review ser marketing og app sammen
- Én header, ét cookie-banner, ét sæt redirects I faktisk ejer
- Fanger de bugs, der kun findes når siderne deler layout og auth
- Er det rigtige default, indtil I kan navngive det job, ét build ikke kan
Det ærlige split
To deployables med hver deres cadence eller radius — og et snit I kan pege på.
- Marketing kan gå live, mens appen er frosset — eller omvendt
- Et ejet fælleslag (pakke eller design-token), ikke copy-paste i to repos
- URL- og hostname-kontrakt: hvad bor hvor, hvad 301'er, hvad er noindex
- Er spild, hvis den eneste smerte var at `app/`-mappen føltes stor
Sådan beslutter I om I overhovedet skal splitte
Uden den her rækkefølge får I et ekstra repo, I kalder uafhængighed.
- 01
Skriv det job, monolitten ikke kan holde — i fem linjer
Ikke 'appen er blevet stor'. 'Marketing skal kunne shippe copy om onsdagen, mens appen er i feature-freeze.' 'Admin må ikke ligge i det offentlige bundle.' 'To teams merger i samme kø, og den ene blocker den anden hver uge.' Hvis I ikke kan sige jobbet, er I ikke klar til et split. I er klar til at rydde op i mapperne, I allerede har.
- 02
Tjek om modulgrænser allerede gør jobbet
Route-grupper, et `ui`-pakke, et forbud mod at importere billing fra bloggen. Det er det, de fleste 'store' Next.js-apps mangler. Hvis smerten er at finde filer, eller at en junior importerer på tværs, så er det lint og mapper. Et andet hostname retter ikke et import-rod. Designsystemet retter tokens. Det retter ikke cadence.
- 03
List det, der stadig skal være ét efter et split
Auth-cookie. Brand-header. Cookie-banner. i18n. Feature-flags. Sitemap og redirects. Hvis listen er lang, og I ikke har en ejer til den, vil splittet gøre listen til to løgne. Skriv ejeren. Skriv releasen af det fælles. Hvis I ikke vil eje det fælles, så bliv i monolitten. Det er ærligere.
- 04
Beslut hostname, URL og blast radius — ikke kun mappen
Samme hostname med rewrite er stadig ét site for Google og for cookien. To hostnames er to sites. Et split 'for sikkerhed' betyder at admin ikke deler bundle med marketing — det er radius. Et split 'for SEO' uden en redirect-liste er et hul. Relancering-indlægget er tjeklisten, hvis I flytter stier. Her skal I bare ikke late som om et nyt `apps/`-directory er en cutover.
- 05
Giv splittet et sluk-kriterium — eller lad være at tænde det
Hvis jobbet var 'to teams, to køer', og I bliver ét team igen, så læg det sammen. Hvis jobbet var 'marketing frosset fra app-release', og I nu deployer begge hver dag, er to builds et abonnement på drift. Et split uden sluk-dato er hvordan I betaler for et shell, efter køen døde. Skriv hvornår I merger det igen.
“I har splittet frontend, når to cadence'er kan gå live uden at vente. Et ekstra repo er et hotel.”
Marketing-site, app og admin — samme ord, tre jobs
Et marketing-site på Next.js plus en app i samme repo er det, vi typisk anbefaler, så længe det er ét team, og copy ikke skal igennem appens freeze. Preview på pull requesten viser hele sandheden. Headeren er én. Cookie-banneret er ét. Når marketing begynder at vente tre uger på en app-release for at rette en forside, har I jobbet. Så er to apps rigtigt — med en ejet pakke til det, der stadig skal ligne, og en redirect-liste så `/` og `/login` ikke bliver et gætteri.
Et WordPress-marketing plus en Next.js-app er allerede et split, I måske ikke har navngivet. To CMS'er, to deploys, to footere. Her er fejlen sjældent at I 'mangler microfrontends'. Her er fejlen at I later som om det er ét brand, uden at eje det fælles, og at I har to sitemaps der kæmper om de samme queries. Enten flyt marketing ind i Next.js, eller skriv kontrakten: hvilke stier WordPress ejer, hvilke appen ejer, og hvad der 301'er. Wordpress-til-Next-indlægget er cutover. Den her er beslutningen om I overhovedet skal lade dem være to.
En admin ved siden af det offentlige site er det split, der oftest er rigtigt af radius, ikke af ego. Admin med interne værktøjer, webhooks, et andet auth-grundlag, hører ikke hjemme i det bundle, I sender til forsiden. Det kan stadig være samme monorepo. Det skal ikke være samme public JavaScript. Hvis I 'splitter' ved at lave et `/admin`-route i samme app uden at skille auth og bundle, har I flyttet URL'en. I har ikke flyttet radiusen.
Microfrontends i ét shell er det sidste job, ikke det første. Når to organisationer skal shippe til samme skærm — en checkout fra ét team, et catalog fra et andet — og I ikke kan dele et build, så er runtime-composition et værktøj. Indtil da er det et framework I debugger i stedet for produktet. Byg monolitten ærligt. Split deployet når køen har et navn. Køb ikke et shell, fordi slideet havde pile.
Hvad I typisk bør gøre i den situation, I står i
Venstre er det, I typisk siger. Højre er det næste skridt, vi typisk anbefaler.
Situation
App-mappen føles stor, og nogen har sagt microfrontends
Næste skridt
Skriv jobbet ét build ikke kan. Hvis I ikke kan, så ryd mapper og imports — ikke hostnames
Situation
Marketing venter på appens freeze for at rette en forside
Næste skridt
Så har I cadence-jobbet. To apps, ejet fælleslag, URL-kontrakt — ikke et iframe-hotel
Situation
Vi har to repos og merger stadig i samme release-uge
Næste skridt
I har ikke splittet. Læg det sammen, eller giv den ene en cadence den anden ikke ejer
Situation
Admin ligger i det offentlige bundle 'så det er nemt'
Næste skridt
Det er radius, ikke bekvemmelighed. Skil auth og det JS, I sender til forsiden
Situation
Vi vil dele header uden at dele et build
Næste skridt
Én pakke med en ejer. To copy-paste-headere er to brands. Runtime-composition er sidste udvej
Tjekliste før I kalder det et frontend-split
Hvis I mangler flere af punkterne, er sætningen uafhængighed — setup'et er et ekstra package.json.
- I kan sige hvilket job det andet deployable holder, som monolitten ikke kan
- Cadence eller blast radius er faktisk to — ikke kun mappetætheden
- Det fælles (header, auth, banner, flags) har en ejer og en release
- Hostname, stier og redirects er skrevet, før I flytter det første route
- To teams kan rulle ud uden at booke samme aften — eller I er ærlige om at I stadig er ét team
- I ved hvornår I lægger det sammen igen, hvis jobbet forsvinder
Spørgsmål vi får igen og igen
Hvornår skal vi splitte en frontend-monolit?
Når cadence, ejerskab eller blast radius faktisk er to jobs: marketing skal kunne gå live, mens appen er frosset; to teams blocker hinanden i samme kø hver uge; admin må ikke dele bundle med forsiden. Hvis jobbet er at mappen føles stor, eller at ordet microfrontends lå i et slide, så ryd op i routes og imports. De fleste Next.js-sites har det sidste job, ikke det første. Skriv sætningen. Hvis I ikke kan, så køb ikke det andet hostname.
Er microfrontends det samme som at splitte frontend?
Nej. Et split er to deployables. Microfrontends er én måde at samle dem på samme skærm i runtime. I kan splitte marketing og app på to hostnames uden nogen federation. I kan også have module federation og stadig være én release, hvis I merger i samme kø. Vi ser oftere det sidste end det første. Køb runtime når to organisationer skal mødes på én side. Indtil da er to kedelige apps — eller én — det ærlige værktøj.
Kan vi have ét monorepo og alligevel kalde det et split?
Ja, hvis der er to builds I kan rulle uafhængigt, og et fælleslag med en ejer. Monorepoet er indretning. Splittet er releasen. Et `apps/web` og `apps/app` der altid deployer samme aften, er en monolit med ekstra tooling. Et monorepo hvor marketing går live om onsdagen og appen venter på en migrering, er et split. Kald det det, det er. Mapper lyver mindre end slides.
Skal marketing-sitet og appen være to hostnames?
Kun hvis I vil have to cadence'er eller to indekseringsflader, og I vil eje kontrakten. Samme hostname med rewrites er stadig ét site for cookies og for Google — ofte det rigtige, når brandet skal føles som ét. To hostnames er rigtigt, når I bevidst skiller app-auth fra det offentlige site, eller når de to ikke må dele et freeze. Uden redirect-liste og uden at beslutte hvem der ejer `/` og `/login`, er to hostnames et hul, ikke en arkitektur.
Hvad gør vi med et split, der stadig venter på den fælles pakke?
Så har I ikke splittet cadence. I har splittet mappen. Enten giv pakken en release, den anden app kan ignorere i en uge, eller læg det sammen igen. En fælles pakke uden semver og uden ejer er den gamle monolit med et `workspace:`-prefix. Det er okay som skridt. Det er ikke okay som den sætning, I siger til ledelsen.
Hvis I har to repos og stadig booker samme release-uge
Lad os skille cadence, radius og mapper ad.
Et navngivet job slår et shell, I opretter fordi monolitten føltes pinlig.
