Nestjsプロバイダーのインジェクションスコープの留意事項

みなさまこんにちは！エアークローゼットのLuckyです。この記事はエアークローゼットの[アドベントカレンダー2024](https://qiita.com/advent-calendar/2024/air-closet)の17日目の記事となってます。

https://qiita.com/advent-calendar/2024/air-closet

# Nestjs injection scope's things to note

### Nestjsとは

NestJSは、効率的で拡張性が高く、メンテナンスが容易なサーバーサイドアプリケーションを構築するための人気フレームワークです。

この記事では、Nestjsのインジェクションスコープの側面に焦点を当てます。Nestjsについて詳しく知りたい場合は、[この記事](https://qiita.com/suzuki0430/items/894e2da2b44156a2a437)を読むか、Nestjs の公式ドキュメントで学習できます。

https://nestjs.com/

## NestJS Singleton - Injection Scope

Nestjsを使ったことがあるなら、おそらくインジェクションについてよく知っているでしょう。

プロバイダーに`@Injectable`デコレーターを定義すると、このプロバイダーのライフサイクルの制御をIoCコンテナー（Inversion of Control）に与えることになります。プロバイダーを使用するには、通常どおり、プロバイダーをコンストラクターに挿入し、モジュールに配置します。

IoCコンテナは、インジェクションスコープと呼ばれる機能を含む強力なプロバイダ管理をサポートします。

### インジェクションスコープ（Injection Scope）: 
プロバイダーのスコープはそのライフサイクルを決定します。NestJSは主に以下の3つのスコープをサポートします：
- **Singleton Scope（SINGLETON）**: デフォルトスコープで、同じインスタンスがどこでも再利用されます。
- **Request Scope（REQUEST）**: 各リクエストごとに新しいインスタンスが作成されます。
- **Transient Scope（TRANSIENT）**: プロバイダーが注入されるたびに新しいインスタンスが作成されます。

以下と同様の方法で各プロバイダーのスコープを定義できます。

```typescript
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つだけでリソースが節約されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935524/2a4f52da-e113-e41c-7599-5d77a08357f7.png)

## 留意事項

インジェクションスコープを効果的に使用するために、以下の留意事項もあります。

### スコープの階層（Scope hierarchy）

プロバイダーのスコープを定義する際に留意すべきことが1つあります、それは、スコープの階層です。

簡単に言うと、コントローラーまたはプロバイダーのスコープは `SINGLETON` が、スコープが `REQUEST` であるクラスに依存している場合、依存するプロバイダーまたはコントローラーのスコープは `REQUEST` に変更されます。

次のようなインジェクションチェーンがあるとします。
`CatsController` &#8592; `CatsService` &#8592; `CatsRepository`
`CatsController` と `CatsService` に `SINGLETON` スコープがあるが、`CatsRepository` に `REQUEST` スコープがある場合、`CatsService` と `CatsController` の両方のスコープが `REQUEST` に強制的に切り替えられます。

たとえば、`CatsRepository`内の `REQUEST` にアクセスしたいため、`@nestjs/core`の `REQUEST` プロバイダーを使用する場合 、このプロバイダーには `REQUEST` のスコープがあるため、誤ってインジェクション チェーン全体が `REQUEST` のスコープに切り替わってしまいます。

したがって、 `REQUEST` スコープは絶対に必要な場合にのみ使用すべきと思います。

### モジュール管理

Nestjs では、プロバイダーをモジュールにインポートすると、そのプロバイダー インスタンスは、インポート先のモジュール全体で使用されます。ただし、いくつかモジュールが同じプロバイダーを使用する場合は、これらのモジュールが同じプロバイダーのインスタンスを使用できるようにすべきです。

たとえば、子モジュールＡと子モジュールＢに同じ親がある場合、子モジュールの両方が同じプロバイダーをインポートするのではなく、そのプロバイダーを子モジュールＡからエクスポートしてから、子モジュールＢにインポートすることをお勧めします（`shared modules`機能）。そうすれば、両方で同じインスタンスを使用してリソースを節約できます。

モジュールの管理と最適化を簡単にするために、`nestjs-spelunker` ライブラリを使用して NestJs の依存関係（Dependencies）を視覚化できます。

https://blog.stackademic.com/how-to-visualize-and-optimize-module-dependencies-in-nestjs-dc4f636676f0

## 最後に

NestJS は、簡単に拡張および保守できるフレームワークです。さらに、IoC のおかげで、アプリケーションの実行時のリソースの最適化にも役立ちます。

NestJS を使用する開発者にとって、NestJS の機能を正しく活用する方法を理解することは、システムを最適化し、ユーザーエクスペリエンス (UX) を向上させ、より良いユーザー体験を提供するために役立ちます。

#
また、エアークローゼットはエンジニア採用活動も行っておりますので、興味のある方はぜひご覧ください！

https://corp.air-closet.com/recruiting/developers/

https://www.wantedly.com/companies/airCloset/projects