A small banking-style API showcasing good practices:
- Global exception handling with
@RestControllerAdvice+ProblemDetail - Business errors with stable error codes + args (for i18n + debugging)
- Validation errors for DTOs and params
traceIdsurfaced (Micrometer Tracing)- Idempotent money transfer using
Idempotency-Key
mvn spring-boot:runH2 console: http://localhost:8080/h2 (JDBC URL: jdbc:h2:mem:banking)
A=$(curl -s -X POST localhost:8080/api/accounts \
-H 'Content-Type: application/json' \
-d '{"ownerName":"Alice","currency":"INR"}' | jq -r .id)
B=$(curl -s -X POST localhost:8080/api/accounts \
-H 'Content-Type: application/json' \
-d '{"ownerName":"Bob","currency":"INR"}' | jq -r .id)
echo $A $Bcurl -s -X POST localhost:8080/api/accounts/$A/deposit \
-H 'Content-Type: application/json' \
-d '{"amount":1000.00}' | jqKEY=$(uuidgen)
curl -s -X POST localhost:8080/api/transfers \
-H "Idempotency-Key: $KEY" \
-H 'Content-Type: application/json' \
-d "{"fromAccountId":"$A","toAccountId":"$B","amount":250.00}" | jq
# Repeating with same key + same body returns the same transfer (safe retries)
curl -s -X POST localhost:8080/api/transfers \
-H "Idempotency-Key: $KEY" \
-H 'Content-Type: application/json' \
-d "{"fromAccountId":"$A","toAccountId":"$B","amount":250.00}" | jqcurl -s -X POST localhost:8080/api/transfers \
-H "Idempotency-Key: $(uuidgen)" \
-H 'Content-Type: application/json' \
-d "{"fromAccountId":"$A","toAccountId":"$B","amount":999999.00}" | jq- Money uses
BigDecimalwith 4 decimal scale (demo). @Versionenables optimistic locking on accounts.- Messages are in
src/main/resources/messages.properties.