#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.
