Weather dashboard built with SvelteKit and Svelte 5, as an example of Hexagonal Architecture in a frontend project.
Weather APIs are swappable via ports and adapters (OpenMeteo and mock for now). Includes unit and E2E tests.
bun install
bun run dev
| Command | Description |
|---|---|
bun run dev |
Start dev server |
bun run dev:mock |
Dev with mock API (no network calls) |
bun run build |
Production build |
bun run test |
Run all tests |
bun run test:unit |
Unit tests (Vitest) |
bun run test:e2e |
E2E tests (Playwright) |
bun run storybook |
Component playground |
bun run lint |
Lint + format check |
Hexagonal architecture with ports & adapters:
src/
├── domain/ # Core types, weather mappings
├── adapters/ # API implementations (OpenMeteo, Nominatim, Mock...)
├── services/ # Application services
├── stores/ # Svelte state management
├── components/
│ ├── ui/ # Design system (GlassCard, Text, Badge...)
│ ├── features/ # Feature components (CitySearch, Forecast...)
│ └── shared/ # Shared components (SearchCombobox...)
└── app/ # Page layouts
The domain layer defines interfaces (ports) that abstract external dependencies. Business logic never touches infrastructure directly.
| Port | What it abstracts | Adapter(s) |
|---|---|---|
WeatherServicePort |
Weather forecast API | OpenMeteo, Mock |
GeocodingPort |
City search (forward) | OpenMeteo, Mock |
ReverseGeocodingPort |
Coords to city name | Nominatim (OpenStreetMap), Mock |
StoragePort |
Key-value persistence | localStorage, Mock |
GeolocationPort |
Device location | Browser Geolocation API, Mock |
NotificationPort |
User-facing messages | Store-backed toasts, Mock |
Weather, geocoding, and reverse geocoding adapters can each be switched independently from the UI. The other three are standalone infrastructure ports backed by browser APIs.
All ports are provided to components through Svelte's context API, so nothing imports an adapter directly.
src/adapters/yourapi/WeatherServicePort and GeocodingPortsrc/stores/weatherDomainsState.svelteE2E tests run against mock adapters by default for speed and reliability.
# Run with UI
bun run test:e2e --ui
# Debug mode
bun run test:e2e --debug
On Linux, tests use system Chromium. Install via your package manager if needed.
develop ──push/PR──> CI (lint, typecheck, test, build)
│
└──PR──> main ──push──> Release (bump, changelog, GitHub Release)
│
└── merge back into develop
Commits follow conventional commits. Version bumps and changelog are automated by cocogitto.
Lefthook enforces ESLint and commit message format locally on every commit.
Weather icons by Icons8