### Comprehensive Dependency Injection Example in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md A complete example demonstrating the setup of interfaces, implementations, service modules, and dependency resolution using core-di. Includes scoped and singleton lifetimes. ```typescript import { dependsOn, createServiceCollection, IServiceModule, type IServiceCollection } from '@shellicar/core-di'; // Define the dependency interface abstract class IClock { abstract now(): Date; } // And implementation class DefaultClock implements IClock { now(): Date { return new Date(); } } // Define your interface abstract class IDatePrinter { abstract handle(): string; } // And implementation class DatePrinter implements IDatePrinter { @dependsOn(IClock) public readonly clock!: IClock; handle(): string { return `The time is: ${this.clock.now().toISOString()}`; } } class TimeModule extends IServiceModule { public registerServices(services: IServiceCollection): void { services.register(IClock).to(DefaultClock).singleton(); services.register(IDatePrinter).to(DatePrinter).scoped(); } } // Register and build provider const services = createServiceCollection(); services.registerModules([TimeModule]); const sp = services.buildProvider(); // Optionally create a scope using scope = sp.createScope(); // Resolve the interface const svc = scope.resolve(IDatePrinter); console.log(svc.handle()); ``` -------------------------------- ### Install @shellicar/core-di dependency Source: https://github.com/shellicar/core-di/blob/main/packages/core-di/README.md The installation command for adding the library to your project using the pnpm package manager. ```shell pnpm add @shellicar/core-di ``` -------------------------------- ### Install @shellicar/core-di Source: https://github.com/shellicar/core-di/blob/main/README.md Commands to install the dependency injection library using common Node.js package managers. ```bash npm i --save @shellicar/core-di ``` ```bash pnpm add @shellicar/core-di ``` -------------------------------- ### Type-Safe Registration in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Demonstrates how to register a service implementation for an abstract interface in a type-safe manner using TypeScript. Shows an example of a compile-time error if the registration is incorrect. ```typescript const services = createServiceCollection(); abstract class IAbstract { abstract method(): void; } abstract class Concrete {} services.register(IAbstract).to(Concrete); // ^ Error ``` -------------------------------- ### Overriding Lifetimes for Testing in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Demonstrates how to override the lifetime of a registered service, for example, changing it from singleton to transient for testing scenarios. ```typescript const services = createServiceCollection({ logLevel: LogLevel.Debug }); services.register(IAbstract).to(Concrete).singleton(); const provider = services.buildProvider(); provider.Services.overrideLifetime(IAbstract, Lifetime.Transient); provider.resolve(IAbstract) !== provider.resolve(IAbstract); ``` -------------------------------- ### Multiple Interface Registrations in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Shows how to register a single implementation for multiple interfaces simultaneously. It also verifies that resolving either interface yields the same instance. ```typescript services.register(IAbstract1, IAbstract2).to(Concrete).singleton(); const provider = services.buildProvider(); provider.resolve(IAbstract1) === provider.resolve(IAbstract2); ``` -------------------------------- ### Register and Resolve Services in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Demonstrates how to create a service collection, register an implementation for an abstract class, and resolve the dependency using a provider. ```typescript import { createServiceCollection } from '@shellicar/core-di'; abstract class IAbstract {} class Concrete implements IAbstract {} const services = createServiceCollection(); services.register(IAbstract).to(Concrete); const provider = services.buildProvider(); const svc = provider.resolve(IAbstract); ``` -------------------------------- ### Service Module Registration in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Demonstrates how to organize related service registrations into a `IServiceModule` and register these modules with the service collection. ```typescript class IAbstract {} class Concrete extends IAbstract {} class MyModule implements IServiceModule { public registerServices(services: IServiceCollection): void { services.register(IAbstract).to(Concrete); } } const services = createServiceCollection(); services.registerModules(MyModule); const provider = services.buildProvider(); const svc = provider.resolve(IAbstract); ``` -------------------------------- ### Custom Logger and Log Level Configuration in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Shows how to provide a custom logger implementation or configure the log level when creating the service collection. ```typescript class CustomLogger extends ILogger { public override debug(message?: any, ...optionalParams: any[]): void { // custom implementation } } // Override default logger const services1 = createServiceCollection({ logger: new CustomLogger() }); // Override default log level const services2 = createServiceCollection({ logLevel: LogLevel.Debug }); ``` -------------------------------- ### Factory Method Registration in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Shows how to register a service using a factory method, allowing for custom instantiation logic. The factory function receives a context object to resolve other dependencies. ```typescript services.register(Redis).to(Redis, x => { const options = x.resolve(IRedisOptions); return new Redis({ port: options.port, host: options.host, }); }); ``` -------------------------------- ### Registering Services within a Scope in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Demonstrates registering services specifically within a created scope, allowing for dependencies that are unique to that scope's context. ```typescript using scope = provider.createScope(); scope.Services.register(IContext).to(Context); ``` -------------------------------- ### Register and resolve services in TypeScript Source: https://github.com/shellicar/core-di/blob/main/packages/core-di/README.md Demonstrates how to create a service collection, register a concrete implementation for an abstract class, and resolve the service using a provider. This pattern enables decoupled architecture by injecting dependencies through the service provider. ```typescript import { createServiceCollection } from '@shellicar/core-di'; abstract class IAbstract {} class Concrete implements IAbstract {} const services = createServiceCollection(); services.register(IAbstract).to(Concrete); const provider = services.buildProvider(); const svc = provider.resolve(IAbstract); ``` -------------------------------- ### Singleton Lifetime Registration in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Illustrates how to register a service with a singleton lifetime, ensuring only one instance is created and reused throughout the application's lifecycle. ```typescript services.register(IAbstract).to(Concrete).singleton(); ``` -------------------------------- ### Overriding Registrations for Testing in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Illustrates how to override an existing service registration, commonly used for testing purposes by substituting a mock implementation. ```typescript import { ok } from 'node:assert/strict'; const services = createServiceCollection({ registrationMode: ResolveMultipleMode.LastRegistered }); services.register(IOptions).to(Options); // Later services.register(IOptions).to(MockOptions); const provider = services.buildProvider(); const options = provider.resolve(IOptions); ok(options instanceof MockOptions); ``` -------------------------------- ### Creating Scopes for Per-Request Lifetimes in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Shows how to create a scope using `createScope()`, which is typically used to manage per-request or per-operation lifetimes for services. ```typescript const services = createServiceCollection(); const provider = services.buildProvider(); using scope = provider.createScope(); ``` -------------------------------- ### Create a Service Collection with @shellicar/core-di Source: https://context7.com/shellicar/core-di/llms.txt Demonstrates how to create a new service collection, which acts as the dependency injection container. It shows basic usage with default options and how to configure registration mode and logging level. ```typescript import { createServiceCollection, LogLevel, ResolveMultipleMode } from '@shellicar/core-di'; // Basic usage - creates service collection with default options const services = createServiceCollection(); // With custom options const servicesWithOptions = createServiceCollection({ // Controls behavior when multiple implementations are registered for same identifier registrationMode: ResolveMultipleMode.LastRegistered, // Default: ResolveMultipleMode.Error // Set log level for debugging logLevel: LogLevel.Debug, // Default: LogLevel.Warn }); // Build the provider to resolve services const provider = services.buildProvider(); ``` -------------------------------- ### Scoped Providers with Automatic Disposal (TypeScript) Source: https://context7.com/shellicar/core-di/llms.txt Demonstrates creating isolated scopes for per-request lifetimes using `createScope` and `using`. It shows how `Symbol.dispose` and the `using` keyword automatically manage the disposal of resources like database connections when a scope exits. ```typescript import { createServiceCollection, dependsOn, type IDisposable } from '@shellicar/core-di'; abstract class IDbConnection { abstract query(sql: string): Promise; abstract get isOpen(): boolean; } class DbConnection implements IDbConnection, IDisposable { private open = true; get isOpen() { return this.open; } async query(sql: string): Promise { if (!this.open) throw new Error('Connection closed'); console.log(`Executing: ${sql}`); return []; } [Symbol.dispose](): void { console.log('Closing database connection'); this.open = false; } } abstract class IRequestHandler { abstract handle(): Promise; } class RequestHandler implements IRequestHandler { @dependsOn(IDbConnection) private readonly db!: IDbConnection; async handle(): Promise { await this.db.query('SELECT * FROM users'); } } const services = createServiceCollection(); services.register(IDbConnection).to(DbConnection).scoped(); services.register(IRequestHandler).to(RequestHandler).scoped(); const provider = services.buildProvider(); // Simulate handling HTTP requests async function handleRequest() { // 'using' automatically disposes the scope when block exits using scope = provider.createScope(); const handler = scope.resolve(IRequestHandler); await handler.handle(); const connection = scope.resolve(IDbConnection); console.log(`Connection open: ${connection.isOpen}`); // true // Scope disposed here, connection closed } await handleRequest(); // Output: // Executing: SELECT * FROM users // Connection open: true // Closing database connection ``` -------------------------------- ### Resolve Multiple Implementations with resolveAll in TypeScript Source: https://context7.com/shellicar/core-di/llms.txt Shows how to register multiple implementations for a single service identifier and resolve them all at once. This is ideal for implementing plugin systems, strategy patterns, or aggregate health checks. ```typescript import { createServiceCollection } from '@shellicar/core-di'; abstract class IHealthCheck { abstract readonly name: string; abstract check(): Promise<{ healthy: boolean; message: string }>; } class DatabaseHealthCheck implements IHealthCheck { readonly name = 'Database'; async check() { return { healthy: true, message: 'Connected' }; } } class RedisHealthCheck implements IHealthCheck { readonly name = 'Redis'; async check() { return { healthy: true, message: 'Cache available' }; } } const services = createServiceCollection(); services.register(IHealthCheck).to(DatabaseHealthCheck); services.register(IHealthCheck).to(RedisHealthCheck); const provider = services.buildProvider(); const healthChecks = provider.resolveAll(IHealthCheck); const results = await Promise.all(healthChecks.map(async (hc) => hc.check())); ``` -------------------------------- ### Implement custom factory methods for services Source: https://context7.com/shellicar/core-di/llms.txt Shows how to register services using a factory function when complex initialization logic or external dependencies are required. The factory receives a resolver instance to access other registered services. ```typescript import { createServiceCollection } from '@shellicar/core-di'; abstract class IRedisOptions { abstract readonly host: string; abstract readonly port: number; } class RedisOptions implements IRedisOptions { readonly host = process.env.REDIS_HOST ?? 'localhost'; readonly port = parseInt(process.env.REDIS_PORT ?? '6379'); } abstract class IRedisClient { abstract get(key: string): Promise; abstract set(key: string, value: string): Promise; } class RedisClient implements IRedisClient { constructor(private readonly host: string, private readonly port: number) {} async get(key: string): Promise { console.log(`GET ${key} from ${this.host}:${this.port}`); return null; } async set(key: string, value: string): Promise { console.log(`SET ${key}=${value} on ${this.host}:${this.port}`); } } const services = createServiceCollection(); services.register(IRedisOptions).to(RedisOptions).singleton(); services.register(IRedisClient).to(IRedisClient, (resolver) => { const options = resolver.resolve(IRedisOptions); return new RedisClient(options.host, options.port); }).singleton(); const provider = services.buildProvider(); const redis = provider.resolve(IRedisClient); await redis.set('key', 'value'); ``` -------------------------------- ### Multiple Interface Registration (TypeScript) Source: https://context7.com/shellicar/core-di/llms.txt Illustrates registering a single implementation class to satisfy multiple abstract interface types. This ensures that all resolved instances for these different interfaces point to the exact same singleton object, promoting instance sharing. ```typescript import { createServiceCollection } from '@shellicar/core-di'; abstract class IReader { abstract read(): string; } abstract class IWriter { abstract write(data: string): void; } abstract class IReadWriter { abstract read(): string; abstract write(data: string): void; } class FileSystem implements IReader, IWriter, IReadWriter { private data = ''; read(): string { console.log('Reading from FileSystem'); return this.data; } write(data: string): void { console.log(`Writing to FileSystem: ${data}`); this.data = data; } } const services = createServiceCollection(); // Register single implementation for multiple interfaces services.register(IReader, IWriter, IReadWriter).to(FileSystem).singleton(); const provider = services.buildProvider(); // All resolve to the same instance const reader = provider.resolve(IReader); const writer = provider.resolve(IWriter); const readWriter = provider.resolve(IReadWriter); writer.write('Hello World'); console.log(reader.read()); // 'Hello World' console.log(reader === writer); // true (same instance) console.log(writer === readWriter); // true (same instance) ``` -------------------------------- ### Organize Services with IServiceModule in TypeScript Source: https://context7.com/shellicar/core-di/llms.txt Demonstrates how to group service registrations into reusable modules using the IServiceModule interface. This approach improves maintainability by encapsulating related service logic and dependencies. ```typescript import { createServiceCollection, dependsOn, type IServiceCollection, type IServiceModule } from '@shellicar/core-di'; abstract class IEmailService { abstract send(to: string, subject: string, body: string): Promise; } abstract class INotificationService { abstract notify(userId: string, message: string): Promise; } class SmtpEmailService implements IEmailService { async send(to: string, subject: string, body: string): Promise { console.log(`Sending email to ${to}: ${subject}`); } } class NotificationService implements INotificationService { @dependsOn(IEmailService) private readonly email!: IEmailService; async notify(userId: string, message: string): Promise { await this.email.send(`${userId}@example.com`, 'Notification', message); } } class NotificationModule implements IServiceModule { registerServices(services: IServiceCollection): void { services.register(IEmailService).to(SmtpEmailService).singleton(); services.register(INotificationService).to(NotificationService); } } const services = createServiceCollection(); services.registerModules(NotificationModule); const provider = services.buildProvider(); ``` -------------------------------- ### Implement Custom Logger (TypeScript) Source: https://context7.com/shellicar/core-di/llms.txt Shows how to provide a custom logger implementation to the core-di container. This allows for detailed debugging and monitoring of the service resolution process using a custom logging format, such as JSON. ```typescript import { createServiceCollection, ILogger, LogLevel } from '@shellicar/core-di'; // Custom logger implementation class JsonLogger extends ILogger { public override debug(message?: any, ...optionalParams: any[]): void { console.log(JSON.stringify({ level: 'debug', message, params: optionalParams })); } public override info(message?: any, ...optionalParams: any[]): void { console.log(JSON.stringify({ level: 'info', message, params: optionalParams })); } public override warn(message?: any, ...optionalParams: any[]): void { console.log(JSON.stringify({ level: 'warn', message, params: optionalParams })); } public override error(message?: any, ...optionalParams: any[]): void { console.log(JSON.stringify({ level: 'error', message, params: optionalParams })); } } // Use custom logger const servicesWithCustomLogger = createServiceCollection({ logger: new JsonLogger() }); // Or just change the built-in log level const servicesWithDebug = createServiceCollection({ logLevel: LogLevel.Debug }); ``` -------------------------------- ### Register and Resolve Services with @shellicar/core-di Source: https://context7.com/shellicar/core-di/llms.txt Illustrates how to register a service identifier (abstract class) with its concrete implementation using a fluent API for type-safety. It also shows how to resolve the registered service. ```typescript import { createServiceCollection } from '@shellicar/core-di'; // Define interface as abstract class abstract class IUserService { abstract getUser(id: string): Promise; } // Concrete implementation class UserService implements IUserService { async getUser(id: string): Promise { return { id, name: 'John Doe' }; } } const services = createServiceCollection(); // Register the service - TypeScript ensures UserService implements IUserService services.register(IUserService).to(UserService); const provider = services.buildProvider(); const userService = provider.resolve(IUserService); // userService is typed as IUserService const user = await userService.getUser('123'); console.log(user); // { id: '123', name: 'John Doe' } ``` -------------------------------- ### Manage Service Lifetimes with @shellicar/core-di Source: https://context7.com/shellicar/core-di/llms.txt Explains and demonstrates how to manage service lifetimes (singleton, scoped, transient) in @shellicar/core-di. This controls instance creation and reuse across the application or within specific scopes. ```typescript import { createServiceCollection } from '@shellicar/core-di'; abstract class IConfigService { abstract get(key: string): string; } class ConfigService implements IConfigService { get(key: string): string { return process.env[key] ?? ''; } } abstract class IRequestContext { abstract readonly requestId: string; } class RequestContext implements IRequestContext { readonly requestId = crypto.randomUUID(); } abstract class ILogger { abstract log(message: string): void; } class Logger implements ILogger { log(message: string): void { console.log(message); } } const services = createServiceCollection(); // Singleton: Same instance for entire application services.register(IConfigService).to(ConfigService).singleton(); // Scoped: Same instance within a scope (e.g., per HTTP request) services.register(IRequestContext).to(RequestContext).scoped(); // Transient: New instance every time (default) services.register(ILogger).to(Logger).transient(); const provider = services.buildProvider(); // Singleton - same instance const config1 = provider.resolve(IConfigService); const config2 = provider.resolve(IConfigService); console.log(config1 === config2); // true // Scoped - same within scope, different across scopes using scope1 = provider.createScope(); using scope2 = provider.createScope(); const ctx1a = scope1.resolve(IRequestContext); const ctx1b = scope1.resolve(IRequestContext); const ctx2 = scope2.resolve(IRequestContext); console.log(ctx1a === ctx1b); // true (same scope) console.log(ctx1a === ctx2); // false (different scopes) // Transient - always new instance const logger1 = provider.resolve(ILogger); const logger2 = provider.resolve(ILogger); console.log(logger1 === logger2); // false ``` -------------------------------- ### Override Registration for Testing (TypeScript) Source: https://context7.com/shellicar/core-di/llms.txt Shows how to override existing service registrations, particularly useful for testing. By configuring `ResolveMultipleMode.LastRegistered`, subsequent registrations for the same service identifier will replace previous ones, allowing mock implementations to be used. ```typescript import { createServiceCollection, ResolveMultipleMode, type IServiceCollection } from '@shellicar/core-di'; abstract class IPaymentGateway { abstract charge(amount: number): Promise<{ success: boolean; transactionId: string }>; } class StripeGateway implements IPaymentGateway { async charge(amount: number) { // Real API call to Stripe return { success: true, transactionId: `stripe_${Date.now()}` }; } } class MockPaymentGateway implements IPaymentGateway { async charge(amount: number) { // Mock for testing - no real API call return { success: true, transactionId: 'mock_12345' }; } } // Production registration function registerProductionServices(services: IServiceCollection) { services.register(IPaymentGateway).to(StripeGateway).singleton(); } // Test registration - overrides production function registerTestServices(services: IServiceCollection) { services.register(IPaymentGateway).to(MockPaymentGateway).singleton(); } // Create with LastRegistered mode to allow overrides const services = createServiceCollection({ registrationMode: ResolveMultipleMode.LastRegistered }); registerProductionServices(services); registerTestServices(services); // Overrides StripeGateway const provider = services.buildProvider(); const gateway = provider.resolve(IPaymentGateway); const result = await gateway.charge(100); console.log(result); // { success: true, transactionId: 'mock_12345' } console.log(gateway instanceof MockPaymentGateway); // true ``` -------------------------------- ### Type-Safe Resolution in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Illustrates how to resolve a registered service from a provider in a type-safe way using TypeScript. The resolved service is assigned to a variable with its correct type. ```typescript const provider = services.buildProvider(); const svc = provider.resolve(IMyService); // ^ IMyService ``` -------------------------------- ### Property Injection with Decorators in TypeScript Source: https://github.com/shellicar/core-di/blob/main/README.md Demonstrates using the `@dependsOn` decorator for property injection, simplifying dependency definition within a class. The decorator automatically injects the required dependency. ```typescript abstract class IDependency {} class Service implements IService { @dependsOn(IDependency) private readonly dependency!: IDependency; } ``` -------------------------------- ### Detect Circular Dependencies (TypeScript) Source: https://context7.com/shellicar/core-di/llms.txt Illustrates the circular dependency detection capabilities of the core-di library. It shows how to handle self-dependencies and circular dependencies (A -> B -> A) with specific error types like `SelfDependencyError` and `CircularDependencyError`. Generic `ServiceError` handling is also demonstrated. ```typescript import { createServiceCollection, dependsOn, CircularDependencyError, SelfDependencyError, ServiceError } from '@shellicar/core-di'; // Self-dependency example abstract class ISelfReferencing { abstract getValue(): string; } class SelfReferencing implements ISelfReferencing { @dependsOn(ISelfReferencing) private readonly self!: ISelfReferencing; getValue(): string { return 'value'; } } // Circular dependency example (A -> B -> A) abstract class IServiceA { abstract doA(): string; } abstract class IServiceB { abstract doB(): string; } class ServiceA implements IServiceA { @dependsOn(IServiceB) private readonly b!: IServiceB; doA(): string { return 'A calls ' + this.b.doB(); } } class ServiceB implements IServiceB { @dependsOn(IServiceA) private readonly a!: IServiceA; doB(): string { return 'B calls ' + this.a.doA(); } } // Handle self-dependency { const services = createServiceCollection(); services.register(ISelfReferencing).to(SelfReferencing); const provider = services.buildProvider(); try { provider.resolve(ISelfReferencing); } catch (error) { if (error instanceof SelfDependencyError) { console.log('Self-dependency detected:', error.message); // Output: Self-dependency detected: Service depending on itself } } } // Handle circular dependency { const services = createServiceCollection(); services.register(IServiceA).to(ServiceA); services.register(IServiceB).to(ServiceB); const provider = services.buildProvider(); try { provider.resolve(IServiceA); } catch (error) { if (error instanceof CircularDependencyError) { console.log('Circular dependency detected:', error.message); // Output: Circular dependency detected: Circular dependency detected while resolving: ServiceA } } } // Generic error handling { const services = createServiceCollection(); services.register(IServiceA).to(ServiceA); services.register(IServiceB).to(ServiceB); const provider = services.buildProvider(); try { provider.resolve(IServiceA); } catch (error) { if (error instanceof ServiceError) { console.log(`Service error [${error.name}]: ${error.message}`); } } } ``` -------------------------------- ### Override Service Lifetime for Testing (TypeScript) Source: https://context7.com/shellicar/core-di/llms.txt Demonstrates how to override the lifetime of a registered service, specifically changing a singleton to transient. This is crucial for testing singleton services in isolation by ensuring each resolution creates a new instance. ```typescript import { createServiceCollection, Lifetime, LogLevel } from '@shellicar/core-di'; abstract class ICounter { abstract increment(): number; } class Counter implements ICounter { private count = 0; increment(): number { return ++this.count; } } const services = createServiceCollection({ logLevel: LogLevel.Debug }); services.register(ICounter).to(Counter).singleton(); const provider = services.buildProvider(); // Override singleton to transient for test isolation provider.Services.overrideLifetime(ICounter, Lifetime.Transient); // Now each resolve creates a new instance const counter1 = provider.resolve(ICounter); const counter2 = provider.resolve(ICounter); console.log(counter1.increment()); // 1 console.log(counter1.increment()); // 2 console.log(counter2.increment()); // 1 (different instance) console.log(counter1 === counter2); // false ``` -------------------------------- ### Inject dependencies with @dependsOn decorator Source: https://context7.com/shellicar/core-di/llms.txt Demonstrates how to use the @dependsOn decorator to inject service dependencies into class properties. This approach simplifies dependency management by automatically resolving registered services during container resolution. ```typescript import { createServiceCollection, dependsOn } from '@shellicar/core-di'; abstract class IDatabase { abstract query(sql: string): Promise; } class Database implements IDatabase { async query(sql: string): Promise { return [{ id: 1, name: 'Test' }]; } } abstract class ICache { abstract get(key: string): string | undefined; abstract set(key: string, value: string): void; } class MemoryCache implements ICache { private store = new Map(); get(key: string) { return this.store.get(key); } set(key: string, value: string) { this.store.set(key, value); } } abstract class IUserRepository { abstract findById(id: string): Promise; } class UserRepository implements IUserRepository { @dependsOn(IDatabase) private readonly db!: IDatabase; @dependsOn(ICache) private readonly cache!: ICache; async findById(id: string): Promise { const cached = this.cache.get(`user:${id}`); if (cached) return JSON.parse(cached); const [user] = await this.db.query(`SELECT * FROM users WHERE id = '${id}'`); if (user) this.cache.set(`user:${id}`, JSON.stringify(user)); return user ?? null; } } const services = createServiceCollection(); services.register(IDatabase).to(Database).singleton(); services.register(ICache).to(MemoryCache).singleton(); services.register(IUserRepository).to(UserRepository); const provider = services.buildProvider(); const userRepo = provider.resolve(IUserRepository); const user = await userRepo.findById('1'); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.