DI
JavaScript
NestJS
TypeScript
public
NestJS で Interface を使った Dependency Injection を実現する
NestJS は文字列のトークンを実体クラスに置き換えて DI することができる。
これを使ってクラス間をインターフェースで結びつけることができる。
インターフェースの定義
以下UsersServiceというサービスをインターフェースで DI できるようにする例を記載する。
インターフェースを定義する。
src/users/interfaces/users.service.interface.ts 1
2
3
4
5
6
7
8
9
10
11
12 import { CreateUserDto } from "../dto/create-user.dto" ;
import { UserEntity } from "../entities/user.entity" ;
export interface IUsersService {
/**
* ユーザを取得する
* @param id ID
*/
findOne ( id : string ) : Promise < UserEntity > ;
// 長いので省略
}
実装
export class UsersService implements IUsersServiceみたいな感じでインターフェースの実装を行う。
src/users/users.service.ts 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import { Inject , Injectable } from "@nestjs/common" ;
import { TYPES } from "src/types" ;
import { IUsersRepository } from "./interfaces/users.repository.interface" ;
import { IUsersService } from "./interfaces/users.service.interface" ;
// 他のimportは省略
@Injectable ()
export class UsersService implements IUsersService {
constructor (
@Inject ( TYPES . UsersRepository )
private readonly usersRepository : IUsersRepository
) {}
async findOne ( id : string ) : Promise < UserEntity > {
// 実装する
return await this . usersRepository . findOne ( id );
}
// 長いので省略
}
文字列トークンの定義
インターフェースの分だけ定数を定義する。
src/types.ts export const TYPES = {
UsersRepository : Symbol.for ( "UsersRepository" ),
UsersService : Symbol.for ( "UsersService" ),
TodosRepository : Symbol.for ( "TodosRepository" ),
TodosService : Symbol.for ( "TodosService" ),
LoggerService : Symbol.for ( "LoggerService" ),
AuthService : Symbol.for ( "AuthService" ),
};
モジュールの記載
以下のprovidersのように{provide: <文字列トークン>, useClass: 実体クラス}で記述する。
src/users/users.module.ts 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import { Module } from "@nestjs/common" ;
import { TypeOrmModule } from "@nestjs/typeorm" ;
import { TYPES } from "src/types" ;
import { UsersRepository } from "./users.repository" ;
import { UsersService } from "./users.service" ;
// 他のimportは省略
@Module ({
// このモジュールが動くのに必要なモジュールを記述する。
imports : [ TypeOrmModule . forFeature ([ UserEntity ])],
// このモジュールが提供するコントローラを記述する。
controllers : [ UsersController ],
// このモジュール内で使うサービスを記述する。
providers : [
{ provide : TYPES.UsersRepository , useClass : UsersRepository },
{ provide : TYPES.UsersService , useClass : UsersService },
],
// このモジュールが他のモジュールへ提供するサービスを記述する。
exports : [ TYPES . UsersService ],
})
export class UsersModule {}
DI
コンストラクタにて@Inject()を使って DI する。
src/users/users.controller.ts 1
2
3
4
5
6
7
8
9
10
11
12
13 import { Inject } from "@nestjs/common" ;
import { TYPES } from "src/types" ;
import { IUsersService } from "./interfaces/users.service.interface" ;
// 他のimportは省略
@Controller ( "users" )
export class UsersController {
constructor (
@Inject ( TYPES . UsersService ) private readonly usersService : IUsersService
) {}
// 省略
}