依存性注入(DIコンテナ)

何をする手法か

依存性注入(DI)は、あるクラスが使う相手を、自分で作らずに外から渡してもらう設計です。

// 注入しない書き方 — 相手を自分で作っている
class OrderService {
  private mailer = new SmtpMailer('smtp.example.com')  // 固定されている
  confirm(order: Order) { this.mailer.send(order.email, '注文を受け付けました') }
}

// 注入する書き方 — 相手を外から受け取る
class OrderService {
  constructor(private mailer: Mailer) {}                // 何が来るかは呼び出し側が決める
  confirm(order: Order) { this.mailer.send(order.email, '注文を受け付けました') }
}

違いは1か所ですが、結果は大きく変わります。前者は SmtpMailer 以外を使えません。後者は同じ形(Mailer)を満たすものなら何でも渡せます。

テストが書けるようになる

前者を [[自動テスト]] にかけると、テストのたびに本物のメールが飛びます。ネットワークが無ければ失敗し、実行時間も伸びます。

後者なら、[[テストダブル(モック・スタブ)]] を渡せます。

const sent: string[] = []
const service = new OrderService({ send: (to, body) => sent.push(to) })
service.confirm(order)
expect(sent).toEqual(['a@example.com'])

「テストしにくい」と感じるコードの多くは、依存を自分で作っています。 DIは設計の理屈より先に、この実務的な効き目で理解するのが早いです。

依存の向きを逆転させる

もう一段の狙いは依存の向きの制御です。業務ロジックが具体的な技術(SMTP・特定のDB)に依存していると、技術を変えるたびに業務ロジックを触ることになります。

  • 業務ロジック側が「こういう形のものが要る」とインターフェースを定義する
  • 技術側がそれを実装する
  • 結果として、技術側が業務ロジックに依存する(向きが逆転する)

この考え方は依存性逆転と呼ばれ、[[デザインパターン]] の Strategy などと組み合わせて使われます。

DIコンテナ

注入する相手が増えると、組み立てのコードが長くなります。

const db = new PgClient(url)
const repo = new OrderRepository(db)
const mailer = new SmtpMailer(host)
const service = new OrderService(repo, mailer)   // 手で配線する

DIコンテナは、この配線を宣言から自動で行う道具です。多くの [[フレームワーク]](Spring・NestJS・ASP.NET Core など)が標準で備えています。

ただしコンテナはDIの本質ではありません。上のような手作業の配線でもDIは成立します。規模が小さいうちは手で配線するほうが、何がどこから来ているか追いやすくなります。

やりすぎの兆候

  • すべてにインターフェースを作る — 実装が1つしかないものに抽象を挟むと、追う手間だけが増えます
  • コンテナが何を注入したか追えない — 実行時に解決する仕組みは、静的に読めなくなります。設定の誤りが起動時まで分かりません
  • 循環依存が出る — AがBを、BがAを必要とする状態は、責務の分け方が間違っているサインです。コンテナで解決せず設計を直します

判断は「差し替える理由があるか」です。テストで差し替える、環境で実装を変える、といった理由が無いなら、直接使って構いません。

関連技術とのつながり

  • [[テストダブル(モック・スタブ)]] — 注入できるからこそ差し替えられる。DIの効き目が最も分かる場面
  • [[自動テスト]] — 外部依存を持つコードをテスト可能にする前提条件
  • [[デザインパターン]] — Strategy や Factory と組み合わせて使われる
  • [[フレームワーク]] — DIコンテナを標準で備えるものが多い
  • [[クリーンコード]] — 依存の向きを制御することは可読性と変更容易性に直結する
Q: 依存性注入がテストで効く理由はどれ?
- [ ] テストコードの記述量が減るから
- [x] 使う相手を外から渡せるため、テスト用の実装に差し替えられるから
- [ ] テストの実行順序を制御できるから
解説: 依存を自分で作っているコードは、本物の外部サービスを呼んでしまうためテストが不安定になります。

Q: 依存性逆転で起きることはどれ?
- [x] 業務ロジックがインターフェースを定義し、技術側がそれを実装する
- [ ] 業務ロジックが技術側のクラスを直接呼ぶ
- [ ] すべての依存が無くなる
解説: 向きが逆転することで、技術を差し替えても業務ロジックを触らずに済みます。

Q: DIコンテナについて正しい説明はどれ?
- [ ] コンテナが無いとDIはできない
- [x] 配線を自動化する道具であり、手で配線してもDIは成立する
- [ ] コンテナはテストを不要にする
解説: 規模が小さいうちは手で配線するほうが、何がどこから来ているか追いやすくなります。