DIコンテナとドメインモデルの相性の悪さ | Ouobpo

DIコンテナとドメインモデルの相性の悪さ

 Seasar、SpringなどのDIコンテナを使っていると、ドメイン層の実装はデータ(Entity/Dto)と振る舞い(Service/Logic)に分ける、いわゆるトランザクションスクリプトのスタイルになりがちだ(参照(1)(2))。理由は、1つにはドメインモデルの設計/実装そのものの敷居が高いこともあるが、そのハードルを乗り越えても、DIコンテナそのものがドメインモデルと馴染みにくい側面があるからだと思われる。

DIコンテナはエンティティやDTOにDIできない
 その側面とは、次の通り。
DIコンテナは設計思想からしてファクトリの役目をするものであるため、DIコンテナを使う場合、インスタンスの生成は基本的にDIコンテナが担当することになり、コンポーネントに必要なオブジェクトはすべてDIで渡されるような設計に誘導されてしまう

 DIを使ったWebアプリケーションのアーキテクチャは、まずリクエストを受け取ったときにDIコンテナがルックアップされ、コンテナから取得したコンポーネント(たいていはActionかService)に処理が委譲される。以後は、コンポーネントの依存性が設定済みの完結したDIの世界で処理が行なわれる(Teeda、S2Struts、Spring MVCなど、最近のDI対応したWebアプリフレームワークを使っていると、リクエスト取得時のDIコンテナ・ルックアップもフレームワーク側でやってくれるので、アプリ開発者はDIの世界しか知らなくていい場合がほとんどだ)。

 逆に、DIの中の世界からコンテナをルックアップしなければならないような設計は、Service Locatorパターンと同じになってしまい、コンポーネントがPOJOでなくなりテスト容易性を損ねるため、悪い設計とされる。

 DIベースのドメイン層では、アプリ開発者が意識的にコンストラクタを呼び出してインスタンスを作るのは、エンティティかDTOだけだ。これらが単なるデータオブジェクトにしかならないのは、1つには、コンテナ外で作られたオブジェクトには構造的にDIがしにくいからだろう。

ドメインモデルはエンティティ(=ドメインオブジェクト)が主体の世界
 しかし、ドメインモデルは、非常に単純化しすぎた言い方をすれば、トランザクションスクリプトのEntityとLogicを1クラスにまとめたようなプログラミングモデルになる。LogicがDIで繋がることにより実現されていたドメインロジックをそのままEntityに移植しようとすると、同じようにやはりEntityに対するDIが必要になる。

 (HibernateなどのO/Rマッパを使えば、ドメインオブジェクト(= Entity + Logic)を集約の単位に数珠繋ぎで読み込むことができるので、それだけである程度リッチなドメインモデルを作成可能だ。しかし、DBアクセスのパフォーマンス・チューニングが難しいことや、DDDが示すように、集約の中から他の集約ルートのドメインオブジェクトやサービスを参照したいこともあるため、やはり外のコンポーネントが欲しくなることがある。また、S2Daoなど実際の現場で人気のDAOフレームワークは原理的にO/Rマッパとは違うもので、DAOパターンを採用した場合はそもそもこの議論が当てはまらない)

 上記の相性問題は、とくにSeasar2のSMARTデプロイのように、DI設定が完全に自動化されて細かなカスタマイズがしにくいテクニックを採用した場合に顕著になる。しかし、開発生産性を考えたときに、SMARTデプロイのような優れたテクニックの活用は外せない。

 こうした相性の悪さから、せっかくドメインモデルをやってみようと思い立っても、既存の道具立てとしっくり馴染まず、結局諦めてしまうということもあるのではないかと思う。