Skip to content

Architectuur

Systeemoverzicht

De RL Padel Academy app is een mobile-first club-management applicatie. Een React Native/Expo frontend communiceert met een zelfgehoste PocketBase backend via diens REST API. Er is geen aparte API-gateway of middleware-laag — de mobiele client praat rechtstreeks met PocketBase.

De gedeelde pakketten packages/ui en packages/utils worden door de frontend-app gebruikt voor gedeelde componenten en hulpfuncties.

JSON over HTTP UI Utils App PB PocketBase DB

De Expo-app verbindt met EXPO_PUBLIC_PB_URL (standaard http://localhost:8090). In productie is dit een HTTPS-endpoint dat naar de PocketBase-server wijst.


Tech Stack

LaagTechnologieVersieRol
Mobiel frameworkExpoSDK 52Build-toolchain en native runtime
NavigatieExpo Router~4.0Bestandsgebaseerde routing met route-groups
UI-bibliotheekReact Native0.76Kernrenderende primitieven
StylingNativeWind^4.1Tailwind CSS utility-klassen voor React Native
Styling (fallback)React Native StyleSheetingebouwdInline stijlobjecten voor complexe/dynamische stijlen
BackendPocketBase0.22Ingebedde Go-server met SQLite, REST API en admin-UI
Auth-persistentieAsyncStorage1.23.1Slaat PocketBase auth-token op tussen app-sessies
Pakketbeheerpnpm>=8Workspace-bewust monorepo-pakketbeheer
TaalTypeScript^5.3Statische typering in alle pakketten

Frontend Architectuur

Bestandsgebaseerde Routing (Expo Router 4)

Expo Router gebruikt het bestandssysteem onder apps/frontend/app/ als route-definitie. Het project gebruikt twee route-groups:

  • (auth)/ — niet-geauthenticeerde schermen (inloggen)
  • (app)/ — geauthenticeerde schermen (alle functionaliteitsschermen)

Route-groups zijn omhuld door hun eigen _layout.tsx-bestanden. De root-_layout.tsx bevat RootLayoutNav, die AuthContext bewaakt en programmatische redirects uitvoert:

  • Niet-geauthenticeerde gebruiker buiten (auth) → redirect naar /(auth)/
  • Geauthenticeerde gebruiker binnen (auth) → redirect naar /(app)/home

Dit is de enige plek waar routing-guards worden afgedwongen. Elk scherm binnen (app)/ mag aannemen dat er een geldige geauthenticeerde gebruiker is.

AuthContext

context/AuthContext.tsx biedt een React-context met de volgende vorm:

interface AuthContextValue {
user: AppUser | null;
isLoading: boolean;
login(email: string, pass: string): Promise<void>;
logout(): void;
}

AuthProvider omhult de gehele app. Bij het mounten roept het initPocketBase() aan vanuit lib/pocketbase.ts, die een eerder opgeslagen token leest uit AsyncStorage, dit laadt in de PocketBase auth-store en authRefresh() aanroept om het te valideren. Als de validatie mislukt wordt de store gewist. De provider abonneert zich ook op pb.authStore.onChange zodat tokenwijzigingen direct worden weerspiegeld in de React-state.

Aangepaste Tabnavigatie

In plaats van de ingebouwde tabnavigator van Expo Router implementeert de app een eigen horizontaal scrollbare tabbar in app/(app)/_layout.tsx. Het actieve tabblad-set wordt berekend vanuit ROLE_TABS[user.role] — een statisch opzoekobject dat elke rol koppelt aan een geordende lijst van tabsleutels.


Backend Architectuur

PocketBase

PocketBase is een enkele Go-binary die het volgende bundelt:

  • Een SQLite-database voor alle persistente gegevens
  • Een REST API (/api/collections/:name/records) automatisch gegenereerd uit het schema
  • Een realtime-subscriptie-eindpunt voor live-updates
  • Een bestandsopslaglaag die geüploade bestanden serveert
  • Een Admin UI op /_/ voor schemabeheer en recordinspectie

Het schema is gedefinieerd in apps/pocketbase/pb_schema.json. Het setup.sh-script downloadt de binary en past migraties toe; start.sh start de server op poort 8090.

Toegangsregelpatroon

PocketBase-toegangsregels zijn filter-expressies die per verzoek worden geëvalueerd:

PatroonExpressieGebruik
Elke ingelogde gebruiker@request.auth.id != ''Leesbewerkingen op clubcontent
Eigen record@request.auth.id = user.idReserveringen, facturen
Eigenaar-only@request.auth.role = 'eigenaar'Beheermutaties op contentcollecties
Trainer of eigenaar@request.auth.role = 'trainer' || @request.auth.role = 'eigenaar'Trainingenbeheer
Zelf of eigenaar@request.auth.id = trainer.id || @request.auth.role = 'eigenaar'Trainerbeschikbaarheid

Dataflow: App Start naar Scherm met Data

alt [token geldig] [token verlopen of onbereikbaar] mount (AuthProvider) lees pb_auth token token (of null) authRefresh() gebruikersmodel user !== null redirect naar /(app)/home fout authStore.clear() redirect naar /(auth)/ authWithPassword(e-mail, wachtwoord) token + gebruikersmodel sla token op redirect naar /(app)/home getList(...) in useEffect records (JSON) render vanuit lokale state RootLayout AuthProvider AsyncStorage PocketBase API Scherm

Kleurensysteem

Alle schermen delen hetzelfde donkergroene ontwerppalet, gedefinieerd als een COLORS/C-constanteobject:

TokenHexGebruik
bg#0a0c0bPaginaachtergrond
surface#111410Kaartachtergronden
surface2#181c17Geneste surfaces, invoervelden
teal#2e8b76Primaire merkkleur, CTA-knoppen
tealLight#3aad94Gemarkeerde waarden, actief tabbladtekst
gold#c9a84cWaarschuwingsstatus, “bijna vol”-indicators
text#eaebe8Primaire tekst
text2#9a9e98Secundaire/metatekst
text3#5a5e58Subtekst, plaatsaanduidingen

Monorepo Structuur

PadDoel
apps/frontendExpo Router 4 + React Native iOS-app
apps/pocketbasePocketBase 0.22 backend (download via setup.sh)
packages/uiGedeelde RN-componenten: Button, Card, Badge, SectionLabel, StatBox
packages/utilsPure TypeScript-hulpfuncties: format.ts en date.ts
tools/docs-siteAstro 5 + Starlight documentatiesite
docs/Markdown/MDX-documenten (architectuur, bedrijfslogica, datamodellen)

De scheiding stelt de backend en frontend in staat onafhankelijk te evolueren en maakt het eenvoudig om een webdashboard of adminpaneel toe te voegen als nieuwe app.