MVCフレームワークでWebアプリケーションを作成するとき、肥大化していくモデルをどのように分割すれば良いのか?悩みます。
いつもは適当にやっているのですが、だんだんファイル数、クラス数、メソッド数が増えてくると、把握しづらくなって、辛くなってきますw
他の人のやり方を参考にして、モデルを適切に分割する方法を検討してみました。
●Webアプリとは?
Webアプリの特徴は、インターネットをはさんで、
(1) クライアント側
(2) サーバー側
に分かれていることです。
サーバー側は、
(a) DB(データを置いておく場所。通常はリレーショナルデータベース)
(b) DBのラッパー(CRUDを担当するアプリケーション層)
に分かれています。
で、このラッパー部分(b)に、MVCフレームワークを使っています。
(via もみじ饅頭(こしあん):もみじ饅頭のやまだ屋)
(M) Model … データの処理
(V) View … 表示機能
(C) Controller … 処理を振分ける司令塔(dispatcher)
に分かれています。
一般的に、データの処理(ロジック)は、コントローラーに書かないで、モデルに書くことが推奨されています。
・薄いコントローラー、厚いモデル
・データ加工処理はモデルに担当させる
・コントローラーをシンプルにする
処理の流れがわかりやすくなる
●モデル部分の分割
モデルにプログラムのコードを書いていくと、モデルが肥大化して、コードが読みづらくなります。
肥大化するモデルを分割する方法、パターンがいろいろ考案されています。
ドメイン駆動設計(Domain-driven design, DDD)とはソフトウェアの設計手法
実践ドメイン駆動設計 (Object Oriented SELECTION)
- 作者: ヴァーン・ヴァーノン,高木正弘
- 出版社/メーカー: 翔泳社
- 発売日: 2015/03/17
- メディア: 大型本
- この商品を含むブログを見る
●レイヤー(階層)の導入
コンピューターネットワークの「OSI参照モデル」のように、複雑な処理は、機能を分割すると見通しが良くなりますね?
MVCのモデルも、データ処理の段階に応じて、階層を分ける設計が提唱されていました。
・コントローラーとDBの間のモデルは、「Service」と「DAO」の2層に分割されている。
・「Service」…ビジネスロジックを担当
・「DAO」…データアクセスを担当
入力から出力までの流れで表現すると、次の図のようになる。
更新系の処理を例に、シーケンスを説明する。
1. Controllerが、Requestを受け付ける
2. (Optional) Controllerは、Helperを呼び出し、Formの情報を、Domain ObjectまたはDTOに変換する
3. Controllerは、Domain ObjectまたはDTOを用いて、Serviceを呼び出す
4. Serviceは、Repositoryを呼び出して、業務処理を行う
5. Repositoryは、O/R Mapperを呼び出し、Domain ObjectまたはDTOを永続化する
6. (実装依存) O/R Mapperは、DBにDomain ObjectまたはDTOの情報を保存する
7. Serviceは、業務処理結果のDomain ObjectまたはDTOを、Controllerに返却する
8. (Optional) Controllerは、Helperを呼び出し、Domain ObjectまたはDTOを、Formに変換する
9. Controllerは、遷移先のView名を返却する
10. Viewは、Responseを出力する。
モデルの部分を、
・Service
・Repository
・O/R Mapper
の3層に分割して、役割分担させているんですね。
Repositoryを作成することにより、永続化技術を隠蔽できたり、データアクセス処理を共通化できるなどのメリットがある。
データアクセスの抽象化が必要ないのであれば、Repositoryは作成せず、以下の図のように、Serviceから直接O/R Mapperを呼び出すようにすればよい。
モデルの部分を、2層とか3層に細分化して、役割分担させることができます。
●CodeIgniterの場合
CodeIgniter(PHPのMVCフレームワーク)では、モデルからモデルを呼び出すことが可能です。=モデルを細分化して、再利用することが可能。
・モデルを3層に分割して、役割分担させる。
・汎用性の高い、再利用可能なメソッドを用意する。
ということを実現するために、以下のような構成を考えてみました。
(ViewとControllerのつながりは省略)
・コントローラーには、なるべくロジックを書かない。
・モデルは、
(1)「Service」
(2)「Repository」
(3)「O/R Mapper」(DAO)
の3層に分割する。(名称は何でも良いw)
・Serviceは、コントローラーに対応した機能を受け持つ。
Serviceのファイル数、クラス数は少なくてOK。
Serviceのメソッドは多機能でOK(ごつい層)
Serviceに置かれた機能は、再利用しない前提なので、汎用性は低くてOK
・Repositoryは、1メソッド=1機能のように、単機能でシンプルにする。
Repositoryは、汎用性を高くして、いろいろなServiceから利用できるようにする。
Repositoryは、ヘルパー関数のようにお互いに利用できるようにしてもOK?→これはやめておいた方が良いのかも?
・O/R Mapper(ORM)(DAO:Data Access Object)は、DBをカプセル化する。
DAOからはDBが見えるけど、DAOより上の層にはDBが隠蔽化されている。
DAOは、1テーブル1クラス、1ビュー1クラスで、1対1の対応にする。
DAOには、生のSQLを書いてもOK。
DAOは、scaffoldingで自動生成させておきたい。あるいは、基本的なCRUDメソッドを実装した親クラスを継承するだけでOKにしておく。
・データベース(MySQL)では、ビューを活用する。
DAOで複数のテーブルをまたぐ処理がある場合は、予めテーブル同士をJOINしたビューを用意しておき、1個のビューに対してDAOの1クラスを割り当てればOK。
こんなかんじで、MVCのモデルの分割を試してみようと思います。
あまり複雑な分割方法にすると、面倒くさくなりそうなので、Repositoryを省略した2層分割でも良いかな?
→何か良い方法があったら教えてください。m(__)m
リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)
- 作者: Dustin Boswell,Trevor Foucher,須藤功平,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2012/06/23
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
【追記】2016/08/11
CodeIgniterに関して、「ビジネスロジック(データの処理)を置く場所は、モデル以外でもOK」という参考情報がありました。
(参考)
CodeIgniter初心者の方に知って欲しいCodeIgniterでのMVCについて — A Day in Serenity (Reloaded)
MVCでもっとも重要なパーツはモデルです。いわゆるビジネスロジックやドメインモデルと言われるコードはモデルに記述します。
CodeIgniterでは、MVC以外に「ライブラリ」と「ヘルパー」が存在します。ここで、ヘルパーは関数を定義するものです。クラスを定義するには、ライブラリを使います。
つまり、CodeIgniterではビジネスロジックやドメインモデルを
・application/modelsフォルダに配置する
・application/librariesフォルダに配置する
という方法が考えられます。
データの処理の内容によっては、モデルに置かなくても、ライブラリーやヘルパーに置いておいて、使うこともできますね?
(参考)
GoFのデザインパターンで、Facadeパターンという仕組みを使うと、処理を分散して配置できそうです。
なるほど!こんなふうにして肥大化した処理を分散/スリム化していけるんですね♪
→情報提供どうもありがとうございます。m(__)m