diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg
new file mode 100644
index 0000000..3d59126
--- /dev/null
+++ b/.github/badges/jacoco.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ac8ae7b..0e926df 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -66,7 +66,46 @@ jobs:
path: target/*.jar
# ---------------------------------------------------------------------
- # 2. Build + push a versioned image to Docker Hub
+ # 2. Regenerate the coverage badge from the JaCoCo report and commit it
+ # - runs ONLY on: pushes to main or dev
+ # - the ref check alone excludes pull_request events: their ref is
+ # refs/pull/
-
+
+
Overview ·
Features ·
Tech Stack ·
+ Architecture ·
Getting Started ·
API Docs ·
Observability ·
@@ -74,6 +76,60 @@ WeatherViewer is a personal weather dashboard for tracking the places you care a
| CI/CD | GitHub Actions (build/test, coverage, Docker Hub image push) |
| External API | [OpenWeatherMap](https://openweathermap.org/api) (current weather, forecast, geocoding) |
+## Architecture
+
+**System overview** — the app sits between the browser and four external dependencies. Every HTTP request passes through the rate limiter and the security filter chain before reaching a controller; live dashboard/forecast updates instead flow over a persistent WebSocket connection, pushed on a schedule rather than requested:
+
+```mermaid
+flowchart TB
+ Client[Browser client]
+ RL[Rate limiter]
+ Sec[Security filter chain]
+ Web[Controllers + REST]
+ WS[WebSocket / STOMP]
+ Svc[Services]
+ DB[(PostgreSQL)]
+ Cache[(Redis)]
+ Weather[(OpenWeatherMap API)]
+ Mail[(SMTP)]
+
+ Client --> RL --> Sec --> Web
+ Client -. live updates .-> WS
+ Web --> Svc
+ WS --> Svc
+ Svc --> DB
+ Svc --> Cache
+ Svc --> Weather
+ Svc --> Mail
+```
+
+Postgres holds users, locations, and tokens (schema managed by Liquibase). Redis backs both the rate limiter's fixed-window counters and the weather/forecast/geocoding cache. The two flows below zoom into the parts of this picture that need to tolerate a flaky dependency: weather reads and outbound mail.
+
+Two request paths matter most for reliability: reads that hit the OpenWeatherMap API, and emails triggered by account actions. Both are built so a slow or failing dependency degrades gracefully instead of taking the app down with it.
+
+**Weather read path** — a cache-aside read guarded by retry and a circuit breaker:
+
+```mermaid
+flowchart LR
+ A[Controller] --> B["Cache
@Cacheable"]
+ B --> C["Client
retry + breaker"]
+ C --> D[("Weather API")]
+ C -. fallback .-> E["Fallback
service unavailable"]
+```
+
+A cache hit never reaches `WeatherApiClient`. On a miss, every outbound call is wrapped with Resilience4j: transient failures are retried with backoff, and once OpenWeatherMap is failing consistently the breaker opens and short-circuits straight to the fallback instead of piling up slow requests — so one saved location failing to load doesn't take the rest of the dashboard down with it.
+
+**Async mail path** — a write that only sends mail after its transaction commits:
+
+```mermaid
+flowchart LR
+ F["Service
writes token"] --> G["Event
after commit"]
+ G --> H["Listener
@Async"]
+ H --> I[("SMTP")]
+```
+
+Verification and password-reset emails are sent from a `@TransactionalEventListener(phase = AFTER_COMMIT)`, so an email can never reference a token whose transaction rolled back. The send itself runs `@Async` on a dedicated pool, so a slow SMTP server can't add latency to the request that triggered it. `MailService` retries transient SMTP failures on its own and never throws — a failure there is logged and goes no further.
+
## Prerequisites
- Java 17+
@@ -215,7 +271,8 @@ Tests run against an in-memory H2 database, so no external services are required
Every push to `main` and every pull request into `main`/`dev` runs through GitHub Actions:
1. **Build & Test** — compiles the project and runs the full test suite against H2, publishing a JUnit test report and a JaCoCo coverage report as workflow artifacts.
-2. **Docker build & push** — on pushes to `main`, builds the application image and pushes it to Docker Hub as `podllev/weather-viewer`.
+2. **Update coverage badge** — on pushes to `main`, regenerates the `.github/badges/jacoco.svg` badge from that JaCoCo report and commits it back to the repo.
+3. **Docker build & push** — on pushes to `main`, builds the application image and pushes it to Docker Hub as `podllev/weather-viewer`.
See `.github/workflows/ci.yml` for the full pipeline.