A distributed banking application built on Spring Boot and Spring Cloud, demonstrating the integration of Temporal to orchestrate complex, cross-service business workflows with high resilience and automated Saga compensations.
- The Problem
- Architecture Overview
- Temporal Orchestration
- Observability
- Execution and Evidence
- Documentation
Traditional microservices rely on synchronous HTTP calls or simple messaging to handle business logic. This approach struggles with distributed transactions. When a business process spans multiple services (like a loan origination requiring account verification and fund disbursement), handling partial failures, retries, and rollbacks becomes highly complex and error-prone.
This project solves this by integrating Temporal, transforming stateless HTTP interactions into stateful, resilient workflows using the Saga Pattern.
The base architecture relies on the Spring Cloud ecosystem for service discovery and configuration management. The banking domain is split into three main microservices: Accounts, Loans, and Cards.
graph LR
GTW[API Gateway] --> LOA[Loans MS]
GTW --> ACC[Accounts MS]
GTW --> CAR[Cards MS]
LOA -.-> DB_L[(Loans DB)]
ACC -.-> DB_A[(Accounts DB)]
CAR -.-> DB_C[(Cards DB)]
For full details on the initial setup and responsibilities, see the Base Architecture Documentation.
Temporal replaces direct microservice-to-microservice REST calls with orchestrated workflows. The Loans and Accounts services act as Temporal Workers, executing activities coordinated by the Temporal Server.
The core integration is the LoanOriginationWorkflow, which implements a distributed Saga Pattern:
- Validates the customer remotely in the
AccountsMS. - Evaluates the credit risk locally in the
LoansMS. - Pauses for a manual human-in-the-loop approval (Signal) if the loan exceeds $50,000.
- Disburses funds remotely in the
AccountsMS. - Automatically executes compensations (e.g., reverting the loan status) if the disbursement fails.
sequenceDiagram
participant C as Client
participant W as Temporal Workflow
participant A as Accounts MS
participant L as Loans MS
C->>W: POST /applyDistributed
W->>A: Verify Customer
W->>L: Evaluate Risk
alt Amount > $50k
W-->>C: Paused (Awaiting Approval)
C->>W: POST /approveManual
end
W->>L: Update Status (Pending Disbursement)
W->>A: Disburse Funds
alt Fails
W->>L: Saga Compensation (Revert Status)
end
For a deep dive into the code implementation and activities, see the Temporal Integration Documentation.
The project includes a full local observability stack powered by OpenTelemetry, Grafana Alloy, Loki, Tempo, and Prometheus. This ensures that every step of a distributed Temporal workflow can be traced across multiple microservice boundaries using a unified Correlation ID.
See the Observability Documentation for architecture details.
The following steps serve as evidence of the system's capabilities, demonstrating the successful integration of Temporal, the worker configurations, and the observability stack.
Note
The microservices and Temporal have been fully integrated into the Docker Compose environment. You do not need to run them manually via Maven.
Environment Preparation (Click to expand)
[!WARNING] The following commands use the Pacman package manager. Use APT or your OS's native package manager if you are not on an Arch-based Linux system.
- Install Base Dependencies:
sudo pacman -Syu
sudo pacman -S docker docker-compose jdk17-openjdk maven curl- Start Docker:
sudo systemctl enable --now docker
sudo usermod -aG docker $USER- Rebuild Docker Images: Since the code has been updated to integrate Temporal and OpenTelemetry agents, you must rebuild the local Docker images for all microservices before starting the environment. Otherwise, the pre-compiled images from Docker Hub will crash due to missing dependencies.
for ms in configserver eurekaserver accounts loans cards gatewayserver; do
echo "Building $ms..."
(cd $ms && chmod +x ./mvnw && ./mvnw compile jib:dockerBuild)
doneImportant
The gatewayserver is intentionally omitted for these tests to bypass Keycloak configurations. HTTP requests will target ports 8080 and 8090 directly.
Start the entire environment (Observability, Temporal, and Microservices) with a single command:
cd docker-compose/default
docker-compose up -d prometheus tempo grafana alloy minio gateway read write backend temporal configserver eurekaserver accounts loansWait approximately 60 seconds for the environment to initialize and the microservices to register with Eureka.
You can verify the Temporal Web UI is available at http://localhost:8233.
Tip
Use tools like Postman or the curl CLI to perform the HTTP requests.
Prerequisite: Create a test account.
curl -X POST http://localhost:8080/api/create \
-H "Content-Type: application/json" \
-d '{"name":"Jorge Luis Mamani Huarsaya", "email":"jorge.mamani@unsa.edu.pe", "mobileNumber":"9999535350"}'Expected: HTTP 201 Created.
Initiate a loan for $40,000. The system evaluates the risk and disburses the funds automatically.
curl -X POST "http://localhost:8090/api/applyDistributed" \
-H "Content-Type: application/json" \
-d '{"mobileNumber": "9999535350", "totalLoan": 40000}'Expected: HTTP 202 Accepted. The workflow continues asynchronously in the background.
| Temporal UI - Automated Approval (Loan < $50k) |
|---|
![]() |
Initiate a loan for $60,000. The workflow pauses, awaiting a manual Signal to proceed with the disbursement.
# 1. Start Workflow
curl -X POST "http://localhost:8090/api/applyDistributed" \
-H "Content-Type: application/json" \
-d '{"mobileNumber": "9999535350", "totalLoan": 60000}'
# 2. Approve via Signal
curl -X POST "http://localhost:8090/api/approveManual/9999535350"Expected: The first call returns HTTP 202. The second call returns HTTP 200 OK, resuming the workflow.
| Temporal UI - Human-in-the-Loop (Loan > $50k) |
|---|
![]() |
Validation Steps (Click to expand)
1. Temporal UI Validation (http://localhost:8233)
- Event History: Inspect the interleaving of executions between microservices (e.g.,
evaluateCreditRiskcompleting beforedisburseFunds). - Resilience: Stop the
accountsMS during an active workflow. ThedisburseFundsactivity will appear in "Pending Activities" and retry automatically onceaccountsis restarted. - Signals: Check the "Signals" tab to verify the exact moment the
approveManualsignal was received.
2. Grafana Validation (http://localhost:3000)
- Loki (Logs): Navigate to Explore then Loki. Use the query
{container="loans-ms"}. You will see unified logs from both controllers and Temporal workers in real-time, captured natively from Docker by Grafana Alloy. - Tempo (Traces): Navigate to Explore then Tempo. You can query traces using OpenTelemetry Trace IDs printed in the console logs, or look for the
roadMapLearner-correlation-idif you decide to enable and test the API Gateway. Tempo will display a Gantt chart detailing the exact time spent on HTTP requests versus internal database transactions.
3. Prometheus Validation (http://localhost:9090)
- Navigate to Status then Targets. Ensure
accounts,loans, andcardsare marked as UP. - Query
http_server_requests_seconds_countto track incoming API traffic.
| Grafana Observability (Tempo and Loki) |
|---|
![]() |
![]() |
| Document | Description |
|---|---|
| Base Architecture | Initial microservices setup, components, and limitations. |
| Temporal Integration | Workflows, Activities, Saga Pattern, and configuration details. |
| Observability | Metrics, distributed tracing, and centralized logging architecture. |



