-
Notifications
You must be signed in to change notification settings - Fork 0
Module
The @Module decorator is a crucial part of the Nexus IoC library, used to define modules in your application. A module is a class annotated with a @Module decorator, and it is used to organize and group related components, services, and providers. Modules help in managing the application's structure, making it more maintainable and scalable.
- Organize Application Structure: Modules allow you to logically group related components, services, and providers together.
- Dependency Injection Management: Modules help in managing and injecting dependencies across different parts of the application.
- Modularity: Encourages a modular architecture, making the application easier to understand, develop, and maintain.
A module is defined by decorating a class with the @Module decorator. The decorator takes an object with metadata properties that describe the module, including its providers, imports, and exports.
- imports: An array of modules that are required by this module. These modules will be imported and their exported providers will be available in the current module.
- providers: An array of providers that will be instantiated by the IoC container and can be injected into other components within the module.
- exports: An array of providers that will be available to other modules that import this module.
import { NsModule, Injectable } from '@nexus-ioc/core';
@Injectable()
class AppService {
getHello(): string {
return 'Hello World!';
}
}
@NsModule({
providers: [AppService],
})
export class AppModule {}
In this example, we define a module that depends on another module. This is achieved using the imports property.
import { NsModule, Injectable } from '@nexus-ioc/core';
@Injectable()
class DependencyService {
getDependency(): string {
return 'Dependency';
}
}
@Module({
providers: [DependencyService],
})
export class DependencyModule {}
@Injectable()
class AppService {
constructor(private readonly dependencyService: DependencyService) {}
getHello(): string {
return `Hello ${this.dependencyService.getDependency()}`;
}
}
@NsModule({
imports: [DependencyModule],
providers: [AppService],
})
export class AppModule {}
In this example, we define a module that exports its providers to be used by other modules.
import { NsModule, Injectable } from '@nexus-ioc/core';
@Injectable()
class SharedService {
getShared(): string {
return 'Shared Service';
}
}
@Module({
providers: [SharedService],
exports: [SharedService],
})
export class SharedModule {}
@Injectable()
class AppService {
constructor(private readonly sharedService: SharedService) {}
getHello(): string {
return `Hello from ${this.sharedService.getShared()}`;
}
}
@NsModule({
imports: [SharedModule],
providers: [AppService],
})
export class AppModule {}
In Nexus IoC, modules and providers have a specific scope, controlling where and how dependencies can be accessed. Dependencies are resolved by looking at a module's imports and exports, ensuring clear boundaries between different parts of the application.
Modules can share their providers with other modules by using imports and exports. This system allows you to control which services are available across different modules. If a module needs a provider from another module, it must import that module. If a module wants to share a provider with other modules, it must export that provider.
import { NsModule, Injectable, Inject } from '@nexus-ioc/core';
@Injectable()
class UserService {
getUser() {
return 'User';
}
}
@NsModule({
providers: [UserService],
exports: [UserService], // UserService is made available to other modules
})
export class UserModule {}
@Injectable()
class OrderService {
constructor(@Inject(UserService) private userService: UserService) {}
getOrderDetails() {
return `Order for ${this.userService.getUser()}`;
}
}
@NsModule({
imports: [UserModule], // UserModule is imported to access UserService
providers: [OrderService],
})
export class OrderModule {}
-
UserModule
defines and exportsUserService
, making it available to other modules. -
OrderModule
imports UserModule to gain access toUserService
.
When Nexus IoC tries to resolve a dependency, it looks only in the imports of the current module. If the required provider isn't found there, it won't search in other modules further up the hierarchy. This ensures that each module only has access to the providers it specifically imports, maintaining modularity and clear boundaries.
For example
@NsModule({
imports: [OrderModule], // AppModule doesn't import UserModule
})
export class AppModule {}
Here, AppModule
imports OrderModule
, but cannot access UserService
because it hasn't imported UserModule
. Even though UserModule
is part of the application, it isn't directly available to AppModule
.
Nexus IoC supports re-exporting providers. This means that if a module imports another module and exports it, the providers from the imported module can be passed along to other modules without needing to import them again.
@NsModule({
imports: [UserModule], // Import UserModule
exports: [UserModule], // Re-export UserModule's providers
})
export class SharedModule {}
@Module({
imports: [SharedModule], // AppModule imports SharedModule and gains access to UserService
})
export class AppModule {}
-
SharedModule
imports and re-exports UserModule. -
AppModule
importsSharedModule
and now has access toUserService
through the re-export.
- Imports: A module must import another module to access its providers.
- Exports: A module must export its providers to share them with other modules.
- Closest Imports: Dependencies are resolved only in the nearest module's imports.
- Re-Exporting: Modules can re-export providers to simplify sharing across different parts of the application.
In Nexus IoC, the @Global decorator is used to mark a module as global, which means that its providers will be available across the entire application without needing to explicitly import the module in other modules. Once a module is marked as global, all of its exported providers can be injected anywhere in the application, regardless of the module boundaries.
- Simplify dependency management: By marking a module as global, you make its providers available throughout the application without repeatedly importing the same module.
- Useful for shared services: Common services like logging, configuration, or utilities can be defined in a global module so that they are accessible from any module in the app.
import { NsModule, Global, Injectable } from '@nexus-ioc/core';
@Injectable()
class ConfigService {
getConfig() {
return 'App Config';
}
}
@Global()
@NsModule({
providers: [ConfigService],
exports: [ConfigService], // Export to make the service available globally
})
export class ConfigModule {}
In this example:
-
ConfigService
is provided in theConfigModule
. - The
@Global()
decorator makesConfigModule
global, soConfigService
is now available throughout the entire app without importingConfigModule
in every other module.
- Global Availability: Any provider exported by a global module can be injected into any part of the application without importing the module.
- Single Registration: You only need to register a global module once, and it will be automatically available across all other modules.
- Typical Use Cases: Shared services like configuration, logging, or common utilities are ideal candidates for being part of a global module.
- Use the
@Global
decorator for services that need to be accessed across multiple modules without redundancy. - Avoid overusing global modules as it can make dependency management harder to track in very large applications. Only mark modules as global if their services are truly application-wide concerns.
In Nexus IoC, the forRoot patterns are used when you want to configure a module globally or initialize it with some options. These patterns are especially useful for modules that need dynamic configuration or initialization, like database modules, authentication, or configuration providers.
import { NsModule } from '@nexus-ioc/core';
interface ConfigOptions {
apiUrl: string;
}
@NsModule({})
export class ConfigModule {
static forRoot(options: ConfigOptions) {
return {
module: ConfigModule,
providers: [
{
provide: 'CONFIG_OPTIONS',
useValue: options, // Pass the options to the providers
},
{
provide: 'ASYNC_CONFIG_OPTIONS',
useFactory: async () => {
return options;
}
}
],
exports: ['CONFIG_OPTIONS', 'ASYNC_CONFIG_OPTIONS'], // Export so it can be used globally
};
}
}
-
ConfigModule
has a forRoot method that accepts a configuration object (ConfigOptions
) and registers the options as a provider. - The options can be accessed throughout the app using dependency injection.
import { NsModule } from '@nexus-ioc/core';
import { ConfigModule } from './config.module';
@NsModule({
imports: [
ConfigModule.forRoot({ apiUrl: 'https://api.example.com' }), // Pass options to forRoot
],
})
export class AppModule {}
forFeature
are used to define feature modules that provide specific providers and services to the application. These methods are particularly useful for creating modules that may require additional configuration or initialization, allowing you to organize your codebase into distinct areas of concern.
The forFeature
method is used to create a feature module that registers providers and services related to a specific feature of the application. This method allows you to pass in configuration options that are used to set up the feature providers.
- Feature-specific configuration: Provides a way to register providers that are specific to a certain feature within the application.
- Modular design: Helps organize code by separating concerns and functionalities into different modules.
import { NsModule, Injectable } from '@nexus-ioc/core';
interface UserFeatureOptions {
userRole: string;
}
@Injectable()
class UserService {
constructor(private options: UserFeatureOptions) {}
getUserRole() {
return this.options.userRole;
}
}
@NsModule({})
export class UserModule {
static forFeature(options: UserFeatureOptions) {
return {
module: UserModule,
providers: [
{
provide: UserService,
useFactory: () => new UserService(options), // Create UserService with options. can be sync or async
},
],
exports: [UserService], // Export UserService for use in other modules
};
}
}
-
UserModule
defines a forFeature method that acceptsUserFeatureOptions
. - The
UserService
is created using the provided options, which can be accessed by injecting UserService into other components.
import { NsModule } from '@nexus-ioc/core';
import { UserModule } from './user.module';
@NsModule({
imports: [
UserModule.forFeature({ userRole: 'admin' }), // Pass feature-specific options
],
})
export class AppModule {}