A high-reliability, production-grade microservice implementing a financial account ledger. Built with Java 25 and Spring Boot 4.1.0, this service demonstrates essential patterns required for modern core banking platforms, including high-concurrency pessimistic locking, payment-grade HTTP idempotency key replay, Flyway database migrations, OAuth2 JWT stateless security, and structured ECS JSON logging.
- Double-Entry Financial Integrity & Precision:
- Uses
BigDecimalfor arbitrary-precision monetary calculations (never floating-pointdouble). - Enforces strict business validations on credit and debit requests (preventing negative amounts and insufficient balance states).
- Uses
- Database-Level Concurrency Control (Pessimistic Write Locks):
- Implements
@Transactionalwrite paths backed by JPA pessimistic write locks (findWithWriteLockβSELECT ... FOR UPDATE) to guarantee zero race conditions during simultaneous debits/credits on high-throughput account balances.
- Implements
- Payment-Grade Idempotency Filter (
Idempotency-Key):- Custom HTTP servlet filter (
OncePerRequestFilter) inspecting theIdempotency-Keyheader. - Caches successful 2xx responses (
ContentCachingResponseWrapper) and replays identical status codes, headers, and payload bodies on duplicate/retry requests without re-executing business logic or modifying balances.
- Custom HTTP servlet filter (
- Flyway Versioned Schema Migrations:
- Schema versioning owned by Flyway migrations (
V1__create_accounts_table.sql,V2__create_idempotency_keys.sql) enforcingddl-auto=nonein production profiles.
- Schema versioning owned by Flyway migrations (
- Stateless OAuth2 JWT Security:
- Endpoint protection using Spring Security Resource Server with Nimbus JWT decoding.
- Dedicated authentication controller (
POST /auth/login) issuing signed JWT tokens.
- Multi-Profile Repository Segregation:
- Modular data access layer supporting seamless profile switching (
in-memory,dev,mysql,redis) via Spring@Profileannotations and type-safe@ConfigurationProperties.
- Modular data access layer supporting seamless profile switching (
- Observability & Error Model:
- Centralized global exception handler (
@RestControllerAdvice) delivering cleanErrorResponseandFieldErrorDetailenvelopes. - Native ECS structured JSON logging with thread-safe SLF4J
MDCcorrelation ID propagation (X-Correlation-Id). - Spring Boot Actuator with custom health indicators (
LedgerConfigHealthIndicator) and Kubernetes liveness/readiness probes.
- Centralized global exception handler (
| Category | Technology |
|---|---|
| Language | Java 25 (Temurin LTS) |
| Framework | Spring Boot 4.1.0 (Web MVC, Security, Data JPA, Actuator) |
| Build Tool | Gradle 9.6.1 (Kotlin DSL build.gradle.kts) |
| Database & ORM | MySQL 8.0, Spring Data JPA, Flyway 11, Redis |
| Infrastructure (IaC) | Terraform (AWS Tokyo ap-northeast-1: ECS Fargate, ECR, RDS MySQL 8.0, OIDC) |
| Security | Spring Security OAuth2 Resource Server (Nimbus JWT) |
| Testing | JUnit 5, AssertJ, Mockito, Spring Security Test, Spring Boot WebMvc Test |
| API Docs | OpenAPI 3.0 / Swagger UI (springdoc-openapi) |
The repository includes a complete Terraform configuration under terraform/ to provision production-grade AWS infrastructure in Tokyo (ap-northeast-1):
- AWS ECR Repository:
450963614191.dkr.ecr.ap-northeast-1.amazonaws.com/spring-bank-ledger - AWS RDS MySQL 8.0:
spring-bank-ledger-db.cn200ami8jvv.ap-northeast-1.rds.amazonaws.com:3306(Free-Tierdb.t3.microwith 20 GBgp3storage and automated Flyway migrations) - AWS ECS Fargate Cluster & Service: Container task (0.25 vCPU, 0.5 GB RAM) running Spring Boot connected to MySQL inside AWS Tokyo VPC with Direct Public IP allocation ($0/mo Free Tier).
- AWS IAM OIDC Provider & Roles: Keyless authentication for GitHub Actions CI/CD workflows (
sts:AssumeRoleWithWebIdentity).
cd terraform
# 0. Export active AWS CLI session credentials to shell environment
eval $(aws configure export-credentials --format env)
# 1. Create S3 Bucket for Remote State (one-time setup)
aws s3api create-bucket \
--bucket spring-bank-ledger-tfstate-sheiksadi \
--region ap-northeast-1 \
--create-bucket-configuration LocationConstraint=ap-northeast-1
# 2. Initialize Terraform (connects to AWS S3 remote backend)
terraform init
# 3. Provision ECR & RDS Database
terraform apply -target=aws_ecr_repository.app_repo -target=aws_db_instance.ledger_db
# 4. Authenticate Docker with AWS ECR Tokyo & Push Image
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 450963614191.dkr.ecr.ap-northeast-1.amazonaws.com
docker build -t spring-bank-ledger .
docker tag spring-bank-ledger:latest 450963614191.dkr.ecr.ap-northeast-1.amazonaws.com/spring-bank-ledger:latest
docker push 450963614191.dkr.ecr.ap-northeast-1.amazonaws.com/spring-bank-ledger:latest
# 5. Provision ECS Fargate Service & OIDC Role
terraform applyThe repository features automated CI and CD pipelines under .github/workflows/:
- Continuous Integration (
.github/workflows/ci.yml):- Spins up a healthy MySQL 8.0 service container in GitHub Actions runner.
- Sets up JDK 25 and executes the full Gradle test suite (
./gradlew test) underSPRING_PROFILES_ACTIVE=mysql.
- Continuous Deployment (
.github/workflows/deploy.yml):- Authenticates seamlessly with AWS IAM using OpenID Connect (OIDC) without long-lived static secrets (
sts:AssumeRoleWithWebIdentity). - Builds and tags the multi-stage Docker image with
$GITHUB_SHAandlatest. - Pushes the image to Amazon ECR Tokyo.
- Executes
aws ecs update-servicefor zero-downtime rolling deployment on AWS ECS Fargate.
- Authenticates seamlessly with AWS IAM using OpenID Connect (OIDC) without long-lived static secrets (
ledger-api/
βββ build.gradle.kts # Gradle Kotlin DSL build script
βββ compose.yml # Docker Compose setup for MySQL 8.0
βββ src/
β βββ main/
β β βββ java/com/paypay/learn/ledger/
β β β βββ Account.java # Domain Record (Immutable value carrier)
β β β βββ AccountEntity.java # JPA Entity for MySQL persistence
β β β βββ AccountController.java # REST endpoints for Account CRUD & Transactions
β β β βββ AccountService.java # Core domain business logic & transaction boundaries
β β β βββ AccountRepository.java # Abstract repository interface
β β β βββ JpaAccountRepository.java # MySQL JPA backed repository (Pessimistic locking)
β β β βββ AuthController.java # Authentication & JWT issuance
β β β βββ SecurityConfig.java # Spring Security Resource Server configuration
β β β βββ IdempotencyFilter.java # Servlet filter intercepting Idempotency-Key
β β β βββ CorrelationIdFilter.java # SLF4J MDC trace filter (X-Correlation-Id)
β β β βββ GlobalExceptionHandler.java # @RestControllerAdvice error mapper
β β βββ resources/
β β βββ application.properties
β β βββ application-mysql.properties
β β βββ application-redis.properties
β β βββ db/migration/
β β βββ V1__create_accounts_table.sql
β β βββ V2__create_idempotency_keys.sql
β βββ test/
β βββ java/com/paypay/learn/ledger/
β βββ AccountServiceTest.java # Unit tests with Mockito
β βββ AccountControllerTest.java # Slice tests (@WebMvcTest)
β βββ AccountIntegrationTest.java # E2E & Concurrency tests (@SpringBootTest)
β βββ AuthIntegrationTest.java # Security JWT integration tests
- Java 25 (or compatible JDK)
- Docker & Docker Compose (for running MySQL 8.0)
- Gradle 9.x (or use the bundled
./gradlew)
From the root repository directory:
docker compose up -dRun locally with the default in-memory profile:
./gradlew bootRunRun with the mysql profile (connects to Docker MySQL & executes Flyway migrations):
SPRING_PROFILES_ACTIVE=mysql ./gradlew bootRunOnce running, open your browser to interact with the API:
- Swagger UI:
http://localhost:8080/swagger-ui/index.html - OpenAPI Spec:
http://localhost:8080/v3/api-docs
The repository features comprehensive unit, slice, and integration test suites:
# Run all automated tests
./gradlew testAccountServiceTest.ConcurrencyTests: Verifies pessimistic locking (SELECT ... FOR UPDATE) under multi-threaded parallel debit requests to guarantee zero balance corruption.AccountIntegrationTest.IdempotencyTests: Asserts single-execution and identical response body replay when retrying requests with matchingIdempotency-Keyheaders.AuthIntegrationTest: Tests JWT issuance, valid bearer token access, and 401 Unauthorized rejection on secured endpoints.
POST /auth/loginβ Authenticate and receive a Bearer JWT token.GET /actuator/healthβ Spring Boot Actuator liveness and readiness health check.
POST /accountsβ Create a new account.GET /accounts/{id}β Fetch account details by ID.GET /accounts?currency=USDβ Search accounts by currency (uses database derived query).POST /accounts/{id}/creditβ Deposit funds into an account (Idempotency-Keysupported).POST /accounts/{id}/debitβ Withdraw funds from an account with balance validation and pessimistic locking.
This project is open-source and available under the MIT License.