smart-saga-pattern
Version:
A library implementing the Saga pattern for microservice architecture
102 lines (101 loc) • 4.06 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SagaExecutor = void 0;
const types_1 = require("../types");
/**
* Executes a Saga definition
* @template T Type of the input data
* @template R Type of the result data
*/
class SagaExecutor {
/**
* Creates a new SagaExecutor
* @param definition Saga definition to execute
* @param context Initial Saga context
*/
constructor(definition, context) {
this.currentStepIndex = -1;
this.definition = definition;
this.context = { ...context };
}
/**
* Executes the Saga
*/
async execute() {
const steps = this.definition.getSteps();
try {
this.context.status = types_1.SagaStatus.EXECUTING;
for (let i = 0; i < steps.length; i++) {
this.currentStepIndex = i;
const step = steps[i];
try {
// Execute the transaction with the requiresCompensation flag
const requiresCompensation = step.requiresCompensation !== undefined
? step.requiresCompensation
: true;
const result = await step.transaction.execute(this.context.data, requiresCompensation);
// Update context with the result using the step name as the key
this.context.data = {
...this.context.data,
[step.transaction.getName()]: result,
};
}
catch (error) {
// Transaction failed, start compensation
await this.compensate();
return this.context;
}
}
// All steps completed successfully
this.context.status = types_1.SagaStatus.COMPLETED;
this.context.endTime = Date.now();
return this.context;
}
catch (error) {
// Something went wrong during execution or compensation
this.context.status = types_1.SagaStatus.FAILED;
this.context.error =
error instanceof Error ? error.message : String(error);
this.context.endTime = Date.now();
return this.context;
}
}
/**
* Compensates the Saga
*/
async compensate() {
const steps = this.definition.getSteps();
this.context.status = types_1.SagaStatus.COMPENSATING;
try {
// Compensate steps in reverse order
for (let i = this.currentStepIndex; i >= 0; i--) {
const step = steps[i];
// Only compensate if the step requires compensation
if (step.compensation && step.requiresCompensation !== false) {
try {
// Get the transaction result from the context
const transactionResult = this.context.data[step.transaction.getName()];
// Execute compensation with transaction result and name
await step.compensation.execute(this.context.data, transactionResult, step.transaction.getName());
}
catch (error) {
// Compensation failed
this.context.status = types_1.SagaStatus.COMPENSATION_FAILED;
this.context.error =
error instanceof Error ? error.message : String(error);
this.context.endTime = Date.now();
throw error;
}
}
}
// All compensations completed successfully
this.context.status = types_1.SagaStatus.COMPENSATED;
this.context.endTime = Date.now();
}
catch (error) {
// Re-throw the error to be caught by the execute method
throw error;
}
}
}
exports.SagaExecutor = SagaExecutor;