5.0 KiB
5.0 KiB
[DEF:ADR-0006:ADR]
@STATUS ACTIVE
@PURPOSE Define the frontend architecture for ss-tools: Svelte 5 runes reactivity model, SvelteKit routing, component composition rules, state management patterns, API client conventions, and UX contract expectations for C4/C5 components.
@RELATION DEPENDS_ON -> [ADR-0001:ADR]
@RELATION DEPENDS_ON -> [ADR-0002:ADR]
@RATIONALE Svelte 5 introduces a fundamentally different reactivity model (runes: $state, $derived, $effect, $props) compared to Svelte 4's $: reactive statements. This ADR locks in the Svelte 5 runes approach and prevents backsliding into legacy patterns when new developers or AI agents contribute code.
@RATIONALE SvelteKit with static adapter (@sveltejs/adapter-static) was chosen over SvelteKit SSR because: (a) ss-tools is a Docker‑deployed SPA behind nginx — server‑side rendering provides no latency benefit, (b) static SPA simplifies Docker deployment (no Node.js server needed, just nginx serving static files), (c) all data is fetched via REST API, not server‑side load functions.
@RATIONALE Svelte 5 runes ($state, $derived, $effect) are mandated over Svelte 4 $: reactivity and writable stores because: (a) runes are the forward path — Svelte 4 patterns are deprecated, (b) runes provide fine‑grained reactivity without subscription boilerplate, (c) $state can be used outside .svelte files in .svelte.js modules, enabling reusable reactive logic.
@REJECTED React/Next.js — rejected by ADR-0003 (technology stack independence from Superset). Svelte 5 was chosen for its compile‑time approach, smaller bundle size, and superior DX for this project's scale.
@REJECTED Svelte 4 legacy patterns ($:, writable/readable stores) — rejected because Svelte 5 runes are the actively maintained reactivity model. Mixing two models creates confusion and potential reactivity bugs.
@REJECTED Server‑Side Rendering (SSR) with SvelteKit — rejected because the data is entirely API‑driven (no server‑side page data loading), and SSR adds deployment complexity (Node.js process) without latency benefit for authenticated SPA users.
Decision
Technology Stack
| Layer | Choice | Version |
|---|---|---|
| Framework | SvelteKit | 2.x |
| UI Library | Svelte | 5.x (runes mode) |
| Build | Vite | 7.x |
| Styling | Tailwind CSS | 3.x |
| Adapter | @sveltejs/adapter-static | 3.x (SPA mode) |
| Testing | vitest + @testing-library/svelte | 4.x / 5.x |
Reactivity Model (Svelte 5 Runes)
Legacy (FORBIDDEN) Svelte 5 Runes (REQUIRED)
──────────────────────── ──────────────────────────
let count = 0; let count = $state(0);
$: doubled = count * 2; let doubled = $derived(count * 2);
$: { /* side effect */ } $effect(() => { /* side effect */ });
export let name; let { name } = $props();
import { writable } from ... ❌ stores are .svelte.js modules with $state
State Management Pattern
Three tiers of state, strictly separated:
- Component‑local state:
$state()inside.sveltefiles. Never exported. - Shared reactive state:
.svelte.jsmodules exporting$stateobjects. Imported by components.// frontend/src/lib/stores/dashboard.svelte.js export const dashboardState = $state({ dashboards: [], selectedId: null, isLoading: false }); - Server state: Fetched via API client, held in
$statevariables locally. No global cache — each route fetches what it needs.
API Client Convention
API client modules live under frontend/src/lib/api/. Each module wraps fetch calls to the FastAPI backend, attaches the JWT access token from the auth store, and normalises errors into a typed ApiError. Contract annotations follow the rules defined in the semantics-core and semantics-contracts skills.
Component Complexity & UX Contracts
Svelte components with side effects (API calls, WebSocket subscriptions, file operations) MUST carry UX contract tags as defined by the semantics-frontend skill (.opencode/skills/semantics-frontend/SKILL.md). The skill is the canonical source for tag inventory, syntax, and per‑complexity requirements.
Component File Structure
frontend/src/lib/components/
├── DashboardGrid.svelte # C4: complex data grid with WebSocket updates
├── MappingTable.svelte # C3: migration mapping table
├── PluginExecutionCard.svelte # C4: plugin runner with progress feedback
├── RoleBadge.svelte # C1: simple role display
└── ...
Routing
- SvelteKit file‑based routing under
frontend/src/routes/ - Protected routes check auth in
+layout.svelte(redirect to/loginif no token) - Route structure:
/(dashboard list),/envs/[id](environment detail),/migration(migration wizard),/plugins(plugin management),/admin(user/role management)