Moduleとは
NestJSにおける
Module
とは、@Module()
デコレータでアノテーションされたクラスを意味します。@Module()
デコレータは、NestJSがアプリケーションの構造を整理するために利用するメタデータを提供します。各アプリケーションには、少なくとも1つの
Module
(ルートモジュール)があります。ルートモジュールとは、Nestがアプリケーショングラフを構築するための出発点であり、NestがModule
とProvider
の関係や依存関係を解決するために使用する内部データの構造です。非常に小さなアプリケーションでは、理論的にはルートモジュールだけでいいかもしれません。しかし、これは良いケースだと言えません。Module
はコンポーネントを組織化する上では非常に効果的な方法です。ほとんどのアプリケーションでは、結果として得られるアーキテクチャは複数のモジュールを採用し、それぞれが密接に関連した機能のセットをカプセル化することになります。@Module()
デコレータは、Module
のプロパティを記述したオブジェクトを一つだけ受け取ります。NestJSにおけるModuleは以下の4つの要素で構成されています。
providers
:Nestインジェクタによってインスタンス化され、少なくともこのModule
全体で共有される可能性があるProvider。controllers
:このModule
で定義された、インスタンス化する必要のあるControllerのセット。imports
:このModuleで必要とされるProviderをエクスポートするインポートモジュールのリスト。exports
:このモジュールが提供するProviderのサブセットで、このModuleをインポートしている他のModuleで利用可能であるべきものを指定するリスト。Provider自体。 またはそのトークンだけを使える。
このModuleは、デフォルトでProviderをカプセル化します。これは、現在のModuleの直接の一部でもなく、インポートしているモジュールからエクスポートされたものでもないProviderを注入することは不可能であることを示します。したがって、あるModuleからエクスポートされたProviderは、そのModuleのAPIとみなせます。
次のようにして、
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 {}
メモ
NestJSでModuleを作る時、以下のコマンドを実行してください。
nest g module cats
上記では、
cats.module.ts
ファイルでCatsModuleを定義し、このModuleに関するものをすべてcats
ディレクトリに移動させました。最後に、このModuleをルートモジュール(app.module.ts
ファイルで定義されているAppModule
)にイン ポートしてください。ディレクトリは以下のようになります。
src
cats
dto
create-cat.dto.ts
interfaces
cat.interface.ts
cats.controller.ts
cats.module.ts
cats.service.ts
app.module.ts
main.ts
NestJSでは、Moduleはデフォルトでシングルトンなので、複数のModule間で任意のプロバイダの同じインスタンスを簡単に共有することができます。
すべてのModuleは自動的に共有モジュールとなります。一度作成したモジュールは、どのモジュールでも再利用できます。例えば、
CatsService
のインスタンスを他のモジュールと共有したいとします。そのためには、まずCatsService
プロバイダをエクスポートして、以下のようにモジュールのexports
配列に追加する必要があります。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 {}
これで、
CatsModule
をインポートしたモジュールは全てCatsService
にアクセスできるようになり、同様にインポートした他のすべてのModuleと同じインスタンスを共有できます。上述で見たように、Moduleは内部のプロバイダをエクスポートできます。さらに、インポートしたModuleを再エクスポートできます。次の例では、
CommonModule
はCoreModule
にインポートされ、CoreModule
からもエクスポートされます。@Module({
imports: [CommonModule],
exports: [CommonModule],
})
export class CoreModule {}
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) {}
}
もし、どこでも同じModuleのセットをインポートする必要がある場合は面倒になります。NestJSでは、ProviderはModuleのスコープ内にカプセル化されます。カプセル化されたModuleをインポートしないと、そのモジュールのProviderを他の場所で使用できません。
すぐにどこでも使えるProviderを提供したい場合は、
@Global()
デ コレータを活用してModuleをグローバル化します。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 {}
デコレータを使うと、Moduleをグローバルに展開できます。グローバルModuleは一度だけ登録する必要があり、一般的にはルートモジュールやコアモジュールによって登録されます。上記の例では、
CatsService
プロバイダはどこにでもあるもので、サービスをインジェクトしたいModuleは、imports
配列でCatsModule
をインポートする必要はありません。メモ
すべてのModuleをグローバルにすることは良い設計判断とはいえません。グローバルModuleは、必要な定型句を減らすために用意されています。
imports
配列は、一般的にModuleのAPIをコンシューマが利用できるようにするために望ましい方法です。NestJSのModuleシステムには、動的Moduleという強力な機能があります。この機能で、Providerを動的に登録・設定できるカスタマイズ可能なModuleを簡単に作成できます。動的Moduleは、ここで広範囲にカバーされます。この章では、Moduleの紹介を完了するために、簡単に概要を説明します。
以下は、
DataabaseModule
の動的Module定義の例です。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()
メソッドは同期または非同期で動 的Moduleを返せます。このModuleは、デフォルトで
Connection
プロバイダを定義しています。さらに、forRoot()
メソッドに渡されるエンティティやオプション・オブジェクトに応じて、Providerのコレクション(リポジトリ)を公開します。動的Moduleが返すプロパティは、@Module()
デコレータのメタデータを拡張することに注意してください。このようにして、静的に宣言された
Connection
プロバイダと動的に生成されたリポジトリプロバイダの両方がModuleからエクスポートされます。動的Moduleをグローバルスコープに登録したい場合は、
global
プロパティをtrue
に設定します。{
global: true,
module: DatabaseModule,
providers: providers,
exports: providers,
}
注意
前述したように、すべてのModuleをグローバル化するのは設計上良い判断とはいえません。
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 {}
動的Moduleを順番に再エクスポートする場合は、
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 {}
Last modified 9mo ago