Files
hemhub/docs/architecture.md
2026-07-27 13:15:39 +02:00

214 lines
8.3 KiB
Markdown

# HemHubs arkitektur
Detta dokument beskriver den arkitektur som kan verifieras i repositoryts kod,
tester och konfiguration. Historiska implementationssteg finns under
[`features/`](features/) och övergripande beslut under
[`decisions/`](decisions/).
## Aktuell implementation
### Monorepo
HemHub ligger i ett Git-repository med två separata applikationer:
```text
hemhub/
├── backend/
├── frontend/
└── docs/
```
Applikationerna har egna byggverktyg och beroenden. De delar inte källkod eller
byggprocess.
### Frontend
Frontend finns i `frontend/` och använder React 19, TypeScript, Vite, pnpm och
dnd-kit-ekosystemets aktuella React-adapter. Den ansvarar för:
- hämtning och presentation av användare och uppgifter;
- lokalt val av aktiv användare;
- formulär för att skapa användare och uppgifter;
- val och visning av ansvarig användare på uppgifter;
- serverbekräftade statusändringar genom knappar på uppgiftskorten;
- optimistiska statusflyttar genom drag-and-drop mellan brädans kolumner;
- klientnära validering och begripliga felmeddelanden;
- uppgiftsbrädan med kolumnerna Väntande, Pågående och Klart.
Tillståndet hanteras lokalt i React-komponenter. Ingen router eller separat
global state-lösning används.
### Backend
Backend finns i `backend/` och använder Java 21, Spring Boot 4.1.0, Maven,
Spring Web, Spring Data JPA och Flyway. Maven Wrapper ingår i repositoryt.
Backend ansvarar för API, slutlig validering, skapande av UUID och tidsstämplar,
persistens samt sortering av returnerade användare och uppgifter.
### Kommunikation
Alla applikationsendpoints ligger under `/api`. Frontend använder enbart
relativa adresser, exempelvis `/api/users` och `/api/tasks`.
Vid lokal utveckling kör Vite normalt på port 5173 och proxar `/api` till
`http://localhost:8080`, där Spring Boot körs. Ingen generell
CORS-konfiguration finns i backend. Webbläsaren anropar därmed Vites origin,
och utvecklingsservern vidarebefordrar API-anropen.
Aktuella endpoints:
- `GET /api/health`
- `GET /api/users`
- `POST /api/users`
- `GET /api/tasks`
- `POST /api/tasks`
- `PUT /api/tasks/{taskId}/assignee`
- `PUT /api/tasks/{taskId}/status`
### Databas och migreringar
Lokal körning använder en H2-databas i minnet. Databasen finns under
backendprocessens livstid och lokal utvecklingsdata återställs när backend
startas om. Automatiska backendtester använder en separat H2-databas i minnet.
Båda anslutningarna använder H2:s `MODE=PostgreSQL`,
`DATABASE_TO_LOWER=TRUE` och `DEFAULT_NULL_ORDERING=HIGH`. Det är en verifierbar
kompatibilitetsinställning, inte samma sak som att applikationen har verifierats
mot PostgreSQL.
Flyway kör migreringarna:
- `V1__create_users.sql`
- `V2__create_tasks.sql`
- `V3__add_task_points.sql`
- `V4__add_task_assignee.sql`
Hibernate är konfigurerat med `ddl-auto=validate`; Flyway skapar schemat och
Hibernate validerar entiteterna mot det.
### Domänmodell
#### Användare
En användare lagras i tabellen `app_user` med:
- `id`: UUID;
- `name`: visningsnamn, högst 50 tecken;
- `normalized_name`: trimmat namn i gemener, internt och unikt;
- `created_at`: en `Instant`, lagrad som `TIMESTAMP WITH TIME ZONE`.
`normalized_name` exponeras inte via API. Användare returneras alfabetiskt efter
visningsnamn med deterministiska sekundära jämförelser.
#### Uppgift
En uppgift lagras i tabellen `task` med:
- `id`: UUID;
- `title`: obligatorisk titel, högst 100 tecken;
- `description`: valfri beskrivning, högst 500 tecken;
- `status`: `WAITING`, `IN_PROGRESS` eller `COMPLETED`;
- `points`: obligatoriskt heltal mellan 1 och 99;
- `assignee_id`: nullable främmande nyckel till `app_user`;
- `created_at`: en `Instant`, lagrad som `TIMESTAMP WITH TIME ZONE`.
Status lagras som enumens textvärde genom `EnumType.STRING`. Nya uppgifter får
alltid status `WAITING`. Poängintervallet skyddas i backend och med en
databasconstraint. En uppgift kan vara otilldelad eller referera till exakt en
ansvarig användare. Relationen hämtas tillsammans med uppgifterna när de listas,
så API-responsen kan innehålla ansvarigs `id` och `name` utan separata
frontend-anrop. Alla aktiva användare ser samma uppgiftslista.
Ansvarig är valfri vid skapande. Tilldelnings-API:t kan tilldela eller byta
ansvarig i samtliga statusar. Ansvarig kan tas bort i `WAITING` och `COMPLETED`,
men inte i `IN_PROGRESS`. Tilldelning ändrar aldrig uppgiftens status.
Alla direkta statusövergångar är tillåtna och samma målstatus är idempotent.
`IN_PROGRESS` kräver en ansvarig. När en otilldelad uppgift påbörjas skickar
frontend aktiv användares id, och backend tilldelar användaren och ändrar status
i samma transaktion. En befintlig ansvarig byts aldrig av statusoperationen.
### Aktiv användare
Användarlistan hämtas från backend. Frontend lagrar endast den valda
användarens UUID i webbläsarens `localStorage` under nyckeln
`hemhub.activeUserId`.
Vid start verifieras det lagrade id:t mot backendens aktuella användarlista. Ett
giltigt val återanvänds i samma browser. Ett ogiltigt val tas bort. Valet är
lokalt per browser och utgör inte autentisering eller behörighetskontroll.
### Felhantering
Backend använder ett litet gemensamt JSON-format med `code` och `message`.
`ApiExceptionHandler` översätter kända valideringsfel till `400 Bad Request`,
saknade uppgifter eller användare till `404 Not Found` och dubbletter eller
otillåtna tilldelningsändringar till `409 Conflict`.
Frontend skiljer mellan fel vid hämtning och skapande. Hämtfel kan
återförsökas. Formulärfel visas nära formuläret och inmatningen behålls vid
misslyckade API-anrop. Status- och tilldelningsfel visas lokalt på berört kort;
statusknappar och tilldelning uppdateras först med backendens bekräftade
respons. Drag-and-drop flyttar kortet optimistiskt men återställer hela den
tidigare uppgiften vid fel. Vid framgång ersätts alltid det lokala värdet med
backendens fullständiga respons. Status- och tilldelningsanrop delar låsning per
task-id, så det berörda kortet blockeras utan att resten av brädan låses.
Drag-and-drop återanvänder backendens befintliga status-API oförändrat.
### Teststrategi
Backend har JUnit 5-tester:
- ett fristående MockMvc-test för health-endpointen;
- Spring Boot-integrationstester via MockMvc mot H2 in-memory för användar- och
uppgifts-API.
Frontend använder Vitest, jsdom och React Testing Library. `fetch` och
`localStorage` ersätts i testerna, så frontendtesterna kräver inte en körande
backend. Drag-and-drop-adaptern översätter bibliotekshändelser till task-id och
status, så stateflöden kan testas utan att simulera fysisk layout i jsdom.
Produktionsbygget kör TypeScript-kompilering följt av Vite.
### Produktionsdeployment
Ingen produktionsdeployment är implementerad i repositoryt. Det finns inga
Dockerfiler, pipelinefiler eller produktionsspecifika Nginx-, Watchtower- eller
databaskonfigurationer. H2 används både lokalt och i automatiska tester; någon
PostgreSQL-konfiguration finns ännu inte.
## Beslutad planerad riktning
Repositoryt anger att affärsregler även framöver ska säkerställas i backend och
att större arkitekturella beslut ska diskuteras innan de införs.
Följande produktionsriktning är beslutad men ännu inte implementerad:
- PostgreSQL ska användas som produktionsdatabas.
- Frontend och backend ska paketeras som separata Docker-images.
- Källkoden ligger i Gitea.
- Drone ska bygga och publicera images till ett privat registry.
- Watchtower ska uppdatera de körande tjänsterna när nya images publiceras.
- Nginx kan användas som reverse proxy framför tjänsterna.
- Produktionsmiljön ska köras på Ubuntu-servern Biff.
Den planerade riktningen beskrivs även i
[`005-production-deployment-direction.md`](decisions/005-production-deployment-direction.md).
Punkterna ovan beskriver målbilden och ska inte tolkas som att motsvarande
konfiguration redan finns eller har verifierats.
## Fortfarande öppna detaljer
Följande har inte fastställts i dokumentationen och ska beslutas i samband med
att produktionslösningen implementeras:
- exakt containerstruktur och tjänsteindelning;
- image-namn och taggningsstrategi;
- produktions-URL;
- hantering och distribution av secrets;
- exakt Nginx-konfiguration;
- exakt Drone-, registry-, Watchtower- och deploymentkonfiguration.
Miljöspecifika adresser, credentials och secrets ska inte lagras i dessa
arkitekturdokument.