UNPKG

saga-transaction-lib

Version:

A TypeScript library for implementing the Saga pattern to manage distributed transactions and complex workflows

221 lines (169 loc) 5.09 kB
# Saga Transaction Library A TypeScript library for implementing the Saga pattern to manage distributed transactions and complex workflows. ## Features - TypeScript-first implementation - Flexible and extensible architecture - Built-in error handling and compensation - Customizable logging - Transaction context management - Decorators for fine-grained control over step execution and compensation ## Installation ```bash npm install saga-transaction-lib ``` ## Configuration ### TypeScript Configuration To use the decorators in this library, you need to enable experimental decorators in your `tsconfig.json`: ```json { "compilerOptions": { "experimentalDecorators": true ... other options } } ``` ## Basic Usage ```typescript import { Saga, IStep } from 'saga-transaction-lib'; // 1. Define your context type interface MyContext { // your context properties } // 2. Implement your steps class MyStep implements IStep<MyContext> { name = 'My Step'; async invoke(context: MyContext): Promise<void> { // Implementation } async compensate(context: MyContext): Promise<void> { // Compensation logic } } // 3. Create and execute the saga async function runMyTransaction() { const saga = new Saga<MyContext>(); const context: MyContext = { /* ... */ }; const steps: IStep<MyContext>[] = [new MyStep()]; try { const result = await saga.execute(context, steps); console.log('Transaction completed successfully'); } catch (error) { console.error('Transaction failed:', error); } } ``` ## Advanced Usage ### Using Decorators The library provides two decorators for fine-grained control over step execution and compensation: #### @BeforeInvoke & @BeforeCompensate() - The @BeforeInvoke decorator checks if the step can proceed by returning true. If it does, the invoke method is executed. - The @BeforeCompensate decorator does not require any condition checks and will directly execute the compensation logic when necessary. ```typescript import { BeforeInvoke, IStep } from 'saga-transaction-lib'; class MyStep implements IStep<MyContext> { name = 'My Step'; @BeforeInvoke<MyStep, MyContext>(async ({ instance, context }) => { console.log(`Preparing to execute ${instance.name}`); // Perform checks or preparations return true; // Return true to proceed, false to skip this step }) async invoke(context: MyContext): Promise<void> { // Step implementation } @BeforeCompensate() async compensate(context: MyContext): Promise<void> { // Compensation logic } } ``` ### Custom Logger ```typescript import { ILogger } from 'saga-transaction-lib'; class CustomLogger implements ILogger { log(message: string): void { // Custom implementation } error(message: string, error?: Error): void { // Custom implementation } warn(message: string): void { // Custom implementation } debug(message: string): void { // Custom implementation } } ``` ### Custom Error Handler ```typescript import { IErrorHandler, TransactionContext } from 'saga-transaction-lib'; class CustomErrorHandler<TContext> implements IErrorHandler<TransactionContext<TContext>> { async handleError( error: Error, context: TransactionContext<TContext> ): Promise<void> { // Custom error handling logic } } ``` ### NestJS Saga Module ```typescript // saga.module.ts import { Module, DynamicModule } from '@nestjs/common'; import { Saga, SagaOptions } from 'saga-transaction-lib'; @Module({}) export class SagaModule { static forRoot<TContext>(options?: SagaOptions<TContext>): DynamicModule { return { module: SagaModule, providers: [ { provide: 'SAGA', useFactory: () => new Saga<TContext>(options), }, ], exports: ['SAGA'], }; } } // app.module.ts @Module({ imports: [ SagaModule.forRoot<YourContextType>({ logger: new CustomLogger(), }), ], }) export class AppModule {} // your.service.ts @Injectable() export class YourService { constructor( @Inject('SAGA') private readonly saga: Saga<YourContextType> ) {} async executeTransaction() { // Use saga here } } ``` ## Best Practices 1. Keep steps atomic and focused 2. Implement proper compensation logic 3. Use meaningful step names 4. Handle errors appropriately 5. Use typing to ensure compile-time safety 6. Use decorators to add pre-execution and pre-compensation logic when needed 7. Ensure `isBreakStep` is properly managed in your step implementations ## Example Usage A detailed example can be found in the [`main.ts`](https://github.com/dngiang2003/saga-transaction-lib/blob/master/example/main.ts) file located in the `example` folder. This example demonstrates how to implement a transaction step using both the `@BeforeInvoke` and `@BeforeCompensate` decorators. ## License MIT ## Contributing Contributions are welcome! Please read our contributing guidelines and submit pull requests. ## Support If you have any questions or need help, please open an issue on GitHub.