制御の反転 (IoC) とは - DI との関係とフレームワーク設計の原則
制御の反転はフレームワークがアプリケーションコードを呼び出す設計原則。依存性注入 (DI) との関係 / Hollywood 原則 / IoC コンテナの落とし穴と Lambda での実例を解説
制御の反転とは
制御の反転 (Inversion of Control, IoC) は、プログラムの制御フローをアプリケーションコードではなくフレームワークやコンテナが管理する設計原則である。「ハリウッドの原則 (Don't call us, we'll call you)」とも呼ばれる。
通常のライブラリ利用では、アプリケーションがライブラリを呼び出す。IoC では逆に、フレームワークがアプリケーションのコードを呼び出す。この「制御の方向の反転」が名前の由来だ。
Martin Fowler は 2005 年の解説で、ライブラリとフレームワークの違いをまさにこの点に置いている。ライブラリは呼び出せる関数の集まりで、どれをどの順で呼ぶかはアプリケーション側が決める。フレームワークは設計の骨格をあらかじめ持っていて、利用者はサブクラス化や自作クラスの登録で自分の処理を差し込む。実行時にその差し込み点を呼ぶのはフレームワークの側だ。ライブラリとフレームワークを分ける線は規模ではなく、制御を握っているのがどちらかである。
// 通常: アプリケーション → ライブラリ (アプリが制御)
import { readFileSync } from 'node:fs';
const raw = readFileSync('config.json', 'utf-8'); // 呼ぶ順番とタイミングをアプリが決める
const config = JSON.parse(raw);
applyConfig(config);
// IoC: フレームワーク → アプリケーション (フレームワークが制御)
app.get('/users', (req, res) => {
// Express がリクエストを受け取り、適切なハンドラを呼び出す
// アプリは「何をするか」だけを定義し、「いつ呼ばれるか」はフレームワークに委ねる
});
IoC の実現手段
IoC は広い概念であり、複数の手段で実現できる。依存性注入 (DI) はその 1 つにすぎない。
| 手段 | 仕組み | 例 |
|---|---|---|
| 依存性注入 (DI) | 依存オブジェクトを外部から注入 | コンストラクタインジェクション |
| イベントリスナー | イベント発生時にフレームワークがリスナーを呼び出す | DOM イベント、EventEmitter |
| テンプレートメソッド | 基底クラスがアルゴリズムの骨格を定義し、サブクラスが詳細を実装 | Android の Activity の onCreate / onResume |
| コールバック | 処理完了時にフレームワークがコールバックを呼び出す | Node.js の非同期 API |
| 宣言とメタデータ | フレームワークが宣言を読み取り、適切なコンポーネントを組み立てる | Spring のコンポーネント検出と設定クラス |
これらの境界は言語やライブラリの流行で動く。React は以前クラスコンポーネントのライフサイクルメソッドでテンプレートメソッド型の差し込み点を提供していたが、公式ドキュメントは 2026 年 8 月時点でクラスコンポーネントを Legacy API に分類し、新しいコードでは関数コンポーネントを勧めている。差し込み方が継承からフック関数の登録へ移っただけで、呼ぶ側がフレームワークであることは変わらない。手段の名前を覚えるより、「自分のコードが呼ばれる側になっているか」で見分けるほうが実務では役に立つ。
Lambda ハンドラーは IoC の典型例
AWS Lambda のハンドラー関数は IoC そのものだ。開発者は「何をするか」(ハンドラーの実装) だけを定義し、「いつ呼ばれるか」(API Gateway のリクエスト、SQS のメッセージ、EventBridge のイベント) は Lambda ランタイムが制御する。
制御が向こう側にあることは、初期化コードの置き場所にも影響する。ハンドラーの外側に書いたコードは最初の呼び出しの前に一度だけ走り、実行環境が生きている間は初期化された状態で残る。AWS のドキュメントは、関数の実行が終わったあとも次の呼び出しを見込んで実行環境をしばらく保持し、再び呼ばれたときはそれを解凍して再利用すると説明している。ハンドラーの外で作ったデータベース接続やクライアントが次の呼び出しでも使える、という定石はここから来ている。
一方で、実行環境がいつ捨てられるかはこちら側では決められない。ランタイムの更新や保守のために数時間おきに終了させられることがあり、連続して呼ばれている関数でも例外ではない。だから外側に置くのは「あれば使い回せる」性質のものに限り、処理の続きを次の呼び出しへ引き渡す前提の設計はしない。制御を渡した相手の都合で環境が消える、というのが IoC を受け入れた代償である。
DI による IoC の実践
依存性注入は IoC の中で最も実務的に重要な手段だ。テスト容易性とモジュールの疎結合を実現する。
ここで本質なのは注入という手続きではなく、依存先を選ぶ権限がどこへ移るかだ。クラスの内部で new を書いた時点で、そのクラスは自分の仕事に加えて具体的な実装を選ぶ責任まで抱えている。コンストラクタ引数で受け取る形にすれば、選ぶのは組み立てを担う側になり、クラス自身は与えられたものを使うだけになる。テストで差し替えられるのはこの権限移動の副産物であって、目的ではない。
組み立てを担う場所は 1 か所へ寄せる。Web アプリなら起動処理、Lambda ならハンドラーモジュールの先頭で依存グラフを組み、以降の層は受け取った依存を使うだけにする。この境界が曖昧なまま各所で new と注入が混ざると、どの実装が実際に動いているかを知るために全ファイルを追う羽目になる。
IoC コンテナ
Java の Spring や .NET の ASP.NET Core には IoC コンテナ (DI コンテナ) が組み込まれている。コンテナが依存関係のグラフを解析し、必要なオブジェクトを自動的に生成・注入する。
TypeScript や Node.js では tsyringe や InversifyJS がコンテナを提供する。どちらも 2026 年 8 月時点で開発が続いており、InversifyJS はリポジトリを monorepo 構成へ移した。ただし Lambda の関数のように 1 つの入口が抱える依存が数個で収まる規模では、手書きのコンストラクタインジェクションで足りることが多い。コンテナを入れるかどうかは、依存グラフの深さと組み立てコードの重複量で判断する。
コンテナには固有の落とし穴が 2 つある。1 つは、登録漏れや循環依存がコンパイル時ではなく解決時に初めて露見することだ。型で守られていた結線が、コンテナを挟んだ途端に実行時エラーへ後退する。起動時に全登録を一度解決してみるチェックを置いておくと、この後退をある程度取り戻せる。
もう 1 つは寿命の混在だ。Microsoft のドキュメントは、寿命の長いサービスが寿命の短いサービスを抱え込んでしまう誤設定を captive dependency と呼んでいる。シングルトンにスコープ付きのサービスを注入すると、シングルトンは一度しか生成されないため、最初に受け取った短命サービスをそのまま持ち続ける。登録が個別には正しく見えるだけに気づきにくく、コンテナも自動では直してくれない。寿命の指定は登録のたびに意識して選ぶ。
よくある誤解
「IoC = DI」
DI は IoC の一手段であり、IoC そのものではない。Express のルーティング、React が副作用フックを呼ぶタイミング、Lambda のイベントハンドラーもすべて IoC だ。コンテナを 1 つも使っていないコードベースでも、フレームワークの上に乗っている限り IoC は既に効いている。
「IoC を使えば自動的にテストしやすくなる」
IoC (特に DI) はテスト容易性の前提条件だが、十分条件ではない。依存を注入可能にしても、インターフェースが巨大すぎたり、副作用が多すぎたりすれば、テストは依然として困難だ。注入口を増やした結果、コンテナを組み立てないと 1 クラスも動かせない状態になり、かえって単体テストが書きにくくなることもある。
さらに掘り下げるなら関連書籍が参考になる。
この記事は役に立ちましたか?
関連用語
依存性注入 (DI)
オブジェクトが必要とする依存関係を外部から注入することで、モジュール間の結合度を下げテスタビリティを向上させる設計パターン
クリーンアーキテクチャ
ビジネスロジックを外部の技術的詳細から分離し、依存関係を内側に向けることで変更に強い設計を実現するアーキテクチャ原則
単一責任の原則
SOLID の S - クラスやモジュールが変更される理由は 1 つだけであるべきという設計原則
ライブラリ
特定の機能を提供する再利用可能なコードの集まりで、プログラムから呼び出して使う
Template Method パターン
アルゴリズムの骨格を親クラスで定義し、具体的なステップをサブクラスに委ねるデザインパターン
Scala
オブジェクト指向と関数型を融合した JVM 言語。大規模データ処理でも使われる
関連する記事
Web 開発本ガイド - フロントエンドからバックエンドまで
Web 開発の全体像を学べる技術書の選び方と学習マップを紹介。フレームワーク本の賞味期限問題と公式ドキュメントとの使い分けも解説します。
OS / 低レイヤー本ガイド - コンピュータの仕組みを学ぶ技術書の選び方
OS、コンパイラ、ネットワークなど低レイヤーを学べる技術書の 4 ジャンルと、どこから始めるべきかの指針、賞味期限の見極め方を紹介します。
インフラ / クラウド本ガイド - AWS や Docker を本で学ぶ
クラウドインフラ、コンテナ、IaC を学べる技術書の選び方と学習順序を紹介。インフラ本の賞味期限問題と公式ドキュメントとの使い分けも解説します。