依存性注入 (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 方式
注入の 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) については関連書籍でも詳しく扱われている。
この記事は役に立ちましたか?
関連用語
制御の反転
フレームワークがアプリケーションコードを呼び出す設計原則で、依存性注入の基盤となる概念
単一責任の原則
SOLID の S - クラスやモジュールが変更される理由は 1 つだけであるべきという設計原則
クリーンアーキテクチャ
ビジネスロジックを外部の技術的詳細から分離し、依存関係を内側に向けることで変更に強い設計を実現するアーキテクチャ原則
依存性逆転の原則
SOLID の D - 上位モジュールは下位モジュールに依存せず、両者とも抽象に依存すべきという設計原則
依存関係
モジュールやサービスが他のモジュールやサービスに依存する関係で、結合度と変更の影響範囲を決定する
Init コンテナ
Kubernetes でメインコンテナの起動前に初期化処理を実行する特殊なコンテナ
関連する記事
テスト本ガイド - テスト設計を学べる技術書の選び方
テストの書き方からテスト戦略まで学べる技術書の選び方を紹介。テストピラミッド、TDD の正しい読み方、テストの ROI の考え方を解説します。
バグを生むのは知識不足ではなく想像力不足である
バグの多くは、コードを書いた時点で「こういうケースもありうる」と想像できなかったことが原因です。想像力を鍛える読書法と、エッジケースへの感度を高める方法を解説します。
写経を超える - 技術書のコードを自分のプロジェクトに応用する方法
技術書のサンプルコードを写経するだけでは実力は伸びません。書籍のコードを自分のプロジェクトに応用し、実務で使える力に変える 5 つのステップを解説します。