読者です 読者をやめる 読者になる 読者になる

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

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

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

プログラミング

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

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

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

 

●Webアプリとは?

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

(1) クライアント側

(2) サーバー側

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

 

サーバー側は、

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

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

に分かれています。

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

 

MVCフレームワークは、

(M) Model … データの処理

(V) View … 表示機能

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

に分かれています。

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

 

www.slideshare.net

 

qiita.com

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

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

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

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

 

●モデル部分の分割

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

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

 

ドメイン駆動設計 - Wikipedia

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

 

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

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

 

 

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)