Skip to main content

Modules trong NestJS

Module là khối xây dựng cơ bản trong NestJS. Mỗi ứng dụng NestJS có ít nhất một module - Root Module (AppModule), và thường được chia nhỏ thành nhiều modules khác để tổ chức code một cách logic và có thể tái sử dụng.

Khái niệm Module

Module là một class được trang trí bằng @Module() decorator. Nó nhóm các components có liên quan lại với nhau (controllers, providers, imports, exports) thành một đơn vị cohesive.

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}

Cấu trúc của Module

Module được định nghĩa qua đối tượng metadata có các thuộc tính sau:

1. providers

Các service hoặc injectable được NestJS tạo instance và quản lý dependency injection.

@Module({
providers: [UsersService, UserRepository],
})
export class UsersModule {}

2. controllers

Các controller xử lý incoming requests và trả về responses.

@Module({
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}

3. imports

Các modules khác cần được import để sử dụng providers của chúng.

@Module({
imports: [DatabaseModule, AuthModule],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}

4. exports

Các providers từ module này được expose để modules khác có thể import và sử dụng.

@Module({
providers: [UsersService],
exports: [UsersService], // Cho phép modules khác import UsersService
})
export class UsersModule {}

Ví dụ thực tế

Module đơn giản

import { Module } from '@nestjs/common';
import { ProductsController } from './products.controller';
import { ProductsService } from './products.service';
import { ProductRepository } from './products.repository';

@Module({
controllers: [ProductsController],
providers: [ProductsService, ProductRepository],
})
export class ProductsModule {}

Module với imports và exports

import { Module } from '@nestjs/common';
import { DatabaseModule } from '../database/database.module';
import { OrdersController } from './orders.controller';
import { OrdersService } from './orders.service';

@Module({
imports: [DatabaseModule],
controllers: [OrdersController],
providers: [OrdersService],
exports: [OrdersService],
})
export class OrdersModule {}

Root Module (AppModule)

import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module';
import { ProductsModule } from './products/products.module';
import { OrdersModule } from './orders/orders.module';
import { DatabaseModule } from './database/database.module';

@Module({
imports: [
DatabaseModule,
UsersModule,
ProductsModule,
OrdersModule,
],
})
export class AppModule {}

Module Exports và Reusability

Khi bạn export một provider, nó có thể được inject vào modules khác mà không cần tạo instance mới.

// auth.module.ts
@Module({
providers: [AuthService, JwtService],
exports: [AuthService], // Chỉ export AuthService
})
export class AuthModule {}

// users.module.ts
@Module({
imports: [AuthModule], // Import AuthModule
providers: [UsersService],
})
export class UsersModule {
constructor(private authService: AuthService) {} // Có thể sử dụng AuthService
}

Global Modules

Bạn có thể tạo Global Module để tránh phải import nó ở mọi nơi. Sử dụng @Global() decorator:

import { Module, Global } from '@nestjs/common';
import { ConfigService } from './config.service';

@Global()
@Module({
providers: [ConfigService],
exports: [ConfigService],
})
export class ConfigModule {}

Sau đó, bạn có thể sử dụng ConfigService ở bất kỳ module nào mà không cần import ConfigModule:

@Module({
controllers: [AppController],
providers: [ConfigService], // Không cần import ConfigModule
})
export class AppModule {}

Dynamic Modules

Dynamic Modules cho phép bạn truyền cấu hình khi import module:

// database.module.ts
@Module({})
export class DatabaseModule {
static forRoot(options: DatabaseOptions): DynamicModule {
return {
module: DatabaseModule,
providers: [
{
provide: 'DATABASE_OPTIONS',
useValue: options,
},
DatabaseService,
],
exports: [DatabaseService],
};
}
}

// app.module.ts
@Module({
imports: [
DatabaseModule.forRoot({
host: 'localhost',
port: 5432,
}),
],
})
export class AppModule {}

Best Practices

1. Tổ chức theo Features

Chia ứng dụng thành các modules theo tính năng (users, products, orders, ...), không theo loại (controllers, services, ...).

src/
├── users/
│ ├── users.controller.ts
│ ├── users.service.ts
│ ├── users.module.ts
│ └── users.repository.ts
├── products/
│ ├── products.controller.ts
│ ├── products.service.ts
│ └── products.module.ts
└── app.module.ts

2. Dependency Management

Quản lý dependencies một cách rõ ràng. Chỉ export những gì cần thiết:

@Module({
providers: [
UsersService,
UserRepository,
UserValidator, // Internal, không export
],
exports: [UsersService], // Chỉ export service
})
export class UsersModule {}

3. Tránh Circular Dependencies

Nếu ModuleA import ModuleB và ModuleB import ModuleA, sẽ gây lỗi circular dependency.

// ❌ Sai - Circular dependency
// users.module.ts imports orders.module
// orders.module.ts imports users.module

// ✅ Đúng - Tạo shared module
@Module({
providers: [SharedService],
exports: [SharedService],
})
export class SharedModule {}

@Module({
imports: [SharedModule],
providers: [UsersService],
})
export class UsersModule {}

@Module({
imports: [SharedModule],
providers: [OrdersService],
})
export class OrdersModule {}

4. Sử dụng Lazy Loading (khi cần)

Để cải thiện performance, bạn có thể lazy load modules:

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();

Lifecycle của Module

Module Initialization

OnModuleInit()

Application Bootstrap

OnApplicationBootstrap()

Application Running

(Graceful Shutdown)

OnModuleDestroy()

OnApplicationShutdown()

Sử dụng Lifecycle Hooks để thực hiện initialization logic:

@Module({
providers: [DatabaseService],
})
export class DatabaseModule implements OnModuleInit {
constructor(private dbService: DatabaseService) {}

onModuleInit() {
this.dbService.connect();
}
}

Ví dụ Toàn Diện

// config.module.ts
import { Module, Global } from '@nestjs/common';
import { ConfigService } from './config.service';

@Global()
@Module({
providers: [ConfigService],
exports: [ConfigService],
})
export class ConfigModule {}

// database.module.ts
import { Module } from '@nestjs/common';
import { DatabaseService } from './database.service';

@Module({
providers: [DatabaseService],
exports: [DatabaseService],
})
export class DatabaseModule {}

// users.module.ts
import { Module } from '@nestjs/common';
import { DatabaseModule } from '../database/database.module';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
imports: [DatabaseModule],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from './config/config.module';
import { DatabaseModule } from './database/database.module';
import { UsersModule } from './users/users.module';
import { AppController } from './app.controller';

@Module({
imports: [
ConfigModule,
DatabaseModule,
UsersModule,
],
controllers: [AppController],
})
export class AppModule {}

Kết luận

Modules là nền tảng của kiến trúc NestJS. Chúng giúp:

  • Tổ chức code thành các đơn vị logic độc lập
  • Quản lý dependencies một cách rõ ràng
  • Tái sử dụng code giữa các phần khác nhau của ứng dụng
  • Dễ dàng testing và bảo trì

Bằng cách chia nhỏ ứng dụng thành các modules có tổ chức tốt, bạn sẽ tạo ra code dễ bảo trì, mở rộng và test hơn.