Skip to content

Instantly share code, notes, and snippets.

@jckw
Created February 19, 2024 14:38
Show Gist options
  • Save jckw/10949a19ac74b896f65b27528ff3c43d to your computer and use it in GitHub Desktop.
Save jckw/10949a19ac74b896f65b27528ff3c43d to your computer and use it in GitHub Desktop.
A simple Saga implementation with TypeScript
class Saga<TContext> {
private operations: Array<{
up: (context: any) => Promise<any>
down: (context: any) => Promise<void>
}> = []
add<TNewContext>(
up: (context: TContext) => Promise<TNewContext>,
down: (context: Partial<TContext & TNewContext>) => Promise<void>
): Saga<TContext & TNewContext> {
this.operations.push({ up, down })
return this as unknown as Saga<TContext & TNewContext>
}
async execute(initialContext: Partial<TContext> = {}): Promise<void> {
const completed: Array<{
down: (context: Partial<TContext>) => Promise<void>
}> = []
let context = initialContext
try {
for (const { up, down } of this.operations) {
const result = await up(context)
context = { ...context, ...result }
completed.push({ down })
}
} catch (error) {
console.error("Error encountered, rolling back...", error)
// Rollback in reverse order
for (const { down } of completed.reverse()) {
try {
await down(context)
} catch (rollbackError) {
console.error("Rollback failed", rollbackError)
// Handle rollback failure, potentially a manual intervention alert
}
}
// Rethrow or handle the original error as needed
throw error
}
}
}
// Usage example
const saga = new Saga()
saga
.add(
async () => {
console.log("Step 1")
return { step1: "done" }
},
async () => {
console.log("Rollback step 1")
}
)
.add(
async (context) => {
console.log("Step 2", context.step1)
return { step2: "done" }
},
async () => {
console.log("Rollback step 2")
}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment