Overview
Dependency injection sounds complicated, but the idea is simple: instead of creating the code it depends on, a piece of code receives it from outside. This video builds the attachment service of a business chat app from scratch to show what that gives you, and where it goes too far.
Key Points
- Supporting S3, SFTP and WebDAV storage with if statements piles responsibilities into one class, fills the method with optional arguments and forces connection settings to be passed down through every layer.
- With DI, a storage interface has one implementation per backend, each taking its settings in the constructor. Once the user’s company is known, a factory builds the right storage, and the request handler simply calls upload.
- The same approach covers a pipeline of virus scanning (ThreatProtect in production, Synergy in development), image resizing with the sharp library, previews chosen by MIME type and AES encryption with per-user keys, all wired together in one place, some at startup and some per request.
- Because the parts connect through interfaces, tests can swap in a local fake S3 or an encryptor that does nothing to isolate one piece, and fakes make failures easy to reproduce.
- Criticisms from engineers include functions with too many parameters, jump-to-definition landing on interfaces and premature abstraction. Signs an injection point is worth it: three or more branches, a dependency that is painful to set up for tests, or a rarely used component slowing startup. Start simple and extract only where it hurts.
Chapters
- 0:00 Introduction
- 0:56 The example app
- 1:37 The limits of if statements
- 3:24 Introducing DI
- 4:26 The pipeline
- 6:38 The finished design
- 7:22 Testing
- 10:00 Voices from the field
- 13:05 When to use it
- 13:20 Summary
概要
依存性の注入(DI)は難しそうな名前ですが、中身はシンプルです。あるコードが使う別のコードを内部で作る代わりに、外から渡してもらう。それだけです。この動画では、ビジネスチャットアプリの添付ファイルを扱うサービスをゼロから組み立てながら、DIで何が得られるのか、そしてどこからがやりすぎなのかを解説します。
ポイント
- 保存先としてS3、SFTP、WebDAVをif文で切り替えると、1つのクラスが責任を抱えすぎ、メソッドは任意の引数だらけになり、接続情報を何層にもわたってバケツリレーすることになります。
- DIでは、ストレージのインターフェースに対して保存先ごとの実装を用意し、それぞれの接続設定はコンストラクターで受け取ります。ユーザーの所属する会社が分かった時点でファクトリーが適切なストレージを作り、リクエスト処理はアップロードを呼ぶだけになります。
- 同じ考え方で、ウイルススキャン(本番はスレットプロテクト、開発環境はシナジー)、sharpライブラリーによる画像の縮小、MIMEタイプに応じたプレビュー生成、ユーザーごとの鍵を使ったAES暗号化という一連の処理も組み立てます。構成はすべて1か所で決まり、起動時に決まるものとリクエストごとに選ばれるものがあります。
- 部品同士がインターフェース越しにつながっているので、テストではローカルで動く偽物のS3や何もしない暗号化を差し込み、一部分だけを隔離して検証できます。偽物を使えば、失敗のケースも簡単に再現できます。
- 現場のエンジニアからは、引数の多すぎる関数、定義へジャンプするとインターフェースに飛んでしまうこと、早すぎる抽象化といった批判も挙がっています。差し込み口を作る目安は、分岐が3つ以上になったとき、テストのためにその依存を用意するのが面倒なとき、たまにしか使わない部品が起動を重くしているときの3つです。まずは素直に作り、つらくなった部分だけを切り出します。