Should be a generic plugin that would enable seamless propagation of database transaction using a Proxy provider.
Should be generic enough to support writing custom adapters for ORMs and libraries that support callback-type transactions (interactive transactions).
Example (with prisma):
Registration
@Module({
imports: [
ClsModule.forRoot({
middleware: { mount: true },
plugins: [
new ClsPluginTransactional({
// option to choose a built-in adapter or a custom one
adapter: new PrismaAdapter({
// custom adapter options.
// This one accepts the injection token for the Prisma client
prismaClientToken: Prisma
})
})
]
})
]
providers: [CallingService, CalledService]
})
export class AppModule {}
Usage
@Injectable()
class CallingService {
constructor(
private readonly calledService: CalledService,
private readonly txHost: TransactionHost<Prisma>,
) {}
// this wraps the method with `prisma.$transaction(( txClient ) => { ... })`
// and stores the `txClient` in CLS
@Transactional()
async startTransaction() {
// due to magic of AsyncLocalStorage, both of these calls will receive the same txClient
await this.calledService.doWork()
await this.calledService.doOtherWork()
}
// Optionally, we're able to pass in adapter-specific transaction options
@Transactional<PrismaAdapter>({
isolationLevel: Prisma.TransactionIsolationLevel.Serializable
})
async otherTransactionalWork() {
await this.calledService.doWork()
await this.calledService.doOtherWork()
}
async imperativeTransaction() {
// alternatively, we can inect the `TransactionHost` and use
// the imperative API to start a CLS-enabled transaction
await this.txHost.runWithTransaction(async () => {
await this.calledService.doWork()
await this.calledService.doOtherWork()
})
}
}
@Injectable()
class CalledService {
constructor(
// This inject a CLS-powered Proxy provider that refers to the
// tx client that is currently in the CLS.
// If no client is in CLS, it uses the non-transactional one (or throws, this might be configurable)
private readonly txHost: TransactionHost<Prisma>
) {}
async doWork() {
this.txHost.tx.someTable.create({ something: ... })
}
async doOtherWork() {
this.txHost.tx.someOtherTable.create({ somethingElse: ... })
}
}
Should be a generic plugin that would enable seamless propagation of database transaction using a Proxy provider. Should be generic enough to support writing custom adapters for ORMs and libraries that support callback-type transactions (interactive transactions).
Example (with
prisma
):Registration
Usage