みなさまこんにちは!エアークローゼットのLuckyです。この記事はエアークローゼットのアドベントカレンダー2024の17日目の記事となってます。
Nestjs injection scope's things to note
Nestjsとは
NestJSは、効率的で拡張性が高く、メンテナンスが容易なサーバーサイドアプリケーションを構築するための人気フレームワークです。
この記事では、Nestjsのインジェクションスコープの側面に焦点を当てます。Nestjsについて詳しく知りたい場合は、この記事を読むか、Nestjs の公式ドキュメントで学習できます。
NestJS Singleton - Injection Scope
Nestjsを使ったことがあるなら、おそらくインジェクションについてよく知っているでしょう。
プロバイダーに@Injectable
デコレーターを定義すると、このプロバイダーのライフサイクルの制御をIoCコンテナー(Inversion of Control)に与えることになります。プロバイダーを使用するには、通常どおり、プロバイダーをコンストラクターに挿入し、モジュールに配置します。
IoCコンテナは、インジェクションスコープと呼ばれる機能を含む強力なプロバイダ管理をサポートします。
インジェクションスコープ(Injection Scope):
プロバイダーのスコープはそのライフサイクルを決定します。NestJSは主に以下の3つのスコープをサポートします:
- Singleton Scope(SINGLETON): デフォルトスコープで、同じインスタンスがどこでも再利用されます。
- Request Scope(REQUEST): 各リクエストごとに新しいインスタンスが作成されます。
- Transient Scope(TRANSIENT): プロバイダーが注入されるたびに新しいインスタンスが作成されます。
以下と同様の方法で各プロバイダーのスコープを定義できます。
import { Injectable, Scope } from '@nestjs/common';
// ほとんどの場合、プロバイダーが特別な状態を保存しない場合は、
// SINGLETONスコープ (デフォルト) を使用することになります。
@Injectable({ scope: Scope.REQUEST })
export class CatsService {}
singletonを使用すべき
ほとんどの場合、プロバイダーが特別な状態(state)を保存しない場合は、 SINGLETON
スコープ (デフォルト) を使用する必要があります。
なぜそうなるのかというと、Nestjsがプロバイダーのインスタンスを作成するときに、それが SINGLETON
スコープの場合、このインスタンスはインポート先のモジュール全体で共有されるからです。
多くのメリットがありますが、特に次の2つのメリットがあります。
-
モジュールでは各プロバイダーのインスタンスが1つだけなのでメモリを節約できます。
-
各呼び出し後に作成されるインスタンスが多すぎると、処理が完了したときにクリーンアップするため
garbage-collection
システムでCPUリソースが浪費されるため、インスタンスが1つだけでリソースが節約されます。
留意事項
インジェクションスコープを効果的に使用するために、以下の留意事項もあります。
スコープの階層(Scope hierarchy)
プロバイダーのスコープを定義する際に留意すべきことが1つあります、それは、スコープの階層です。
簡単に言うと、コントローラーまたはプロバイダーのスコープは SINGLETON
が、スコープが REQUEST
であるクラスに依存している場合、依存するプロバイダーまたはコントローラーのスコープは REQUEST
に変更されます。
次のようなインジェクションチェーンがあるとします。
CatsController
← CatsService
← CatsRepository
CatsController
と CatsService
に SINGLETON
スコープがあるが、CatsRepository
に REQUEST
スコープがある場合、CatsService
と CatsController
の両方のスコープが REQUEST
に強制的に切り替えられます。
たとえば、CatsRepository
内の REQUEST
にアクセスしたいため、@nestjs/core
の REQUEST
プロバイダーを使用する場合 、このプロバイダーには REQUEST
のスコープがあるため、誤ってインジェクション チェーン全体が REQUEST
のスコープに切り替わってしまいます。
したがって、 REQUEST
スコープは絶対に必要な場合にのみ使用すべきと思います。
モジュール管理
Nestjs では、プロバイダーをモジュールにインポートすると、そのプロバイダー インスタンスは、インポート先のモジュール全体で使用されます。ただし、いくつかモジュールが同じプロバイダーを使用する場合は、これらのモジュールが同じプロバイダーのインスタンスを使用できるようにすべきです。
たとえば、子モジュールAと子モジュールBに同じ親がある場合、子モジュールの両方が同じプロバイダーをインポートするのではなく、そのプロバイダーを子モジュールAからエクスポートしてから、子モジュールBにインポートすることをお勧めします(shared modules
機能)。そうすれば、両方で同じインスタンスを使用してリソースを節約できます。
モジュールの管理と最適化を簡単にするために、nestjs-spelunker
ライブラリを使用して NestJs の依存関係(Dependencies)を視覚化できます。
最後に
NestJS は、簡単に拡張および保守できるフレームワークです。さらに、IoC のおかげで、アプリケーションの実行時のリソースの最適化にも役立ちます。
NestJS を使用する開発者にとって、NestJS の機能を正しく活用する方法を理解することは、システムを最適化し、ユーザーエクスペリエンス (UX) を向上させ、より良いユーザー体験を提供するために役立ちます。
また、エアークローゼットはエンジニア採用活動も行っておりますので、興味のある方はぜひご覧ください!