99 lines
5.8 KiB
XML
99 lines
5.8 KiB
XML
#heading[Architectuur van het project]
|
||
|
||
De architectuur van Serengo is opgebouwd rond SvelteKit als full-stack framework, Drizzle ORM voor de
|
||
PostgreSQL-database en een aantal gespecialiseerde services voor storage, kaarten en notificaties. De frontend en backend leven in dezelfde codebase en delen type-informatie, zodat de volledige stack strongly typed is.
|
||
|
||
== Opbouw van de applicatie
|
||
|
||
Op hoog niveau bestaat de applicatie uit de volgende lagen:
|
||
|
||
- *Presentatielaag (UI)* – Svelte 5-componenten in `src/lib/components` en pagina's in
|
||
`src/routes`. Componenten zijn per domein gegroepeerd (auth, finds, map, media, notifications,
|
||
profile, ...).
|
||
- *Domein- en servicelaag* – Svelte stores en hulpfuncties in `src/lib/stores` en
|
||
`src/lib/utils`, plus server-side services in `src/lib/server` (auth, db, media-processor,
|
||
push, R2, oauth).
|
||
- *API-laag* – SvelteKit-endpoints in `src/routes/api/*` die CRUD- en acties aanbieden voor
|
||
finds, friends, users, notifications, profile pictures, places en media.
|
||
- *Datalaag* – een PostgreSQL-database aangestuurd via Drizzle ORM. De schema-definitie staat in
|
||
`src/lib/server/db/schema.ts`, migraties in de map `drizzle/`.
|
||
|
||
Deze lagen zijn zodanig opgebouwd dat UI-componenten alleen via duidelijke interfaces praten met
|
||
stores en services, en dat alle persistente data via de Drizzle-laag loopt.
|
||
|
||
== Projectstructuur
|
||
|
||
De belangrijkste mappen van het project zijn:
|
||
.
|
||
- `src/lib/components/` – herbruikbare UI-componenten, gegroepeerd per domein:
|
||
- `auth/` – login-form en gerelateerde auth-componenten.
|
||
- `finds/` – componenten rond finds (overzichten, detailweergave, editmodals, comment-UI, ...).
|
||
- `map/` – kaartcomponenten (Map, LocationManager, POI-zoekfunctionaliteit).
|
||
- `media/` – video- en mediacomponenten.
|
||
- `notifications/` – notificatie-UI en beheer.
|
||
- `profile/` – profielpaneel en profielfoto’s.
|
||
- plus een set shadcn-achtige UI-primitieven [@shadcn-svelte] (button, card, dropdown-menu,
|
||
sheet, skeleton, sonner, ...).
|
||
- `src/lib/server/` – server-side logica:
|
||
- `db/` – databaseconfiguratie en Drizzle-schema.
|
||
- `auth.ts` – Lucia-authenticatie. [@lucia-docs]
|
||
- `oauth.ts` – Google OAuth-integratie.
|
||
- `push.ts` – Web Push-notificaties.
|
||
- `r2.ts` – integratie met Cloudflare R2.
|
||
- `media-processor.ts` – beeld- en videobewerking (o.a. WebP/JPEG-pipeline).
|
||
- `src/lib/stores/` – Svelte stores:
|
||
- `api-sync.ts` – centrale sync-service voor optimistic updates.
|
||
- `location.ts` – tracking van de huidige gebruikerslocatie.
|
||
- `src/lib/utils/` – hulpfuncties, o.a. `geolocation.ts` en `places.ts` (Google Places API).
|
||
- `src/routes/` – pagina's en API-endpoints:
|
||
- `+page.svelte` – homepage met de kaart en finds.
|
||
- `finds/`, `friends/`, `login/`, ... – feature-specifieke pagina's.
|
||
- `api/` – submappen voor finds, friends, users, notifications, profile-picture, places, media,
|
||
...
|
||
- `drizzle/` – migratiebestanden en metadata.
|
||
- `static/` – statische assets (fonts, map-styles, afbeeldingen, manifest, robots.txt, ...).
|
||
- `scripts/` – hulpscripts (zoals het genereren van VAPID-keys).
|
||
|
||
== Location-gecentreerde architectuur
|
||
|
||
Eén van de belangrijkste architecturale keuzes is de overstap naar een *location-gecentreerd
|
||
architectuurmodel*. In plaats van elke find zijn eigen, duplicerende locatiedata te laten opslaan,
|
||
worden locaties in een aparte `locations`-tabel beheerd. Meerdere finds kunnen aan dezelfde locatie
|
||
gekoppeld worden. Dit heeft meerdere voordelen:
|
||
|
||
- *Data-normalisatie* – locatiegegevens (coördinaten, naam, type) worden op één plaats beheerd.
|
||
- *Betere performance* – queries kunnen efficiënter filteren en groeperen op locatie.
|
||
- *Functionaliteit per locatie* – het wordt eenvoudiger om alle finds op één plek te combineren en hier extra functionaliteit rond te bouwen (bijv. populariteitsmetrieken in de toekomst).
|
||
|
||
De grote logic overhaul (Phase 6–7 in het logboek) beschrijft hoe de bestaande finds-architectuur
|
||
werd omgevormd naar dit model, inclusief migraties en aanpassingen op zowel API als frontend.
|
||
|
||
== Sync-service en api-sync store
|
||
|
||
Om de gebruikerservaring soepel te houden, maakt Serengo gebruik van een *sync-service* en een
|
||
centrale `api-sync` store. In plaats van bij elke wijziging te wachten op een serverrespons, wordt
|
||
het volgende patroon gebruikt:
|
||
|
||
1. De gebruiker voert een actie uit (bijvoorbeeld een find aanpassen of een comment toevoegen).
|
||
2. De wijziging wordt onmiddellijk lokaal toegepast (optimistic update) zodat de UI instant reageert.
|
||
3. De sync-service stuurt de wijziging naar de API.
|
||
4. Bij succes wordt de lokale staat bevestigd; bij een fout wordt de wijziging teruggedraaid (rollback) en ziet de gebruiker een duidelijke foutmelding.
|
||
|
||
Deze architectuur zorgt voor een responsieve interface, zelfs bij netwerkvertraging, en centraliseert
|
||
state management voor finds, comments, likes en ratings.
|
||
|
||
In de toekomst zou ik hier eventueel WebSockets aan kunnen toevoegen voor real-time updates tussen gebruikers. Waarschijnlijk zou het ook interessant zijn om hiervoor te kijken naar een externe service. Ik kijk hierbij naar Convex [@convex-docs] die real-time sync en offline-first functionaliteit biedt zodat dit niet allemaal zelf gebouwd en onderhouden hoeft te worden.
|
||
|
||
== Integratie met externe diensten
|
||
|
||
De architectuur integreert verschillende externe diensten op een consistente manier:
|
||
|
||
- *Cloudflare R2* voor opslag van media, benaderd via de `r2.ts`-service en beveiligde signed
|
||
URLs.
|
||
- *Google OAuth* voor authenticatie, afgehandeld via `oauth.ts` in combinatie met Lucia [@lucia-docs].
|
||
- *Google Places API* voor POI-zoekfunctionaliteit, gebruikt door de map- en locatiecomponenten.
|
||
- *Web Push* voor notificaties, met VAPID-keys en een service worker die push-events verwerkt.
|
||
|
||
Elke integratie is ondergebracht in een eigen module of service, zodat de invloed op de rest van het
|
||
systeem beperkt en overzichtelijk blijft.
|