浜村拓夫(・∀・)作品集

頭の中にあるイメージを表現できるデザイン力が欲しいです(><)

MVCのモデルを分割する方法

MVCフレームワークでWebアプリケーションを作成するとき、肥大化していくモデルをどのように分割すれば良いのか?悩みます。

いつもは適当にやっているのですが、だんだんファイル数、クラス数、メソッド数が増えてくると、把握しづらくなって、辛くなってきますw

他の人のやり方を参考にして、モデルを適切に分割する方法を検討してみました。

 

●Webアプリとは?

Webアプリの特徴は、インターネットをはさんで、

(1) クライアント側

(2) サーバー側

に分かれていることです。

 

サーバー側は、

(a) DB(データを置いておく場所。通常はリレーショナルデータベース)

(b) DBのラッパー(CRUDを担当するアプリケーション層)

に分かれています。

で、このラッパー部分(b)に、MVCフレームワークを使っています。

 

f:id:hamamuratakuo:20160820095405j:plain

(via もみじ饅頭(こしあん):もみじ饅頭のやまだ屋)

 

MVCフレームワークは、

(M) Model … データの処理

(V) View … 表示機能

(C) Controller … 処理を振分ける司令塔(dispatcher)

に分かれています。

一般的に、データの処理(ロジック)は、コントローラーに書かないで、モデルに書くことが推奨されています。

 

www.slideshare.net

 

qiita.com

・薄いコントローラー、厚いモデル

・データ加工処理はモデルに担当させる

・コントローラーをシンプルにする

 処理の流れがわかりやすくなる

 

●モデル部分の分割

モデルにプログラムのコードを書いていくと、モデルが肥大化して、コードが読みづらくなります。

肥大化するモデルを分割する方法、パターンがいろいろ考案されています。

 

ドメイン駆動設計 - Wikipedia

ドメイン駆動設計(Domain-driven design, DDD)とはソフトウェアの設計手法

 

実践ドメイン駆動設計 (Object Oriented SELECTION)

実践ドメイン駆動設計 (Object Oriented SELECTION)

 

 

 

●レイヤー(階層)の導入

コンピューターネットワークの「OSI参照モデル」のように、複雑な処理は、機能を分割すると見通しが良くなりますね?

MVCのモデルも、データ処理の段階に応じて、階層を分ける設計が提唱されていました。

 

sites.google.com

 

f:id:hamamuratakuo:20160809213024j:plain

・コントローラーとDBの間のモデルは、「Service」と「DAO」の2層に分割されている。

・「Service」…ビジネスロジックを担当

・「DAO」…データアクセスを担当

 

2.4. アプリケーションのレイヤ化 — TERASOLUNA Global Framework Development Guideline 1.0.0.publicreview documentation

入力から出力までの流れで表現すると、次の図のようになる。

f:id:hamamuratakuo:20160809214003p:plain

更新系の処理を例に、シーケンスを説明する。

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を呼び出すようにすればよい。

f:id:hamamuratakuo:20160809214744p:plain

 

モデルの部分を、2層とか3層に細分化して、役割分担させることができます。

 

●CodeIgniterの場合

CodeIgniter(PHPMVCフレームワーク)では、モデルからモデルを呼び出すことが可能です。=モデルを細分化して、再利用することが可能。

 

・モデルを3層に分割して、役割分担させる。

・汎用性の高い、再利用可能なメソッドを用意する。

ということを実現するために、以下のような構成を考えてみました。

(ViewとControllerのつながりは省略)

 

f:id:hamamuratakuo:20160809213557p:plain

 

・コントローラーには、なるべくロジックを書かない。

・モデルは、

 (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

 

 

hamamuratakuo.hatenablog.com 

 

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

 

 


【追記】2016/08/11

CodeIgniterに関して、「ビジネスロジック(データの処理)を置く場所は、モデル以外でもOK」という参考情報がありました。

 

(参考)

CodeIgniter初心者の方に知って欲しいCodeIgniterでのMVCについて — A Day in Serenity (Reloaded)

MVCでもっとも重要なパーツはモデルです。いわゆるビジネスロジックドメインモデルと言われるコードはモデルに記述します。

CodeIgniterでは、MVC以外に「ライブラリ」と「ヘルパー」が存在します。ここで、ヘルパーは関数を定義するものです。クラスを定義するには、ライブラリを使います。

つまり、CodeIgniterではビジネスロジックドメインモデルを

・application/modelsフォルダに配置する

・application/librariesフォルダに配置する

という方法が考えられます。

データの処理の内容によっては、モデルに置かなくても、ライブラリーやヘルパーに置いておいて、使うこともできますね?

 

(参考)

hamuhamu.hatenablog.jp

GoFデザインパターンで、Facadeパターンという仕組みを使うと、処理を分散して配置できそうです。

 

なるほど!こんなふうにして肥大化した処理を分散/スリム化していけるんですね♪

→情報提供どうもありがとうございます。m(__)m