I Built a Production-Ready Spring Boot Microservices Architecture (So You Don't Have To)
The Problem: The "Hello World" Trap
Every time we start a new microservices project, we fall into the same trap. Setting up a basic Spring Boot app is fastโbut taking it to production readiness is exhausting.
You need to configure an API Gateway, set up an Identity Provider (like Keycloak), figure out how to securely serve both a web app and mobile clients, configure distributed tracing, establish a robust PostgreSQL + Redis baseline, and wire it all together in Docker. Suddenly, you've burned two weeks of dev time before writing a single line of business logic.
I got tired of repeating this process, so I built an Enterprise Spring Microservices Template that solves these infrastructural headaches out of the box.
In this article, I want to walk through the architectural decisions behind this template, specifically focusing on the "Two-Lane" Security Architecture and Defense in Depth.
๐๏ธ The "Two-Lane" Architecture: BFF for Web, JWT for Mobile
One of the biggest security debates in modern web development is where to store your authentication tokens. Storing JWTs in localStorage in the browser makes your app vulnerable to Cross-Site Scripting (XSS).
To solve this, this architecture implements the Backend-For-Frontend (BFF) pattern for browser clients, while keeping standard Token-based access for mobile clients.
Lane 1: The Web Application (Angular 18)
Web applications should never touch the actual JWT. Instead:
- The Angular HTTP interceptor prefixes all API calls with
/bff/api/{service}. - The BFF handles the OAuth2 Login flow with Keycloak and receives the tokens.
- The BFF stores the tokens securely and issues an HttpOnly, Secure Session Cookie to the browser.
- When Angular makes a request, the BFF attaches the saved JWT and forwards the request to the Spring Cloud Gateway.
Lane 2: The Mobile Application
Mobile apps don't suffer from the same browser XSS vulnerabilities. iOS and Android have secure enclaves (like Keychain) to store tokens.
- The mobile app authenticates directly with Keycloak and receives the JWT.
- The mobile app makes requests directly to the API Gateway using the Authorization Header (
Bearer <token>).
๐ก๏ธ Defense in Depth: Zero-Trust Security
Just securing the outer perimeter isn't enough anymore. If an attacker breaches the API Gateway, your internal microservices shouldn't blindly trust the incoming request.
This template enforces a strict Defense in Depth strategy using JWKS (JSON Web Key Sets).
1. Gateway Validation
The Spring Cloud Gateway acts as the first line of defense. It completely strips the BFF prefix and restructures the path.
-
UI Request:->/bff/api/profile-service/users -
BFF Forwards:->/profile-service/users -
Gateway Rewrites:->/api/users(Routes toprofile-service)
At the Gateway, the JWT signature and basic claims are validated before the request ever touches an internal network.
2. Service-Level Validation (Microsegmentation)
Even though the Gateway validated the token, every individual microservice validates it again.
Each Spring Boot service fetches the JWKS from Keycloak to cryptographically verify the token. Furthermore, fine-grained Role-Based Access Control (RBAC) is enforced at the method or endpoint level within the specific service.
โฑ๏ธ Solving the Token Expiry Edge Case (Proactive Token Refresh)
Have you ever experienced a bug where a user clicks a button, the request goes through, but the request fails mid-flight because the token expired a millisecond too soon?
This template addresses token lifecycle issues with a 60-Second Proactive Refresh Buffer.
When the client (BFF or Mobile app) prepares to make a request, it checks the token's expiration. If the token is set to expire in the next 60 seconds, it actively requests a refresh before dispatching the API call.
This eliminates mid-flight 401 Unauthorized errors and provides a perfectly smooth user experience.
๐ Observability First
When a request travels through an Angular app -> BFF -> Gateway -> Microservice, finding out where it failed is a nightmare without traceability.
This architecture has Observability included by default:
-
Micrometer Tracing: Generates and propagates standard
traceIdandspanIdacross all services. -
Loki / Grafana: Centralized structured logging. You can query logs by
traceIdto instantly see the entire lifecycle of a request. - Zipkin: Visualizes request latency and bottlenecks.
You don't need to manually inject UUIDs into your logs; the context is already handled by the framework.
๐ Get Started Immediately
If you want to skip the boilerplate phase of your next project and start directly with a scalable, secure, and observable baseline, you can use this template.
The Stack:
- Backend: Java 25 (LTS), Spring Boot 4.x, Spring Cloud Gateway
- Frontend: Angular 18
- Infrastructure: Keycloak, PostgreSQL, Redis
- DevOps: Fully containerized with Docker / Maven
๐ GitHub Repository
๐ GitLab Repository
If you found this architecture breakdown helpful, consider giving the repository a โญ๏ธ on GitHub! I'd love to hear your thoughts on the Two-Lane approach in the comments.
Top comments (0)