A saga orchestration implementation using Temporal framework to manage distributed transactions across microservices.
This project implements the Saga pattern for distributed transactions using Temporal as the orchestration framework. It ensures data consistency across multiple microservices through a sequence of local transactions that can be compensated if any step fails.
- Docker and Docker Compose
- Node.js
- npm
-
Start Temporal Server
cd docker docker-compose -f temporal-docker-compose.yaml up -d -
Start Required Services
cd docker docker-compose -f service-docker.yml up -dThis will start:
- Redis
- PostgreSQL
-
Start Order Service
cd order-microservice cp .env.example .env npm run start:dev -
Start Order Saga Orchestrator
cd order-saga-orchestrator cp .env.example .env npm run start:dev
To test the setup, run:
curl localhost:3000/orders
⚠️ IMPORTANT: Transaction ID Requirements All workflows MUST include a transactionId which serves two critical purposes:
- Acts as an idempotency key
- Serves as the identifier for compensation actions in case of failures
- Must be persisted in the database for all entities created during the saga
This is crucial for maintaining data consistency and enabling proper rollback mechanisms.
Pseudo code
try {
const compensations = [];
const transactionId = uuidv4();
// Step 1: Create Order
compensations.unshift({
fn: () => orderService.cancelOrder(transactionId),
});
const order = await orderService.createOrder({ transactionId, ...orderData });
// Step 2: Create Payment
compensations.unshift({
fn: () => paymentService.cancelPayment(transactionId),
});
const payment = await paymentService.createPayment({ transactionId, ...paymentData });
// Step 3: Create Shipment
compensations.unshift({
fn: () => shipmentService.cancelShipment(transactionId),
});
await shipmentService.createShipment({ transactionId, ...shipmentData });
return order;
} catch (error) {
for (const compensation of compensations) {
await compensation.fn();
}
throw error;
}- Why we use unshift instead of push?
Because we want to ensure that the compensations are executed in reverse order of the steps. This is important to maintain consistency and avoid data corruption.
- A workflow is a unit of actions containing multiple steps (activities)
- Each workflow represents a complete business transaction
- Workflows are defined declaratively and handle the orchestration logic
- Every activity must have a corresponding compensation activity
- Compensation activities are responsible for rolling back changes if the workflow fails
- Activities should be idempotent whenever possible
- Refer to
order-saga-orchestrator/src/domains/order/saga/workflowsfor a sample workflow implementation
