DI Integration
The ProxyTransactionContextResolver class provides an ITransactionContextResolver whose underlying resolver is resolved from a DI container.
The proxy supports the ITransactionContextResolver operations:
use(adapterName?)returns a transaction runner that resolves the resolver from the container for each operation.run(...)delegates to the default adapter's transaction runner.afterCommit(asyncInvocable, settings?)resolves the resolver from the container and delegates to it.
The proxy resolves the resolver lazily, so it supports the container's configured lifetimes. Scoped registrations must be used inside container.run(). Initialize the resolver from the container before using it.
Initial configuration
Set up the MongoDB and SQLite databases and transaction adapters. Create the container, register the MongoDB adapter as primary (default) and the SQLite adapter as secondary, initialize the resolver from its onInit hook, and wrap it with ProxyTransactionContextResolver:
1. Set up the MongoDB database
import { MongoClient } from "mongodb";
export const mongoClient = new MongoClient("mongodb://localhost:27017");
export const mongoDatabase = mongoClient.db("DATABASE_NAME");
2. Create the MongoDB transaction adapter
import { MongodbTransactionAdapter } from "eridu-tech/transaction-context/mongodb-transaction-adapter";
import { mongoClient, mongoDatabase } from "./mongodb-database.js";
export const mongodbTransactionAdapter = new MongodbTransactionAdapter({
client: mongoClient,
database: mongoDatabase,
});
3. Set up the SQLite database
import Sqlite from "better-sqlite3";
import { Kysely, SqliteDialect } from "kysely";
export const sqliteDatabase = new Kysely<any>({
dialect: new SqliteDialect({ database: new Sqlite(":memory:") }),
});
4. Create the SQLite transaction adapter
import { KyselyTransactionAdapter } from "eridu-tech/transaction-context/kysely-transaction-adapter";
import { sqliteDatabase } from "./sqlite-database.js";
export const sqliteTransactionAdapter = new KyselyTransactionAdapter({
database: sqliteDatabase,
});
5. Create the dependency injection container
import { Container } from "eridu-tech/di";
import { ExecutionContext } from "eridu-tech/execution-context";
import { AlsExecutionContextAdapter } from "eridu-tech/execution-context/als-execution-context-adapter";
export const executionContext = new ExecutionContext(
new AlsExecutionContextAdapter(),
);
export const container = new Container({ executionContext });
6. Register and initialize the transaction context resolver
import { genericToken, LIFETIME } from "eridu-tech/di/contracts";
import { ProxyTransactionContextResolver } from "eridu-tech/transaction-context/di";
import { TransactionContextResolver } from "eridu-tech/transaction-context";
import { container, executionContext } from "./container.js";
import { mongodbTransactionAdapter } from "./mongodb-transaction-adapter.js";
import { sqliteTransactionAdapter } from "./sqlite-transaction-adapter.js";
type Adapters = "primary" | "secondary";
const resolverToken = genericToken<TransactionContextResolver<Adapters>>(
"transactionContextResolver",
);
container.registerFactory({
token: resolverToken,
factory: () =>
new TransactionContextResolver<Adapters>({
adapters: {
primary: mongodbTransactionAdapter,
secondary: sqliteTransactionAdapter,
},
defaultAdapter: "primary",
executionContext,
}),
onInit: async (resolver) => {
await resolver.init();
},
deps: {},
lifetime: LIFETIME.SINGLETON,
});
// Create the proxy before container.init()
export const transactionContext = new ProxyTransactionContextResolver<Adapters>(
{
container,
resolverToken,
},
);
await container.init();
Usage
1. Using the default adapter
import { transactionContext } from "./transaction-context-resolver-di.js";
// Runs inside a transaction using the default (primary) adapter
await transactionContext.run(async () => {
// Perform transactional work here
await transactionContext.afterCommit(async () => {
console.log("Transaction committed");
});
});
2. Specifying an adapter explicitly
import { transactionContext } from "./transaction-context-resolver-di.js";
// Runs inside a transaction using the secondary adapter
await transactionContext.use("secondary").run(async () => {
// Perform transactional work here
});
Further information
For further information refer to eridu-tech/transaction-context API docs.