Tổng quan – NestJS

Modules

Module là một class được chú thích bằng @Module() decorator. @Module() decorator cung cấp metadata mà Nest sử dụng để tổ chức cấu trúc ứng dụng.

Mỗi ứng dụng có ít nhất một module, một module gốc. Module gốc là điểm khởi đầu Nest sử dụng để xây dựng biểu đồ ứng dụng – cấu trúc dữ liệu nội bộ mà Nest sử dụng để giải quyết các mối quan hệ và phụ thuộc của Module và các Providers. Trong khi các ứng dụng rất nhỏ về mặt lý thuyết có thể chỉ có module gốc, đây không phải là trường hợp điển hình. Chúng tôi muốn nhấn mạnh rằng các module được khuyến khích sử dụng như một cách hiệu quả để tổ chức các thành phần của bạn. Do đó, đối với hầu hết các ứng dụng, kết quả của kiến trúc sẽ sử dụng nhiều module, mỗi module đóng gói một bộ khả năng có liên quan chặt chẽ.

@Module() decorator nhận một đối tượng duy nhất có thuộc tính mô tả module:

providersCác Provider sẽ được khởi tạo bởi injector Nest và ít nhất có thể được chia sẻ trên module này
controllersTập hợp các controller được xác định trong module này phải được khởi tạo
importsDanh sách các module được imported để xuất các providers được yêu cầu trong module này.
exportsTập hợp con các providers được cung cấp bởi module này và sẽ có sẵn trong các module khác mà import module này.

Module đóng gói các providers theo mặc định. Điều này có nghĩa là không thể inject providers mà ko trực tiếp là 1 phần của module hiện tại cũng như đã exported từ các module đã imported. Do đó, bạn có thể coi các providers đã exported từ một module là giao diện công khai của module hay API.

Feature modules

CatsControllerCatsService thuộc cùng một miền ứng dụng. Vì chúng có liên quan chặt chẽ với nhau, nên chuyển chúng vào một module tính năng sẽ rất hợp lý. Module tính năng chỉ đơn giản là tổ chức mã phù hợp với một tính năng cụ thể, giữ cho mã có tổ chức và thiết lập ranh giới rõ ràng. Điều này giúp chúng tôi quản lý sự phức tạp và phát triển với các nguyên tắc SOLID, đặc biệt là khi quy mô của ứng dụng và / hoặc nhóm ngày càng tăng.

Để chứng minh điều này, chúng tôi sẽ tạo CatsModule.

cats/cats.module.ts

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}

HINT Để tạo một module sử dụng CLI, câu lệnh thực thi $ nest g module cats.

Ở trên, chúng tôi đã xác định CatsModule trong tệp cat.module.ts và chuyển mọi thứ liên quan đến module này vào thư mục cats. Điều cuối cùng chúng ta cần làm là import module này vào mô-đun gốc (AppModule, được định nghĩa trong tệp app.module.ts).

app.module.ts

import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule {}

Đây là cấu trúc thư mục của chúng ta giống như thế này:

Shared modules

Trong Nest, các module là singleton theo mặc định, và do đó bạn có thể chia sẻ cùng một thực thể của bất kỳ provider nào giữa nhiều module một cách dễ dàng.

Mỗi module tự động là một module được chia sẻ. Sau khi được tạo, nó có thể được sử dụng lại bởi bất kỳ module nào. Hãy tưởng tượng rằng chúng ta muốn chia sẻ một thực thể của CatsService giữa một số module khác. Để làm điều đó, trước tiên chúng ta cần export provider CatsService bằng cách thêm nó vào mảng exports của module, như được hiển thị bên dưới:

cats.module.ts

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService]
})
export class CatsModule {}

Bây giờ bất kỳ module nào import CatsModule đều có quyền truy cập vào CatsService và sẽ chia sẻ cùng một instance với tất cả các module khác cũng import nó.

Module re-exporting

Như đã thấy ở trên, Module có thể export các providers nội bộ của chúng. Ngoài ra, họ có thể re-import các module mà họ đã import. Trong ví dụ bên dưới, CommonModule được imported và export từ CoreModule, làm cho nó có sẵn cho các module khác nhập module này.

@Module({
  imports: [CommonModule],
  exports: [CommonModule],
})
export class CoreModule {}

Dependency injection

Một class module cũng có thể inject các providers (ví dụ: cho mục đích cấu hình):

cats.module.ts

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {
  constructor(private catsService: CatsService) {}
}

Tuy nhiên, bản thân các lớp module không thể được injected làm provider do phụ thuộc vòng tròn.

Global modules

Nếu bạn phải import cùng một tập hợp các module ở khắp mọi nơi, nó có thể trở nên tẻ nhạt. Không giống như trong Nest, Angular providers được đăng ký trên phạm vi toàn cục. Sau khi được xác định, chúng có sẵn ở khắp mọi nơi. Nest, tuy nhiên, đóng gói các providers bên trong phạm vi module. Bạn không thể sử dụng các providers của ở nơi khác mà không nhập module đóng gói trước.

Khi bạn muốn cung cấp một tập hợp các providers nên có sẵn ở mọi nơi ngay lập tức (ví dụ: helpers, database connections, v.v.), hãy đặt module toàn cục với @Global() decorator.

import { Module, Global } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Global()
@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],
})
export class CatsModule {}

@Global() làm cho module có phạm vi toàn cục. Module toàn cục chỉ nên được đăng ký một lần, thường là module gốc hoặc module lõi. Trong ví dụ trên, provider CatsService sẽ phổ biến và các module muốn đưa service vào sẽ không cần nhập CatsModule trong mảng imports của chúng.

HINT: Làm cho mọi thứ toàn cục không phải là một quyết định thiết kế tốt. Các module toàn cục có sẵn để giảm số lượng bản mẫu cần thiết. Mảng imports nói chung là cách ưa thích để cung cấp API của module cho người tiêu dùng.

Dynamic modules

Hệ thống module Nest bao gồm một tính năng mạnh mẽ được gọi là module động. Tính năng này cho phép bạn dễ dàng tạo các module có thể tùy chỉnh có thể đăng ký và cấu hình động các providers. Các mô-đun động được đề cập rộng rãi ở đây. Trong chương này, chúng tôi sẽ trình bày tổng quan ngắn gọn để hoàn thành phần giới thiệu về các module.

Sau đây là một ví dụ về định nghĩa mô-đun động cho DatabaseModule:

import { Module, DynamicModule } from '@nestjs/common';
import { createDatabaseProviders } from './database.providers';
import { Connection } from './connection.provider';

@Module({
  providers: [Connection],
})
export class DatabaseModule {
  static forRoot(entities = [], options?): DynamicModule {
    const providers = createDatabaseProviders(options, entities);
    return {
      module: DatabaseModule,
      providers: providers,
      exports: providers,
    };
  }
}

HINT Phương thức forRoot() có thể trả về một module động đồng hộ hoặc ko đồng bộ. (i.e., qua một Promise).

Mô-đun này xác định provider kết nối theo mặc định (trong metadata @Module() decorator), nhưng ngoài ra – tùy thuộc vào các thực thể và đối tượng tùy chọn được truyền vào phương thức forRoot() – hiển thị một tập hợp các provider, ví dụ, repositories Lưu ý rằng các thuộc tính do mô-đun động trả về sẽ mở rộng (thay vì ghi đè) metadata mô-đun cơ sở được xác định trong @Module() decorator. Đó là cách cả provider kết nối được khai báo tĩnh và nhà cung cấp kho lưu trữ được tạo động đều được xuất từ mô-đun.

Nếu bạn muốn đăng ký một mô-đun động trong phạm vi toàn cục, hãy đặt thuộc tính global thành true.

{
  global: true,
  module: DatabaseModule,
  providers: providers,
  exports: providers,
}

WARNING Như đã đề cập ở trên, biến mọi thứ trở nên toàn cục không phải là một quyết định thiết kế tốt.

DatabaseModule có thể được nhập và cấu hình theo cách sau:

import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';
import { User } from './users/entities/user.entity';

@Module({
  imports: [DatabaseModule.forRoot([User])],
})
export class AppModule {}

Nếu bạn muốn lần lượt xuất lại một mô-đun động, bạn có thể bỏ qua lệnh gọi phương thức forRoot() trong mảng exports:

import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';
import { User } from './users/entities/user.entity';

@Module({
  imports: [DatabaseModule.forRoot([User])],
  exports: [DatabaseModule],
})
export class AppModule {}

Chương  Dynamic modules Mô-đun động bao gồm chủ đề này chi tiết hơn và bao gồm một ví dụ hoạt động  working example.

One Comment on “Tổng quan – NestJS

  1. Nếu một interceptor handle() được gọi ở bất kỳ đâu trên đường đi, phương thức create() sẽ không được thực thi. => Nếu một interceptor handle() “”không”” được gọi ở bất kỳ đâu trên đường đi, phương thức create() sẽ không được thực thi.

Leave a Reply

%d bloggers like this: