Dobbelt oppdrag: Produksjonsapp + Arkitekturvalidering Dette prosjektet har en uvanlig begrensning: Jeg definerer samtidig arkitekturstandardene og bygger den første store applikasjonen ved bruk av dem. Denne 'dogfooding'-tilnærmingen sikrer at utviklerverktøyene (generatorer, CLI, komponentbibliotek) blir kamptestet umiddelbart i stedet for å oppdage feil måneder etter at team har tatt dem i bruk.
Applikasjonen administrerer Statsbyggs omfattende eiendomsportefølje, og lar anleggsansvarlige spore vedlikeholdsplaner, beleggdata, energiforbruk og overholdelsesdokumentasjon på tvers av tusenvis av bygninger. Kjerneutfordringen var å bygge en datatung dashbord-applikasjon som kunne skalere for å håndtere komplekse relasjonelle data samtidig som den opprettholdt under-sekund responstider.
Next.js App Router og React Server Components Jeg arkitekterte applikasjonen for å maksimere fordelene med Next.js 15s App Router og React Server Components (RSC). Strategien var å pushe så mye datahenting til serveren som mulig, redusere klient-bundle-størrelse og eliminere lasting-spinnere for første render.
For eksempel bruker bygningsdetaljsidene Server Components til å hente bygningsmetadata, nylige vedlikeholdslogger og energistatistikk parallelt på serversiden. Disse dataene blir deretter strømmet til klienten ved bruk av React Suspense-grenser, slik at sideskallet kan rendres umiddelbart mens dataseksjoner populeres inkrementelt.
Den vanskelige delen var å bestemme grensen mellom Server og Client Components. Komplekse interaktive funksjoner som vedlikeholdsplanleggeren krevde klient-side tilstand, men jeg pakket dem inn i `'use client'`-grenser så dypt i komponenttreet som mulig for å bevare server-rendering-fordelene for det omkringliggende layoutet.
TanStack Query for kompleks dataorkestrering TanStack Query håndterer all klient-side servertilstand. Jeg etablerte mønstre for query key-navngiving (f.eks. `['buildings', 'list', { filters }]`) for å sikre forutsigbar cache-invalidering. Alle API-kall er innkapslet i funksjons-spesifikke custom hooks som `useBuildings()` og `useCreateMaintenanceTask()`.
Det mest komplekse datamønsteret var håndtering av avhengige queries for hierarkiske data. Når en bruker velger en bygning, må appen hente dens etasjer, deretter rom på de etasjene, deretter utstyr i de rommene - hver avhengig av forrige valg. Jeg brukte TanStack Querys `enabled`-alternativ for å orkestrere denne fossefallet effektivt samtidig som jeg forhindret redundante forespørsler.
For mutasjoner (oppretting/oppdatering av poster), implementerte jeg optimistiske oppdateringer med rollback-logikk. Når en anleggsansvarlig oppretter en vedlikeholdsoppgave, oppdateres UI-et umiddelbart mens API-kallet skjer i bakgrunnen. Hvis serveren avviser forespørselen, rulles den optimistiske oppdateringen tilbake og brukeren ser en feilmelding - som gir umiddelbar tilbakemelding samtidig som datakonsistens opprettholdes.
Zustand for kompleks UI-tilstand Mens TanStack Query administrerer servertilstand, håndterer Zustand klient-side UI-tilstand som må deles på tvers av komponenter. Jeg opprettet separate stores for distinkte anliggender: en `useFiltersStore()` for å persistere dashbord-filtervalg, en `useLayoutStore()` for sidebar-kollapstilstand og temapreferanser, og en `useNotificationsStore()` for å administrere toast-meldinger.
Filteren store var spesielt interessant. Den persisterer filtervalg til LocalStorage og synkroniserer dem med URL query-parametere, slik at brukere kan bokmerke filtrerte visninger. Jeg brukte Zustand middleware for å automatisk kode/dekode tilstanden, noe som gjør persistens-logikken usynlig for komponenter som konsumerer storen.
Mikrofrontend-klar arkitektur Et kritisk krav var fremtidssikring for et multi-team-miljø hvor forskjellige applikasjoner ville bli distribuert uavhengig, men fremstå som ett sammenhengende system for sluttbrukere. Jeg implementerte '@statsbygg/layout'-pakken for å løse dette.
Layout-systemet tilbyr et shell med global navigasjon og brødsmuler. Hver applikasjon registrerer sine ruter med layoutet via et enkelt API. Layoutet opprettholder et klient-side register (persistert i LocalStorage) som aggregerer ruter fra alle aktive soner.
For eksempel, når denne Property Management-appen laster, kaller den `registerZone({ name: 'property', routes: [...] })`. Hvis en bruker deretter navigerer til Finance-appen (en separat deployment), registrerer den appen sine egne ruter. Navigasjonslinjen oppdateres dynamisk for å vise begge sonene, noe som skaper en sømløs multi-app-opplevelse.
Den mest utfordrende aspektet var tilstandsdeling mellom soner. Siden de teknisk sett er forskjellige deployments, kan de ikke dele React-kontekst. Jeg løste dette ved bruk av et meldingspaserings-system bygget på `postMessage` for cross-origin-kommunikasjon og BroadcastChannel API for same-origin-scenarier. Dette lar én sone utløse handlinger i en annen (f.eks. 'Oppdater dataene dine, en bygning ble oppdatert') uten tett kobling.
Skjemahåndtering og validering Alle skjemaer bruker React Hook Form med Zod-schemas for validering. Jeg opprettet et mønster for å co-lokalisere schemas med deres skjemaer i `.schema.ts`-filer, noe som gjør valideringslogikk enkel å finne og teste uavhengig.
Komplekse skjemaer som vedlikeholdsoppgave-wizarden bruker React Hook Forms `useFieldArray` for dynamisk feltgenerering. Anleggsansvarlige kan legge til flere oppgaver samtidig, hver med sitt eget sett med felter. Skjematilstanden håndterer vilkårlig nesting samtidig som den opprettholder typesikkerhet gjennom diskriminerte union-typer i Zod.
Jeg implementerte også kryssfelt-validering for forretningsregler. For eksempel må en vedlikeholdsoppgaves planlagte dato være etter i dag, men før bygningens neste inspeksjonsdato. Zods `.refine()`-metode håndterer dette elegant, og gir klare feilmeldinger når begrensninger blir brutt.