Modules
Modules are Gland's composition boundaries. They register controllers, channels and other modules, and carry the lifecycle hooks.
A module is a class that groups controllers and channels, can import other modules, and can react to the application lifecycle. It is the unit you organise code around and the unit the framework walks at startup.
Defining a module
Section titled “Defining a module”import { Module } from '@glandjs/common'
@Module({ imports: [ProductModule, AuthModule], controllers: [ProductController, HealthController], channels: [ProductChannel, AuditChannel],})export class AppModule {}All three keys are optional.
| Key | Holds | Instantiated |
|---|---|---|
imports |
module classes, dynamic modules, or promises of them | once per graph node |
controllers |
classes with route decorators | once, as a singleton |
channels |
classes with @On() handlers |
once, as a singleton |
Nesting modules
Section titled “Nesting modules”Imports are resolved transitively, and a module is only ever registered once, no matter how many modules import it. A diamond in the graph converges on a single instance.
@Module({ imports: [ProductModule, OrderModule, HealthModule], channels: [Database],})export class AppModule {} AppModule ┌───┴───────┐ ProductModule OrderModule (both import HealthModule, │ │ HealthModule is created once) └─────┬──────┘ HealthModuleDynamic modules
Section titled “Dynamic modules”A dynamic module extends a decorated module at import time. Its controllers and
channels are merged with the base module’s, and its imports are added
alongside the base’s.
import type { DynamicModule } from '@glandjs/common'
@Module({ controllers: [CoreController] })export class CoreModule {}
export function withAuditChannel(audit: Constructor): DynamicModule { return { module: CoreModule, channels: [audit], }}
@Module({ imports: [withAuditChannel(AuditChannel)] })export class AppModule {}Lazy modules
Section titled “Lazy modules”When two modules would import each other, the cycle breaks at module resolution time. Wrap the import in a promise to defer it.
@Module({ imports: [Promise.resolve(() => require('./lazy').LazyModule)],})export class AppModule {}Lifecycle hooks in a module
Section titled “Lifecycle hooks in a module”Any class in the graph can implement the five lifecycle hooks. On a module they describe application-level concerns.
@Module({ imports: [ProductModule] })export class AppModule implements OnModuleInit, OnModuleDestroy, OnAppBootstrap, OnAppShutdown { onModuleInit() {} // after the graph is built onAppBootstrap() {} // everything is bound onAppShutdown(signal?: string) {} // SIGTERM, SIGINT, … onModuleDestroy() {} // last thing that runs}See Lifecycle events for the full contract and the exact order.
Bootstrapping
Section titled “Bootstrapping”The root module is the argument to GlandFactory.create().
import { GlandFactory } from '@glandjs/core'import { AppModule } from './app.module'
const app = await GlandFactory.create(AppModule)@Module(metadata: ModuleMetadata)
Section titled “@Module(metadata: ModuleMetadata)”interface ModuleMetadata<T = any> { imports?: ImportableModule<T>[] controllers?: Constructor<T>[] channels?: Constructor<T>[]}
type ImportableModule<T = any> = | Constructor<T> | DynamicModule<T> | Promise<DynamicModule<T>>GlandFactory.create(root: Constructor): Promise<GlandBroker>
Section titled “GlandFactory.create(root: Constructor): Promise<GlandBroker>”Walks the module graph, builds the container, runs the hooks and binds routes and
channels. Returns a GlandBroker — see @glandjs/core.