Engineering Requirements Document (ERD)
corebank-microservices-java
Phase 2 – Microservices Migration (Java)
Document Version: 1.0
Last Updated: May 10, 2026
Status: Active
Project Series: CoreBank Modernization Journey
The corebank-microservices-java project is the first evolutionary step in the CoreBank Modernization Journey: a realistic, incremental migration of the Phase 1 legacy monolith into a production-grade microservices architecture using the Strangler Fig Pattern.
From a single tightly-coupled Spring Boot application, we now have:
- Two independently deployable microservices (
auth-service+core-service) - A shared
banking-commonslibrary (extracted common concerns) - Full Hexagonal Architecture (Ports & Adapters) + DDD bounded contexts per service
- Secure, header-driven inter-service communication
- Reactive orchestration (
Mono.zip()) and resilience patterns (Resilience4j) in the core domain
Purpose
Demonstrate the modernization of a legacy banking application into independently scalable microservices while maintaining the exact same external API contract.
Business Capabilities
- Client authentication with JWT and banking headers (
auth-service) - Aggregated product & balance information for authenticated clients (
core-service)
Non-Functional Goals
- 80%+ test coverage enforced per module
- Independent deployments
- High availability via reactive patterns and circuit breaking
- Strict separation of business logic from infrastructure using Hexagonal Architecture
flowchart TD
Client[Client / Insomnia] --> Auth[auth-service :8081<br/>POST /api/auth/login]
Client --> Core[core-service :8082<br/>GET /api/home/balance]
Auth <--> Redis[(Redis Token Cache)]
Core <--> Postgres[(PostgreSQL)]
subgraph banking-commons [Shared Library]
direction TB
Commons[ResponseDTO<br/>JwtUtil<br/>HeaderConstants<br/>Security Filters<br/>Shared DTOs]
end
Auth --> Commons
Core --> Commons
domain/ → Entities, Value Objects (pure business logic)
application/ → Use cases (ports: input + output)
infrastructure/ → Adapters (web controllers, persistence, config)
sequenceDiagram
participant Client
participant CoreService
participant HomeUseCase
participant AccountRepo
participant CardRepo
participant BalanceRepo
Client->>CoreService: GET /api/home/balance + JWT
CoreService->>HomeUseCase: getAggregatedBalance()
HomeUseCase->>AccountRepo: findByCustomerId() (reactive)
HomeUseCase->>CardRepo: findByCustomerId() (reactive)
HomeUseCase->>BalanceRepo: findByCustomerId() (reactive)
Note over HomeUseCase,BalanceRepo: Mono.zip() parallel orchestration
HomeUseCase-->>CoreService: HomeAggregate
CoreService-->>Client: ResponseDTO
| Category | Technology | Version | Service |
|---|---|---|---|
| Language | Java | 21 | All modules |
| Framework | Spring Boot | 4.0.6 | All modules |
| Build | Gradle (Kotlin DSL) | 8.13+ | Multi-module root |
| Architecture | Hexagonal + DDD | - | Both services |
| Web (auth) | Spring MVC | - | auth-service |
| Web (core) | Spring WebFlux | - | core-service (reactive) |
| Security | Spring Security + JJWT | 0.12.6 | Both + commons |
| Resilience | Resilience4j (Circuit Breaker + Retry) | latest | core-service |
| Database | PostgreSQL + Spring Data JPA | - | core-service |
| Cache | Redis + Spring Data Redis | - | auth-service |
| Reactive | Project Reactor (Mono.zip) | - | core-service orchestration |
| Shared Library | banking-commons | - | Common DTOs, security, utils |
| Container | Docker + Docker Compose | - | Full ecosystem |
| Testing | JUnit 5 + Mockito + StepVerifier | - | ≥80% JaCoCo per module |
corebank-microservices-java/
├── banking-commons/ # Shared library
│ └── src/main/java/com/corebank/commons/
│ ├── model/ResponseDTO.java
│ ├── dto/{LoginRequestDTO,AccountDTO,CardDTO,BalanceDTO,HomeAggregateDTO}
│ ├── security/{JwtUtil,HeaderConstants,BankingSecurityFilter,ReactiveJwtFilter}
│ └── exception/GlobalExceptionHandler.java
├── auth-service/ # Authentication bounded context (MVC)
│ └── src/main/java/com/corebank/auth/
│ ├── domain/model/{AuthToken,Credentials}
│ ├── application/port/input/AuthenticateUseCase
│ ├── application/port/output/TokenCachePort
│ ├── application/service/AuthApplicationService
│ └── infrastructure/adapter/{web/AuthController,persistence/RedisTokenCacheAdapter}
├── core-service/ # Product/Home bounded context (WebFlux)
│ └── src/main/java/com/corebank/core/
│ ├── domain/model/{Account,Card,Balance,HomeAggregate}
│ ├── application/port/input/GetHomeBalanceUseCase
│ ├── application/port/output/{AccountRepositoryPort,CardRepositoryPort,BalanceRepositoryPort}
│ ├── application/service/HomeApplicationService
│ └── infrastructure/adapter/{web/HomeController,persistence/*Adapter}
├── docker-compose.yml
├── build.gradle.kts
└── settings.gradle.kts
Authentication Domain (auth-service)
- Full Hexagonal implementation.
- Login with document number / password (mock).
- JWT generation + custom claims cached in Redis.
- Banking header enrichment.
Homepage / Product Domain (core-service)
- Full Hexagonal implementation.
- Aggregated view of accounts, cards, and balances.
- Reactive, parallel data retrieval using
Mono.zip(). - Protected by Circuit Breaker and Retry mechanisms using Resilience4j.
| Endpoint | Service | Port | Method | Description | Required Headers |
|---|---|---|---|---|---|
/api/auth/login |
auth-service | 8081 | POST | Authenticate + issue JWT | X-CustIdentNum, X-CustIdentType |
/api/home/balance |
core-service | 8082 | GET | Aggregated homepage data | All banking headers + Authorization |
/actuator/health |
both | 8081/2 | GET | Health check | - |
Response Format (from banking-commons):
{
"statusCode": 200,
"body": { ... },
"extraArgs": { ... }
}- Java 21
- Docker & Docker Compose
docker compose up -d # Postgres + Redis./gradlew clean build
# In separate terminals:
./gradlew :auth-service:bootRun
./gradlew :core-service:bootRundocker compose down -v- Dual-Security Architecture implemented in
banking-commons:BankingSecurityFilter: Blocks and authenticates requests for MVC apps (auth-service) usingOncePerRequestFilter.ReactiveJwtFilter: Secures WebFlux apps (core-service) natively usingWebFilter.
- Extracts standard banking headers (
X-CustIdentNum,X-CustIdentType,X-RqUid,X-SesID). - All business endpoints require a valid JWT except for login and actuator.
- Local infrastructure powered by Docker Compose.
- PostgreSQL runs on port
5432forcore-service. - Redis runs on port
6379forauth-servicetoken caching. - Services are built natively as executable JARs using the Spring Boot plugin in sub-modules.
Coverage Target: ≥ 80% per module (JaCoCo enforced in root build.gradle.kts)
Commands:
./gradlew clean build jacocoTestReportTests strictly adhere to testing layers individually (e.g., Use cases are unit-tested without loading the Spring Context, Web adapters are tested using @WebMvcTest and @WebFluxTest).
| Aspect | Phase 1 (Monolith) | Phase 2 (Microservices) |
|---|---|---|
| Architecture | Layered (Controller→Svc→Repo) | Hexagonal + DDD per service |
| Deployment | Single JAR | 2 independent services + shared lib |
| Web Framework | Spring MVC (blocking) | MVC (auth) + WebFlux (core) |
| Data Orchestration | Synchronous, sequential | Reactive Mono.zip() (parallel) |
| Resilience | None | Resilience4j (Circuit Breaker + Retry) |
| Shared Code | Everything coupled | Extracted to banking-commons |
| Testability | Coupled tests | Per-layer tests (domain, app, infra) |
| Scalability | Scale entire app | Scale each service independently |
| External Contract | ResponseDTO on :8080 |
Identical ResponseDTO on :8081/:8082 |
- Architecture Rules: Business logic strictly belongs in the
domainandapplicationlayers. Spring/JPA/Web code strictly belongs ininfrastructure. - Dependencies: The core layers (
domain,application) must NOT depend on Spring, JPA, or Web classes. Use interfaces (ports) to communicate. - Shared Commons: Cross-cutting concerns go in
banking-commons. Do not duplicate DTOs, Security classes, or utility functions in the individual microservices. - Testing: Mock input/output ports when testing application services. Target minimum 80% coverage per module.
- Spring Boot Actuator: Present in both services for
/actuator/health. - Resilience4j Metrics: Available in
core-serviceto monitor Circuit Breaker states for external calls (like DB queries). - Redis Cache: Integrated to cache issued JWTs.
Since services are now split by port, using an environment setup is highly recommended.
-
Create a new Collection:
CoreBank Microservices - Phase 2 -
Create an Environment named
Localwith these variables:{ "authBaseUrl": "http://localhost:8081", "coreBaseUrl": "http://localhost:8082", "custIdentNum": "123456789", "custIdentType": "CC" }
Request Name: 1. POST Login
- Method:
POST - URL:
{{ authBaseUrl }}/api/auth/login - Headers:
Content-Type:application/jsonX-CustIdentNum:{{ custIdentNum }}X-CustIdentType:{{ custIdentType }}
- Body (JSON):
{ "username": "user", "password": "password" }
Expected: 200 OK with ResponseDTO containing JWT in body.
Tip: Right-click the token in the response → Store Response → Save as variable named
authToken
Request Name: 2. GET Home Balance
- Method:
GET - URL:
{{ coreBaseUrl }}/api/home/balance - Headers:
Authorization:Bearer {{ authToken }}X-CustIdentNum:{{ custIdentNum }}X-CustIdentType:{{ custIdentType }}
Expected: 200 OK with ResponseDTO containing parallel-aggregated account data.
# Login (auth-service :8081)
curl -X POST http://localhost:8081/api/auth/login \
-H "Content-Type: application/json" \
-H "X-CustIdentNum: 123456789" \
-H "X-CustIdentType: CC" \
-d '{"username":"user","password":"password"}'
# Home Balance (core-service :8082 — replace <token> with the token from above)
curl http://localhost:8082/api/home/balance \
-H "Authorization: Bearer <token>" \
-H "X-CustIdentNum: 123456789" \
-H "X-CustIdentType: CC"This project is not a toy — it is a deliberate, production-inspired demonstration of the exact modernization journey used in enterprise fintech.
All phases use Spring Boot 4.0.6 + Java 21 while demonstrating progressive architectural improvement.