This project aims to simulates a real-world cross-border money transfer system. Built to demonstrate advanced Spring Boot patterns, distributed systems architecture, and fintech system design.
- Overview
- Demo Video
- Architecture & Service Interaction
- Tech Stack
- Getting Started
- Testing & Quality
- Project Roadmap
- Key Concepts Covered
This project showcases a distributed system capable of handling idempotent transactions, compliance validation, and asynchronous settlement in a cross-border context.
The system follows a microservices architecture where each service owns its specific domain and data:
flowchart LR
%% ==========================================
%% Nodes
%% ==========================================
FE["Frontend App"]
TS["Transfer Service"]
QS["Quote Service"]
TR["Transfer Repository"]
REDIS[("Redis Cache<br/>(Quotes)")]
US["User Service"]
CS["Compliance Service"]
KAFKA[("Kafka<br/>Event Bus")]
SS["Settlement Service"]
EXT_FX["External FX API"]
%% ======================
%% QUOTE FLOW
%% ======================
FE -- "(1) Request Quote" --> QS
QS -- "(1.1) Get FX Rate" --> EXT_FX
QS -- "(1.2) Cache Quote" --> REDIS
%% ======================
%% TRANSFER REQUEST
%% ======================
FE -- "(2) Create Transfer Request" --> TS
TS -- "(2.1) Verify Idempotency Key" --> TR
TS -- "(2.2) Record Transfer<br/>(INITIATED)" --> TR
TS -- "(2.3) Validate Quote<br/>Cross-check Cache)" --> REDIS
TS -- "(2.4) Validate User<br/>(Sync HTTP)" --> US
TS -- "(2.5) Verify Compliance<br/>(Sync HTTP)" --> CS
TS -- "(2.6) Update Transaction with Quote Details" --> TR
TS -- "(2.7) Advance State = SETTLEMENT_IN_PROGRESS" --> TR
%% ======================
%% EVENT-DRIVEN SETTLEMENT
%% ======================
TS -- "(2.8) Publish TRANSFER_CREATED" --> KAFKA
KAFKA -- "(2.9) Consume TRANSFER_CREATED" --> SS
SS -- "(2.10) Publish TRANSFER_SETTLED / TRANSFER_FAILED" --> KAFKA
KAFKA -- "(2.11) Consume Settlement Result" --> TS
TS -- "(2.12) Update Status = COMPLETED / FAILED" --> TR
%% ==========================================
%% Styling / Class Definitions
%% ==========================================
classDef controller fill:#e1f5fe,stroke:#01579b,stroke-width:1px;
classDef messaging fill:#fff3e0,stroke:#e65100,stroke-width:1px;
classDef repository fill:#f3e5f5,stroke:#4a148c,stroke-width:1px;
classDef external fill:#e8f5e9,stroke:#1b5e20,stroke-width:1px;
classDef infra fill:#eceff1,stroke:#263238,stroke-width:1px;
%% ==========================================
%% Apply Styles
%% ==========================================
class FE controller;
class TR repository;
class EXT_FX external;
class REDIS infra;
class KAFKA messaging;
- User Service: The identity provider. Manages registration, profiles, and provides user validation for transactions.
- Transfer Service: The core orchestrator. Handles POST requests for new transfers, enforces ACID-compliant Idempotency (PostgreSQL), and publishes events to Kafka.
- Compliance Service: A validation gate. Performs automated AML/KYC checks before approving settlement.
- Settlement Service: The financial ledger. Listens for approved transfers via Kafka, simulates clearing, and publishes settlement results.
- Notification Service: The user-facing alert system. Listens for terminal events and dispatches alerts (Email/SMS simulation).
- Quote Caching: The system utilizes Redis to cache FX quotes, ensuring high-performance validation and managing quote expiration.
flowchart TB
User((User)) --> FE["Frontend App"]
%% ======================
%% Infrastructure
%% ======================
subgraph INFRA["**Core Infrastructure**"]
REDIS[("Redis Cache<br/>(Quotes)")]
KAFKA[("Kafka<br/>Event Bus")]
end
EX_API["External FX API"]
%% ======================
%% User Service
%% ======================
subgraph USER_SRV["**User Service**"]
direction TB
UC["UserController<br/>AuthController"]
US["UserService"]
UR["UserRepository"]
UDB[("PostgreSQL<br/>user_db")]
UC --> US --> UR --> UDB
end
%% ======================
%% Transfer Service (Orchestrator)
%% ======================
subgraph TRANSFER_SRV["**Transfer Service**"]
direction TB
subgraph TS_CLIENT["Downstream Clients"]
TSC_CC["Compliance Client"]
TSC_UC["User Client"]
TSC_FX["FX Rate Client"]
end
subgraph TS_MESSAGING["Message Handling"]
T_SUB["CONSUMER<br/>SettlementResultListener"]
T_PUB["PUBLISHER<br/>TransferEventPublisher"]
end
TC["TransferController<br/>QuoteController"]
TS["TransferService<br/>QuoteService"]
TR["TransactionRepository"]
TDB[("PostgreSQL<br/>transfer_db")]
TC --> TS --> TR --> TDB
%% Internal Orchestration
TS --> TSC_CC
TS --> TSC_UC
TS --> TSC_FX
TS --> T_PUB
T_SUB --> TS
end
%% ======================
%% Compliance Service
%% ======================
subgraph COMPLIANCE_SRV["**Compliance Service**"]
CC["ComplianceController"]
CS["ComplianceService"]
CC --> CS
end
%% ======================
%% Async Services
%% ======================
subgraph SETTLEMENT_SRV["**Settlement Service**"]
SL["SettlementListener"]
SS["SettlementService"]
S_PUB["SettlementEventPublisher"]
SL --> SS --> S_PUB
end
subgraph NOTIFICATION_SRV["**Notification Service**"]
NL["NotificationListener"]
end
%% ======================
%% Communication Paths
%% ======================
FE -- "HTTPS" --> TC
%% Sync Orchestration
TSC_UC -- "REST (Sync)" --> UC
TSC_CC -- "REST (Sync)" --> CC
TS -- "Check Cache" --> REDIS
%% External Integration
TSC_FX -- "Fetch Rates" --> EX_API
%% Async Choreography
T_PUB -- "TRANSFER_CREATED" --> KAFKA
KAFKA -- "TRANSFER_CREATED" --> SL
KAFKA -- "TRANSFER_CREATED / TRANSFER_SETTLED / TRANSFER_FAILED" --> NL
S_PUB -- "TRANSFER_SETTLED / TRANSFER_FAILED" --> KAFKA
KAFKA -- "TRANSFER_SETTLED / TRANSFER_FAILED" --> T_SUB
%% ==========================================
%% Styling / Class Definitions
%% ==========================================
classDef controller fill:#e1f5fe,stroke:#01579b,stroke-width:0.5px;
classDef service fill:#fff3e0,stroke:#e65100,stroke-width:0.5px;
classDef repository fill:#f3e5f5,stroke:#4a148c,stroke-width:0.5px;
classDef database fill:#e8f5e9,stroke:#1b5e20,stroke-width:0.5px;
classDef infra fill:#eceff1,stroke:#263238,stroke-width:0.5px;
classDef messaging fill:#ffffb0,stroke:#fbc02d,stroke-width:0.5px;
classDef client fill:#fcefec,stroke:#880e4f,stroke-width:0.5px;
%% ==========================================
%% Apply Styles
%% ==========================================
class UC,TC,CC controller;
class US,TS,CS,SS service;
class UR,TR repository;
class UDB,TDB,REDIS database;
class KAFKA infra;
class T_PUB,T_SUB,SL,S_PUB,NL messaging;
class TS_CLIENT,TS_MESSAGING client;
sequenceDiagram
autonumber
participant FE as Frontend App
participant QS as Quote Service
participant EXT as External FX API
participant REDIS as Redis / Cache
participant TS as Transfer Service
participant TR as Transfer Repository
participant US as User Service
participant CS as Compliance Service
participant KAFKA as Kafka (Event Bus)
participant SS as Settlement Service
participant NS as Notification Service
Note over FE, REDIS: Phase 1: Quote Flow
FE->>QS: Request Quote
QS->>EXT: Fetch FX Rate
QS->>REDIS: Cache Quote
QS-->>FE: Quote Response (Quote ID)
Note over FE, CS: Phase 2: Transfer Request
FE->>TS: Create Transfer Request (Idempotency-Key)
rect rgb(240, 240, 240)
Note right of TS: Idempotency Guard
TS->>TR: Verify Idempotency Key
TR-->>TS: Result (Existing / None)
end
TS->>TR: Record Transfer (INITIATED)
rect rgb(232, 245, 233)
Note over TS, CS: Resilience Layer (Circuit Breaker & Retry)
par Checks
TS->>REDIS: Validate Quote (Cross-check Cache)
TS->>US: Validate User
US-->>TS: 200 OK (Active)
TS->>CS: Verify Compliance
CS-->>TS: 200 OK (Cleared)
end
end
TS->>TR: Update Transaction with Quote Details
TS->>TR: Advance State = SETTLEMENT_IN_PROGRESS
TS-->>FE: 202 Accepted (Transaction ID)
Note over TS, NS: Phase 3: Settlement & Notifications
TS->>KAFKA: Publish TRANSFER_CREATED
par Async Processing
KAFKA->>SS: Consume TRANSFER_CREATED
KAFKA->>NS: Consume TRANSFER_CREATED
NS->>NS: Send "Transfer Received" Email
end
SS->>SS: Process Settlement (External Rails)
SS->>KAFKA: Publish TRANSFER_SETTLED / TRANSFER_FAILED
par Async Updates
KAFKA->>TS: Consume Settlement Result
KAFKA->>NS: Consume Settlement Result
NS->>NS: Send "Transfer Complete/Failed" Email
end
TS->>TR: Update Status = COMPLETED / FAILED
- FX Quote: User requests a currency conversion rate.
- Creation: User submits a transfer with an
Idempotency-Key. - Validation:
transfer-servicecallsuser-service(Sync HTTP) andcompliance-service(Sync HTTP). - Messaging: Once approved,
transfer-servicepublishes aTRANSFER_CREATEDevent to Kafka. - Settlement:
settlement-serviceprocesses the event and publishesTRANSFER_SETTLED. - Terminal State:
transfer-serviceupdates the database toCOMPLETEDandnotification-servicealerts the user.
- Java 21 & Spring Boot 3.5.x
- Spring Data JPA & Flyway (Schema management)
- Resilience4j (Fault tolerance: Circuit Breaker, Retry, Timeout)
- SpringDoc OpenAPI (Swagger UI for API documentation)
- React 19 (TypeScript)
- Vite (Build tool & Dev server)
- PostgreSQL (ACID-compliant relational database)
- Redis (High-performance caching for FX quotes)
- Apache Kafka (Asynchronous event-driven communication)
- Docker & Docker Compose (Container orchestration)
- Testcontainers (Infrastructure-accurate integration testing)
- JUnit 5 & Mockito
- Awaitility (Asynchronous test verification)
- JaCoCo (Code coverage reporting)
Please refer to the CONTRIBUTING.md file for detailed instructions on prerequisites, configuration, and environment setup.
We use Vertical-Slice Integration Testing with Testcontainers to verify the entire stack (REST -> DB -> Redis -> Kafka).
This script runs tests for all 5 services and generates a high-fidelity report with coverage and latency metrics:
./run-all-tests.shTo view detailed Line and Branch coverage:
- Run
./run-all-tests.sh - Open
[service-name]/target/site/jacoco/index.htmlin your browser.
- Phase 1: Initial Spring Boot scaffolds with health checks
- Phase 2: User Service with registration/auth
- Phase 3: Transfer Service with basic CRUD
- Phase 4: Inter-service communication (Sync Feign/REST)
- Phase 5: Resilience (Circuit Breaker, Retry, Timeouts)
- Phase 6: Compliance Service integration & Daily Limit rules
- Phase 7: Event-Driven Messaging (Kafka wiring)
- Phase 8: Async Settlement, FX Quotes & Redis Caching
- Phase 9: Database-level Idempotency
- Phase 10: Observability (Spring Boot Actuator & Metrics)
- Phase 11: API Documentation (Swagger/OpenAPI UI)
- Phase 12: Refactoring & Cleanup
- Phase 13: Unit & Integration Testing with Coverage
- Phase 14: Simple Frontend Demo UI (React & Vite)
- Phase 16: Architecture Diagram Creation
- Phase 17: Migration from H2 to PostgreSQL
- Phase 18: Dockerization of microservices and frontend
- Idempotency: Preventing duplicate charges in a distributed environment using unique database constraints.
- Caching: Low-latency state management using Redis for high-frequency quote lookups.
- Event-Driven Architecture: Choreography vs. Orchestration using Kafka.
- Infrastructure Fidelity: Using Testcontainers to ensure tests reflect production environments.
- Fault Tolerance: Implementing the Circuit Breaker pattern to prevent cascading failures.
- Database Hygiene: Migration from H2 to PostgreSQL for consistent schema behavior.
