NestJS公式ドキュメント翻訳
原文
Modules | NestJS - A progressive Node.js web framework
モジュール
モジュールは@Module()
デコレータが付けられたクラスです。@Module()
デコレータはNestがアプリケーション構造を整理するために使用するメタデータを提供します。
各アプリケーションには少なくとも1つのモジュール、ルート(root)モジュールがあります。ルートモジュールはNestがアプリケーショングラフの構築の始点です。Nestはモジュールとプロバイダの関係と依存関係を解決するために使用する内部データ構造です。非常に小さなアプリケーションでは、理論的にはルートモジュールがありさえすればよいですが、これは一般的なケースではありません。コンポーネントを整理する効果的な方法としてモジュール化を強くお勧めします。ほとんどのアプリケーションではアーキテクチャは複数のモジュールを使用し、各モジュールは密接に関連する機能ごとにカプセル化します。
@Module()
デコレータはプロパティがモジュールを記述する単一のオブジェクトを受け取ります
providers |
Nestインジェクタによってインスタンス化され、少なくともそのモジュール全体で共有されるプロバイダ |
controllers |
インスタンス化する必要があるそのモジュールで定義されたコントローラのセット |
imports |
そのモジュールで必要なプロバイダをエクスポートするインポートされたモジュールのリスト |
exports |
そのモジュールによって提供され、そのモジュールをインポートする他のモジュールで利用できるproviders のサブセット |
モジュールはデフォルトでプロバイダをカプセル化します。これは、現在のモジュールの直接的な一部ではなく、インポートされたモジュールからエクスポートされていないプロバイダを注入することは不可能であることを意味します。したがって、モジュールからエクスポートされたプロバイダは、モジュールのパブリックインターフェースまたはAPIと見なすことができます。
機能モジュール
CatsController
とCatsService
は同じアプリケーションドメインに属します。これらは密接に関連しているため、機能モジュールに移動することは理にかなっています。機能モジュールは特定の機能に関連するコードを整理し明確な境界を確立します。これにより、アプリケーションやチームの規模が大きくなっても、複雑性を管理しつつSOLIDの原則に従って開発することができます。
これを実証するためにCatsModule
を作成します。
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
CLIを使用してモジュールを作成するには、単に$nest g module cats
コマンドを実行します。
上記では、cats.module.ts
ファイルでCatsModule
を定義し、このモジュールに関連するすべてのものをcats
ディレクトリに移動しました。最後に、このモジュールをルートモジュール(app.module.ts
ファイルで定義されているAppModule
)にインポートします。
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule {}
ディレクトリ構造は次のようになります。
src
├── cats
│ ├── dto
│ │ └── create-cat.dto.ts
│ ├── interfaces
│ │ └── cat.interface.ts
│ ├── cats.service.ts
│ ├── cats.controller.ts
│ └── cats.module.ts
├── app.module.ts
└── main.ts
共有モジュール
Nestではモジュールはデフォルトでシングルトンなので、複数のモジュール間で任意のプロバイダの同じインスタンスを簡単に共有することができます。
すべてのモジュールは自動的に共有モジュールです。作成すると任意のモジュールで再利用できます。CatsService
のインスタンスを他のいくつかのモジュール間で共有したいとします。これを行うには、最初にCatsService
プロバイダをモジュールのexports
配列に追加して、以下のようにエクスポートする必要があります。
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 {}
これでCatsModule
をインポートするモジュールはCatsService
にアクセスでき、同じインスタンスをそれをインポートする他のすべてのモジュールで共有します。
モジュールの再エクスポート
上記のように、モジュールは内部プロバイダをエクスポートできます。さらに、インポートしたモジュールを再エクスポートできます。次の例では、CommonModule
がCoreModule
にインポートおよびエクスポートされるため、このモジュールをインポートする他のモジュールで使用できます。
@Module({
imports: [CommonModule],
exports: [CommonModule],
})
export class CoreModule {}
依存性の注入
モジュールクラスはプロバイダを注入することもできます(構成上のためなど)。
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 readonly catsService: CatsService) {}
}
ただし、循環依存となるためモジュールクラス自体をプロバイダとして注入することはできません。
グローバルモジュール
どこにでも同じモジュールのセットをインポートする場合、冗長になる可能性があります。 Nestとは異なり、Angularのproviders
はグローバルスコープに登録されます。定義すればどこでも利用できます。しかし、Nestはプロバイダをモジュールスコープ内にカプセル化します。最初にカプセル化したモジュールをインポートしないと、モジュールのプロバイダを他の場所で使用することはできません。
すぐに使用できるプロバイダのセット(ヘルパー、データベース接続など)を提供する場合は、@Global()
デコレータを使用してモジュールをグローバルにします。
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()
デコレータはモジュールをグローバルスコープにします。グローバルモジュールは、通常はルートモジュールまたはコアモジュールによって一度だけ登録する必要があります。上記の例では、CatsService
プロバイダはどこにでもあり、サービスを注入したいモジュールはimports
配列にCatsModule
をインポートする必要はありません。
すべてをグローバルにすることは設計上よくありません。必要なボイラープレートを減らすために、グローバルモジュールを使用しましょう。imports
配列はモジュールのAPIを使う側が利用できるようにするための方法です。
動的モジュール
Nestモジュールシステムには、動的モジュールと呼ばれる強力な機能が含まれています。この機能により、プロバイダを動的に登録および構成するカスタマイズ可能なモジュールを簡単に作成できます。動的モジュールについてはここで詳しく説明します。この章では簡単な概要を示します。
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,
};
}
}
forRoot()
メソッドは、動的モジュールを同期的または非同期的に(Promiseを介して)返します。
このモジュールは@Module()
デコレータメタデータでConnection
プロバイダを定義しますが、さらに(forRoot()
メソッドに渡されるentities
とoptions
オブジェクトに応じて)リポジトリなどのプロバイダのコレクションを公開します。動的モジュールによって返されるプロパティは@Module()
デコレータで定義されたベースモジュールメタデータを(オーバーライドするのではなく)拡張することに注意してください。これが、静的に宣言されたConnection
プロバイダと動的に生成されたリポジトリプロバイダの両方をモジュールからエクスポートする方法です。
上記のように定義されると、DatabaseModule
は次の方法でインポートおよび構成されます。
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 {}
動的モジュールを順番に再エクスポートする場合、exports
配列ではforRoot()
メソッド呼び出しを省略できます。
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 {}