依存性注入 (DI)
オブジェクトが必要とする依存関係を外部から注入することで、モジュール間の結合度を下げテスタビリティを向上させる設計パターン
設計パターンテスト
依存性注入 (DI) とは
依存性注入 (Dependency Injection) は、オブジェクトが利用する依存オブジェクトを自ら生成するのではなく、外部から渡してもらう設計パターンである。SOLID 原則の依存性逆転の原則 (DIP) を実現する具体的な手法であり、制御の反転 (IoC) の一形態だ。
// ❌ DI なし: クラスが依存を自ら生成 → テスト時に差し替え不可
class OrderService {
private repo = new DynamoDBOrderRepo(); // 密結合
async getOrder(id: string) { return this.repo.findById(id); }
}
// ✅ DI あり: 依存を外部から注入 → テスト時にモックに差し替え可能
class OrderService {
constructor(private readonly repo: OrderRepository) {}
async getOrder(id: string) { return this.repo.findById(id); }
}
注入の 3 方式
| 方式 | 特徴 | 推奨度 |
|---|---|---|
| コンストラクタ注入 | 依存が明示的、不変、必須の依存に最適 | 推奨 |
| セッター注入 | オプショナルな依存、後から差し替え可能 | 限定的 |
| 関数の引数 | TypeScript/JS でシンプル、クラス不要 | 推奨 |
// コンストラクタ注入
class OrderService {
constructor(private readonly repo: OrderRepository) {}
}
// 関数の引数として渡す (TypeScript ではこちらがシンプル)
const createOrderHandler = (repo: OrderRepository) =>
async (event: APIGatewayEvent) => {
const order = await repo.findById(event.pathParameters!.id!);
return { statusCode: 200, body: JSON.stringify(order) };
};
テスタビリティの向上
DI の最大のメリットはテスト時にモックを差し込める点だ。
// 本番: DynamoDB 実装を注入
const handler = createOrderHandler(new DynamoDBOrderRepo(ddbClient));
// テスト: インメモリ実装を注入 (DB 不要で高速)
const mockRepo: OrderRepository = {
findById: vi.fn().mockResolvedValue({ id: '123', status: 'pending' }),
save: vi.fn(),
};
const testHandler = createOrderHandler(mockRepo);
DynamoDB への接続が不要なため、テストが高速 (ミリ秒単位) で安定する。ネットワーク障害やデータの状態に依存しない。
Lambda ハンドラーでの DI パターン
Lambda では、ハンドラーの外側で依存を初期化し、ハンドラー関数に渡すパターンが軽量で実用的だ。
テスト時は dependencies.ts をモックに差し替える。DI コンテナは不要だ。
DI コンテナの要否
Java の Spring や .NET の ASP.NET Core では DI コンテナが標準だが、TypeScript/Node.js では必ずしも必要ない。
| アプローチ | 適するケース |
|---|---|
| 手動 DI (引数渡し) | Lambda、小〜中規模プロジェクト |
| DI コンテナ (tsyringe 等) | 大規模プロジェクト、依存グラフが複雑 |
Lambda のような短命なプロセスでは、手動 DI で十分なケースが多い。DI コンテナの導入コスト (学習、設定、デバッグの複雑化) に見合うかを判断する。
よくある誤解
「DI = DI コンテナ」
DI はパターンであり、DI コンテナはそれを自動化するツールだ。コンストラクタの引数に依存を渡すだけでも立派な DI である。
「すべての依存を注入すべき」
Date.now() や Math.random() まで注入するのは過剰だ。テスト時に差し替えたい依存 (外部サービス、DB、ファイルシステム) だけを注入対象にする。
依存性注入 (DI) については関連書籍でも詳しく扱われている。