MVC とは何かを 1 から学ぶ で投稿した内容の続きとして、MVC の問題点についてどう対処するか、についてまとめていきます。
MVCの問題点 (再掲)
- 業務で扱うデータ要件が複雑になればなるほど、Model が肥大化していく (今回のテーマ)
- 入力に応じた出力要件が複雑になればなるほど、Controller と View の依存性が高くなる
Model の肥大化とは…
例えば、Issue 管理のための Web アプリケーションを作成するとします。
Web の画面から、ユーザが Issue を閲覧・登録・更新・削除出来るようにする場合、以下の実装を担当するのはどこでしょうか。
- DB に格納された Issue から、未完了のものを取得する
- ユーザが更新 / 削除するために選択した Issue の詳細情報を取得する
- ユーザが更新 / 削除するために選択した Issue が完了済でないかチェックする
- 画面から更新された Issue を DB に反映する
…答えはすべて Model です。
Controller はこの Model の状態を View に渡し、View は Model の状態を参照して画面に返すだけなので、上記のようなビジネスロジックを実行し、データ ( ここでは Issue ) を操作するのは全て Model となります。
このように業務機能や、扱うデータ要件が複雑になるほど、Model が担う役割が多くなる = 肥大化することは容易に想像がつきます。
Model 肥大化 への対処
レイヤアーキテクチャの導入
そこで、 MVC のアーキテクチャパターンに、アプリケーションのレイヤ構成を当てはめ、肥大化する Model を分割します。
MVC の設計上、Model が担う役割自体を減らすことは出来ないので Model の中の役割分担を明確にし、煩雑に肥大化していくことを防ぎます。
レイヤ化
レイヤ化にも色々な種類が存在しますが、例として 実践ドメイン駆動設計 で述べられている 以下の 4 層をもとに整理していきます。
- UI 層 (プレゼンテーション層)
- アプリケーション層
- ドメイン層
- インフラストラクチャ層
UI 層 (プレゼンテーション層)
- ユーザのリクエストを受付け、レスポンスを画面に表示する層。
アプリケーション層
- アプリケーションをコーディネートする層。プレゼンテーション層から受取ったリクエストをハンドリングし、ドメイン層を使ってアプリケーションを実行する。
ドメイン層
- Domain Object を持ち、Domain Object に対するアプリケーションのビジネスロジックを実行する。
インフラストラクチャ層
- ドメイン層の実装を持つ層。Domain Object に対する CRUD 操作の実装を提供する層。DB に接続し、データの CRUD 操作を行うことで DB を永続化する。
MVC との対応関係
- UI 層 : View
- アプリケーション層 : Controller
- ドメイン層 : Model
( インフラストラクチャ層 : Model が 保持する Domain Object に対する CRUD 操作の実装 )
ドメイン層とインフラストラクチャ層の分割
ここで更に、肥大化する Model ( = ドメイン層 ) を更に以下の役割に分割します。
##ドメイン層
-
Domain Object
ビジネスロジックを実行する上で必要な資源。Entity。POJO。 -
Repository
Domain Object の CRUD 処理を行うインターフェース。
Repository は DB アクセスの実装は持たず、ルールの定義のみ。 -
Service
アプリケーションのビジネスロジック。
インフラストラクチャ層
-
RepositoryImpl
ドメイン層で定めた Repository の実装をもつ。
O/R Mapper を使用してDB に接続し、Domain Object の CRUD 処理を行う。 -
O/R Mapper
DB と Entity のマッピングを行う。
各レイヤ間の関係は以下の図の通りです。
上記の図で押さえるポイントとしては 2 つです。
- ドメイン層 (Model) は アプリケーション層から使用・参照される
- ドメイン層 (Model) は インフラストラクチャ層のインタフェースを定義
つまり、ドメイン層はどこの層にも依存していないことになります。インフラストラクチャ層の実装が変わったとしても、ドメイン層ではインタフェースを定義しているのみのため影響を受けません。
こうしてアプリケーションをレイヤ化し、分割することで Model が疎となり、煩雑な肥大化を防ぐことが出来ます。
例えば Java の Spring Framework を使用すれば @Service や @Repository アノテーションを付けるだけでフレームワークが上手く処理してくれるのですが、書く側が理解して設計できないと、プログラムが少し複雑になっただけで応用が効かなくなりそう ( 効かなくなって自分の首を締めた経験あり ) ということで整理してみました。
参考
DDDを踏み外してからもう一度踏み出してみた - hatajoeのブログ
実践DDD本の第4章「アーキテクチャ」 ~レイヤからヘキサゴナルへ~ (1/4):CodeZine(コードジン)
2.4. アプリケーションのレイヤ化 — TERASOLUNA Server Framework for Java (5.x) Development Guideline 5.3.0.RELEASE documentation