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

8.3 KiB

HemHubs arkitektur

Detta dokument beskriver den arkitektur som kan verifieras i repositoryts kod, tester och konfiguration. Historiska implementationssteg finns under features/ och övergripande beslut under decisions/.

Aktuell implementation

Monorepo

HemHub ligger i ett Git-repository med två separata applikationer:

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