A six-service backend, modeled on how companies like Imprivata handle unified access management: authenticate once, then get short-lived, audited access into a set of downstream applications, with every access-relevant event streamed for a permanent audit trail and rule-based anomaly detection.
I spent two years as a junior full-stack engineer working with Spring Boot and relational databases, but Docker and Kubernetes never came up on the job — and almost every role I was looking at afterward wanted both. Rather than list a skill I couldn't actually back up in an interview, I built something big enough that I'd have to learn it properly: a system made of several services that genuinely have to talk to each other, not another single-app CRUD project.
Built in six phases, each one verified working end to end before the next started. Every service validates a JWT statelessly using the same signing secret auth-service used to mint it — no service ever calls back to auth-service to check who's asking.
Registration, login, password hashing, JWT access/refresh token issuance. Owns identity, not sessions.
Tracks active sessions and time-boxed access grants into registered target apps. Auto-expires stale sessions and cascades to revoke their grants.
Kafka consumer building an immutable, append-only audit log with a filterable query API. No update or delete path — only inserts.
A second, independent consumer of the same event stream. Rule-based detection: failed-login bursts, impossible travel via IP geolocation, unusual access hours.
A pure REST facade for RBAC admin (users, roles, target apps) with no database of its own, plus a React dashboard calling every service directly from the browser.
Kustomize base + local/AWS overlays, deployed and driven end to end on a real kind cluster, plus a Terraform skeleton for EKS/RDS/MSK.
Screenshots from a real run against the live stack — Kafka, Postgres, and all five backend services actually running, not mocked.
The interesting parts of this project weren't the features — they were the handful of real bugs that only show up once you have more than one service, or once you deploy to something more real than docker-compose.
Adding anomaly-service's new event type in Phase 4 would have broken the already-shipped audit-service the moment it shipped, since its deserializer had no idea what to do with an event type it had never seen. Fixed by wrapping every full-stream consumer's deserializer so unrecognized types are skipped, not fatal.
A Kafka pod crash traced back to Kubernetes auto-injecting a KAFKA_PORT env var that collided with the image's own config convention. A separate liveness-probe race killed JVM services that were merely slow to start, not broken, under CPU contention. Neither would have surfaced if I'd stopped at "the yaml builds."
Every caller through Phase 4 was curl or another backend service. The moment the dashboard called five services directly from the browser in Phase 5, same-origin policy became a real, unavoidable requirement for the first time in this project.