DI
依存性の注入(Dependency Injection)とは、オブジェクトが必要とするオブジェクト(依存)を、自身でnewして構築するのではなく、外部から提供することです。依存を構築するにはそのまた依存が必要、という繰り返しの結果、アプリケーションは相互に接続されたオブジェクトの網(オブジェクトグラフ)になります。このグラフを手作業で構築すると、労力がかかり、ミスが発生しやすく、テストが困難になります。BEAR.Sundayでは、その代わりにDIフレームワークのRay.Diがオブジェクトグラフを構築します。
書くコード
クラスは、必要なものをコンストラクタで宣言するだけです。
class Index extends ResourceObject
{
public function __construct(
private readonly UserRepositoryInterface $repository
) {}
public function onGet(string $id): static
{
$this->body = $this->repository->get($id);
return $this;
}
}
IndexはUserRepositoryInterfaceの実装が何であるか、それがどう作られるかを知りません。実装を決めるのは利用するクラスではなく、モジュールに記述する束縛です。
$this->bind(UserRepositoryInterface::class)->to(UserRepository::class);
インジェクターはこの束縛に従い、依存の依存まで含めてグラフ全体を再帰的に組み立てます。オブジェクトの「利用」と「構築」が分離されているので、実装の差し替えが利用側のコードに波及せず、テストではモックを束縛するだけで済みます。
束縛が決める
束縛には目的に応じた種類があります。
- リンク束縛
to()— インターフェイスに実装クラスを対応させる、最も基本の束縛 - プロバイダー束縛
toProvider()— 生成にロジックが必要なとき、ファクトリーを束縛 - インスタンス束縛
toInstance()— 生成済みの値やインスタンスを直接束縛(接続文字列・設定値・作成済みオブジェクトなど) - 名前付き束縛
annotatedWith()— 同じ型の依存を名前で区別
構文の一覧はモジュールを、それぞれの詳細はRay.Diマニュアルを参照してください。メソッド横断の関心事(ログ、認証、トランザクションなど)は、依存の束縛とは別に、AOP(bindInterceptor()によるメソッドインターセプション)で扱います。
コンテキストでグラフが変わる
束縛のセットを切り替えれば、コードは同じままアプリケーションの振る舞いが変わります。BEAR.Sundayではこの束縛のセットをコンテキストと呼び、prod-hal-api-appのように組み合わせて指定します。開発用・プロダクション用・API用で異なるオブジェクトグラフが構築されるため、設定ファイルや実行モードによる条件分岐は必要ありません。詳しくはアプリケーションを参照してください。
Ray.DiはGoogle Guiceに大きく影響を受けたDIフレームワークです。