依存性注入とは

最初に浸透したDIコンテナであるEJBが生まれてから20年近くたちますが、JAVAアプリケーションで使用していないものは無いぐらいの存在となっています。
pythonやASP.NETでも似たような思想のフレームワークが存在します。
そもそも、DIコンテナとは何でしょう。

DIは、「Dependency Injection:依存性の注入」の略となります。
「依存性注入」という言葉が、すでに分かりにくいので、その言葉の意味(意図)から説明を行います。

クラスAがビジネスロジックで、クラスBが共通の計算クラスだったとします。
クラスAが以下のような処理を行う場合、クラスAは割り算をクラスBに依存しているということになります。

ところが、突然クラスBの仕様が変わるとクラスAは処理が正常にできなくなります。

また、クラスBは共通クラスなの、ほかの処理で負荷が上がるとレスポンスに影響が出る場合があります。

このような問題を解決する方法として以下のような構造が考え出されました。

クラスAは依存するクラスを特定せず、中継するインターフェースのみを指定するようにします。
インターフェースは対象となるクラスを特定して処理を依頼します。
処理結果はインターフェースを経由してクラスAに戻されます。
こうすることで、仕様変更や負荷分散・冗長化が可能になると考えられました。

クラスAが依存するクラスをプログラム上には指定せず、実行時にクラスBを注入するという意味で、依存性注入(DI)という名前が付けられ、実際に処理するクラスをコンテナとしてクラスAが制御できるようにしたことからDIコンテナと呼ばれるようになりました。

データベーストランザクション管理

DIコンテナの役割からビジネスロジックはDIコンテナに実装されるようになり、データベース連携を含めて、3階層アーキテクチャと呼ばれるようになりました。
アプリケーション層からデータ層を呼び出してデータ更新を行いますが、最終的なトランザクション境界はアプリケーション層となります。

そのためDIコンテナがトランザクション管理機能を持つようになりました。

プレゼンテーション層はユーザインタフェースのためのロジック、アプリケーション層は主にビジネスロジック、データ層はデータベースへのアクセスロジックをDIコンテナとして実装します。
このように、そうごとにDIコンテナとすることで、各層で負荷分散や冗長化ができると考えられていました。
(プレゼンテーション層はDIコンテナとしない場合もあります)
データ層はDBテーブルに対するCRUD(Create,Read,Update,Delete)処理の実装となります。
DBテーブルアクセスのためのDIコンテナはDao(Data Access Object)と呼ばれます。
Daoはテーブルと同じ項目(プロパティ)を持ったエンティティクラスを用意し、CRUD処理をエンティティクラスで行うO/Rマッピングという実装が一般的です。
また、CRUD処理は自動生成が可能のため、テーブル名やエンティティクラスの定義のみでCRUD処理を自動化するフレームワークも多く存在します。

データ層のDaoをアプリケーション層のビジネスロジックからDIコンテナとして呼び出します。
Daoはテーブルごとの処理しかできませんが、1回のトランザクションで複数のテーブル更新が必要なビジネスロジックの場合、Daoでトランザクションを管理してしまうとほかのテーブル更新に失敗した場合にロールバックができません。
そのため、トランザクション管理はアプリケーション層のDIコンテナで実施するのが一般的です。

このように、DIコンテナを利用したデータアクセスやトランザクション管理が最近のフレームワークでは当たり前となっています。

プログラムモジュールによる負荷分散・冗長化の必要性

DIコンテナが生まれた当初は負荷分散・冗長化が目的でした。
ですが、最近の考えではサーバーの仮想化やクラウド化などで、サーバーごと、インフラごと負荷分散・冗長化することが多く、プログラムモジュール単位で負荷分散・冗長化するような面倒な設計を行うことはほとんどありません。
ただ、プログラムを作る上で、プログラムの可読性や開発効率の観点からDIコンテナを利用したいと考える技術者が多いく、現在ではトランザクション管理のためにDIコンテナを利用しているのが実態で、DIコンテナで負荷分散・冗長化するシステムは皆無といっていいと思います。

依存性注入機能の必要性

DIコンテナのもう一つの目的として依存性注入があります。
当初、依存関係を祖にするために作られた仕組みですが、実際に運用すると、DI対象クラスが固定されることが前提の設計となり、仕様変更の場合はクラスを直接呼び出すのと大差がないことが多く、依存性注入機能を十分に発揮した設計というものはほとんどないのが実態です。
依存性注入機能には、呼び出し、処理の戻りなどのイベント処理が実装できるという機能もあり、これについてはアプリケーション共通処理などによく利用されています。

最近のフレームワークで利用されている機能

時代の流れとともに、当初の目的の機能が不要になってきていますが、副産物として生まれた機能の利便性が高いため、今でもDIコンテナが利用されています。
しかし、今となっては不要な機能が多く、プログラムが複雑なってしまうため、最近のフレームワークでは必要な機能だけを残し、それ以外の機能は隠蔽しているものが多いようです。
3階層アーキテクチャについてもプレゼンテーション層とアプリケーション層の分離にデメリット(処理が分散する)に感じている技術者は多いと思います。
結果的に最近のフレームワークでは以下のような構造になっているものがあります。

フレームワークによっては、こういった構造すら捨ててしまって、昔のサーブレット程度の機能のみにしてしまっているものもあります。
WEBアプリケーションが生まれてから何十年とたちますが、アプリケーション構造はいまだに発展途上といっても過言ではないと思います。
時代の流れをしっかりと把握し、最善と思われるアプリケーション構造を作るようにしたいですね。