@leandromatos/provider-manager
v0.0.0-snapshot.20241014.4
Published
Provider Manager
Downloads
65
Readme
Provider Manager 📦
Provider Manager is a lightweight dependency injection container designed to manage class constructors and factory functions. It allows you to register and resolve dependencies easily and supports both class-based providers and factory-based providers, giving you flexibility in how you handle your dependencies.
Installation
Install the package using yarn or any other package manager:
yarn add @leandromatos/provider-manager
Features
- Register providers using class constructors or factory functions.
- Resolve dependencies using string identifiers or directly via class constructors or factory functions.
- Automatic dependency resolution with
@Inject
. - Simple and intuitive API for managing dependencies.
Decorators
@Injectable
The @Injectable
decorator marks a class or factory function as a provider that can be managed by the ProviderManager
. Without this decorator, the ProviderManager
will not be able to handle the class or factory.
@Injectable()
class Logger {
log(message: string): void {
console.log(`[LOG]: ${message}`)
}
}
In this example, Logger
is marked as @Injectable
, which allows it to be registered and resolved by the ProviderManager
.
@Inject
The @Inject
decorator injects dependencies into the constructor of a class-based provider. It tells ProviderManager
what dependencies should be resolved when creating a class instance.
@Injectable()
class UserService {
constructor(
@Inject(Logger) private readonly logger: Logger,
@Inject(ConfigService) private readonly configService: Config
) {}
}
In this example, the UserService
class depends on Logger
and ConfigService
. The @Inject
decorator ensures that when UserService
is resolved, Logger
and ConfigService
will be automatically injected.
Usage
Here’s a basic example that demonstrates how to use ProviderManager
to register and resolve different types of providers.
Example: Basic Setup
import { Inject, Injectable, ProviderManager } from '@leandromatos/provider-manager'
// A factory-based provider
type Config = {
appName: string
version: string
}
@Injectable()
const ConfigService = (): Config => ({
appName: 'MyApp',
version: '1.0.0',
})
// A class-based provider
@Injectable()
class Logger {
log(message: string): void {
console.log(`[LOG]: ${message}`)
}
}
// A class-based provider
@Injectable()
class DatabaseConnection {
constructor(private connectionString: string) {}
connect(): void {
console.log(`Connected to database: ${this.connectionString}`)
}
}
// A class-based provider with dependencies
@Injectable()
class UserService {
constructor(
@Inject(Logger) private readonly logger: Logger,
@Inject(ConfigService) private readonly configService: Config
) {}
createUser(name: string): void {
this.logger.log(`Creating user: ${name} for ${this.configService.appName}`)
}
}
// Initialize the ProviderManager
const providerManager = new ProviderManager()
Registering Providers
You can register providers using constructors, factories, or string identifiers. Below, we'll demonstrate registering various types of providers:
Class-based Provider
A class-based provider is a simple class that can be instantiated by the ProviderManager
.
@Injectable()
class Logger {
log(message: string): void {
console.log(`[LOG]: ${message}`)
}
}
// Register a class-based provider
providerManager.registerProvider(Logger)
Factory-based Provider with String Identifier
A factory-based provider is a function that returns an instance of a class or object. You can assign a custom string identifier for later retrieval when using a factory.
@Injectable()
class DatabaseConnection {
constructor(private connectionString: string) {}
connect(): void {
console.log(`Connected to database: ${this.connectionString}`)
}
}
// Register a factory-based provider with a string identifier
providerManager.registerProvider(() => new DatabaseConnection('postgresql://main-database:5432'), 'MAIN_DATABASE')
Factory-based Provider without Identifier
You can also register a factory-based provider without using an identifier. In this case, the function itself acts as the identifier.
type Config = {
appName: string
version: string
}
@Injectable()
const ConfigService = (): Config => ({
appName: 'MyApp',
version: '1.0.0',
})
// Register a factory-based provider without an identifier
providerManager.registerProvider(ConfigService)
Class-based Provider with Dependencies
When you have a class-based provider that depends on other providers, you can use the @Inject
decorator to inject these dependencies.
@Injectable()
class UserService {
constructor(
@Inject(Logger) private readonly logger: Logger,
@Inject(ConfigService) private readonly configService: Config
) {}
createUser(name: string): void {
this.logger.log(`Creating user: ${name} for ${this.configService.appName}`)
}
}
// Register a class-based provider with dependencies
providerManager
.registerProvider(Logger)
.registerProvider(ConfigService)
.registerProvider(UserService)
Resolving Providers
Once the providers are registered, you can resolve them through the ProviderManager
using either their constructor, factory, or string identifier.
Resolving a Class-based Provider
const logger = providerManager.get(Logger)
logger.log('Hello from Logger')
Resolving a Factory-based Provider with String Identifier
const mainDatabaseConnection = providerManager.get('MAIN_DATABASE')
mainDatabaseConnection.connect()
Resolving a Factory-based Provider without Identifier
const configService = providerManager.get(ConfigService)
console.log(configService.appName) // Output: MyApp
Resolving a Class-based Provider with Dependencies
When you resolve a class-based provider with dependencies, the ProviderManager
automatically injects the necessary dependencies.
const userService = providerManager.get(UserService)
userService.createUser('John Doe')
Example Summary
Here's a summary example that demonstrates registering and resolving multiple types of providers:
const providerManager = new ProviderManager()
providerManager
.registerProvider(Logger) // Class-based provider
.registerProvider(() => new DatabaseConnection('postgresql://main-database:5432'), 'MAIN_DATABASE') // Factory-based provider with identifier
.registerProvider(() => new DatabaseConnection('postgresql://another-database:5432'), 'ANOTHER_DATABASE') // Factory-based provider with identifier
.registerProvider(ConfigService) // Factory-based provider without identifier
.registerProvider(UserService) // Class-based provider with dependencies
// Resolve providers
const logger = providerManager.get(Logger)
const mainDatabaseConnection = providerManager.get('MAIN_DATABASE')
const anotherDatabaseConnection = providerManager.get('ANOTHER_DATABASE')
const configService = providerManager.get(ConfigService)
const userService = providerManager.get(UserService)
// Using resolved services
logger.log('Application started')
mainDatabaseConnection.connect()
anotherDatabaseConnection.connect()
console.log(`App Name: ${configService.appName}`)
userService.createUser('John Doe')
API
registerProvider<T>(provider: Constructor<T> | Factory<T>, identifier?: Constructor<T> | string): this
Registers a provider (either a class constructor or a factory) in the ProviderManager
.
- provider: The provider to register (constructor or factory).
- identifier: An optional string or constructor to identify the provider. If not provided, the class name is used for constructors.
get<T>(identifier: string | Provider<T>): T
Retrieves a provider instance by its string identifier, constructor, or factory.
- identifier: The identifier (string or provider) to resolve.
License
This project is licensed under the MIT License. See the LICENSE file for details.