はじめに
AdventCalendar2024 最終日。1人で走り切ったらもらえるQiitaのTシャツがほしくて毎日頑張って記事を書いてきました。
初めてこういうイベントに参加して記事を書きましたが、こういうのは事前の準備がないと内容が薄いなと感じたので、次参加するときはもっと良い内容を書けるようにしたいなと思った次第です。(今回は12/1に勢いで参加したので…)
余談はさておき、本編に入っていきます。
今回はNest.jsのProviderについて勉強したのでまとめていきます。
Providerとは
サービス・リポジトリ・ファクトリ・ヘルパー等のインスタンスを提供するための基本的な構成要素。
使いたいModuleで定義する必要がある。
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class AppModule {}
依存性注入
依存性注入(Dependency Injection、DI) とは、オブジェクトがその依存関係を自分で作成するのではなく、外部から提供される設計パターンである。
DIを使わない場合、自分でインスタンス作成をする必要があるが、使う場合は以下のように宣言することで外部から簡単にインスタンスを取得できる。
DIを使いたい場合は@Injectable()
を使ってそのProviderを定義する必要がある、
// DIを使わない場合
class CatsController {
private catsService: CatsService;
constructor() {
this.catsService = new CatsService(); // 自分でインスタンスを作成
}
}
// DIを使う場合
@Injectable()
class CatsService {
// CatsServiceのロジック
}
@Controller('cats')
class CatsController {
constructor(private readonly catsService: CatsService) {} // 外部から注入
// CatsControllerのロジック
}
スコープ
Providerはスコープと呼ばれるライフタイムがある。
スコープには以下の3種類がある。
-
DEFAULT
:アプリケーション全体(起動から終了まで)で一度だけインスタンス化され、全てのリクエストで同じインスタンスが共有される -
REQUEST
:各リクエストごとに新しいインスタンスが作成される。同じリクエスト内ではインスタンスは共有される -
TRANSIENT
:依存関係が要求されるたびに新しいインスタンスが作成される。同じリクエスト内だったとしてもリクエスト内で複数回依存関係が要求された場合はその回数だけ作成される
オプションプロバイダー
依存関係が必ずしも存在しなくても良い場合(例えばアプリケーションに必須の動作ではない)に使用する。
@Optional()
を使って定義する。
import { Injectable, Optional, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T) {}
}
プロパティベースのインジェクション
これまでのものはコンストラクタを介してプロバイダーが注入されていたが、プロパティに直接依存関係を注入する方法。
@Inject
を使って定義する。
@Injectable()
class CatsService {
// CatsServiceのロジック
}
@Injectable()
class DogsService {
// DogsServiceのロジック
}
@Injectable()
class AnimalService {
@Inject(CatsService)
private readonly catsService: CatsService;
@Inject(DogsService)
private readonly dogsService: DogsService;
someMethod() {
this.catsService.someMethod();
this.dogsService.someMethod();
}
}
ただし、クラスが他のクラスを継承していない場合はコンストラクタによる依存性注入を行った方が良い(@Inject()
と constructor()
+ @Injectable()
はほぼ同じ)。
理由としては以下が挙げられる。
- 明示的な依存関係の宣言
- 可読性の向上
- テストの容易さ
参考