-
Notifications
You must be signed in to change notification settings - Fork 0
Design Decisions
stdOWL edited this page Jun 22, 2026
·
1 revision
Why the project is built the way it is. (AI_USAGE.md in the repo has the full phase-by-phase record.)
| Decision | Why | Trade-off / note |
|---|---|---|
| WebFlux + Kotlin coroutines | Non-blocking reactive stack; suspend handlers read like synchronous code |
Reactive learning curve; blocking work (BCrypt) must be pushed off the event loop |
| JWT, login-only | Stateless auth, no session store; accounts are seeded (no self-registration by design) | No token revocation before expiry (deferred) |
| Token-bucket rate limiting | Smooths bursts, caps the sustained rate; pluggable in-memory / Redis | Behind a proxy, accurate per-client limiting needs the real client IP — see Rate Limiting |
| R2DBC + H2, Flyway over JDBC | Reactive DB access; one tool owns schema + seed | H2 in-memory → data resets, single instance; Postgres is the scale-out path |
| DB-trigger audit trail | Can't be bypassed by the app path; Envers is JPA-only (N/A on R2DBC) | H2 delete-attribution caveat — see Data Migrations and Auditing |
| SPA served from the backend | One origin, one container, no CORS between UI and API | UI + API release together; the image build includes a Node stage |
| Cloud Run over GKE | Scale-to-zero, ~$0 idle, no cluster to operate | Single instance with in-memory data; a managed DB is needed to scale out |
| Keyless CI (WIF) | No long-lived service-account keys stored in GitHub | One-time Workload Identity setup |
- In-memory H2 → single instance; data resets on restart.
- Rate limiting behind Cloud Run is coarse unless the real client IP is wired in (Troubleshooting).
- No JWT revocation before expiry.
Getting started
How it works
Operations
Reference