server/docs/erfaringer/adapter_moenster.md
vegard af8f6f97c2 Docs: oppdater alle dokumenter til ny SpacetimeDB-cache-arkitektur
- synkronisering.md: ~5s → ~1s, trådbasert warmup med per-kanal config
- adapter_moenster.md: omskrevet — dokumenterer nåværende arkitektur + historiske anti-patterns
- chat.md: implementeringsstatus oppdatert til mars 2026
- spacetimedb_integrasjon.md: trådbasert warmup og admin-UI dokumentert

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 02:48:15 +01:00

3.1 KiB

Erfaring: Adapter-mønster for chat (PG ↔ SpacetimeDB)

1. Mønsteret

Et felles interface (ChatConnection) med to implementasjoner:

  • SpacetimeDB-adapter (primær) — all data fra SpacetimeDB, worker håndterer warmup + sync
  • PG-adapter (fallback) — polling hvert 3 sek, brukes kun når SpacetimeDB ikke er konfigurert

Factory-funksjon velger adapter basert på miljøvariabel (VITE_SPACETIMEDB_URL).

ChatBlock.svelte  →  createChat()  →  SpacetimeDB-adapter (primær)
                                    →  PG-adapter (fallback, readonly)

Fordeler:

  • Kan teste PG-adapter isolert uten Docker/SpacetimeDB
  • Fallback er trivielt — fjern env-variabelen
  • Komponenten vet ingenting om hvilken adapter som brukes
  • ChatConnection-interface har edit(), delete(), react() — ingen direkte PG API-kall fra komponenten

Referanse: web/src/lib/chat/ — hele mappen er organisert etter dette mønsteret.

2. Historisk anti-pattern: Lazy wrapper som byttet adapter

Vi prøvde først en "lazy wrapper" som startet med PG-adapter og byttet til SpacetimeDB-adapter når tilkoblingen var klar. Problemet:

  • En plain let activeConnection i wrapperen er ikke reaktiv i Svelte 5
  • Når wrapperen byttet adapter, forsvant meldingene — ny adapter startet med tom liste
  • Svelte-komponentene så aldri byttet fordi proxy-referansen ikke oppdaterte seg

Lærdom: Ikke bytt adapter runtime. Velg én ved oppstart.

3. Historisk anti-pattern: Hybrid-adapter (PG + SpacetimeDB samtidig)

Den andre iterasjonen brukte en hybrid-adapter som hentet historikk fra PG via REST og lyttet på SpacetimeDB for nye meldinger. Dette skapte:

  • Kompleks dedup-logikk (deletedIds Set, merge av PG- og ST-meldinger)
  • Race conditions mellom PG-polling og SpacetimeDB-callbacks
  • BigInt-konverteringer og workarounds i frontend

Løsningen: SpacetimeDB som cache foran PG. Worker gjør warmup (PG → ST) ved oppstart, frontend snakker kun med SpacetimeDB. Ingen merge-logikk nødvendig.

4. Nåværende arkitektur

SpacetimeDB er en varm cache foran PostgreSQL:

  • Worker warmup: Ved oppstart lastes meldinger + reaksjoner fra PG → SpacetimeDB per kanal
  • Frontend → SpacetimeDB: Subscription gir alle meldinger (historikk fra warmup + nye)
  • SpacetimeDB → PG: Sync-worker poller sync_outbox hvert sekund
  • PG er autoritativ — ved SpacetimeDB-restart oppvarmes fra PG

Fordeler over hybrid:

  • Ingen dedup, merge eller deletedIds
  • Frontend-koden er dramatisk enklere
  • Konsistent datamodell — alt kommer fra én kilde
  • Reaksjoner håndteres via SpacetimeDB-tabeller, ikke PG API

5. Anbefaling for neste komponent

Når Kanban eller Whiteboard skal bygges med SpacetimeDB:

  1. Start med PG-adapter. Få hele flyten til å fungere med REST/polling først.
  2. Lag SpacetimeDB-adapter med warmup. Worker laster data fra PG ved oppstart.
  3. Bruk samme factory-mønster. Felles interface, env-variabel for valg.
  4. Legg til warmup-config i channels.config (eller tilsvarende config-felt).
  5. Test begge adaptere uavhengig før du integrerer i UI-komponenten.