それが僕には楽しかったんです。

僕と MySQL と時々 MariaDB

MVCに基づいて設計する時に思う自分なりのベストプラクティス

はじめに

最近、ヴァイオレット・エヴァーガーデンスペシャルをみてダメになったけんつです。

割とよく「MVCってなんだ」とか「MVCを採用している開発でどう切り分けたらいいかわからん」と聞かれる事が多いので
自分なりに考え、「こうなっていると最高に嬉しかった、良かった」というベストプラクティスをまとめてみたくなったのでまとめてみます。

MVCは様々なケースで使われていますが、今回は特にWebサーバサイドに限定して紹介していきます。

MVC とは

特にWebサーバサイドの開発に携わってきた人にとっては古くからお馴染みな Model View Controller という
UIをもつアプリケーションソフトウェアを実装するためのデザインパターン

特徴として、内部で扱うデータをユーザが直接参照・編集する情報から分離する構成を取ることが多い。

そして、これはwikipediaにも書いている。
Model View Controller - Wikipedia

次に、一般的に言われているMVCそれぞれの役割についてまとめる。

Controller の役割

一般的にControllerの役割は、ユーザの入力情報に応じて
ModelとViewを制御する役割を持っている。

Model の役割

一般的にModelは、ビジネスロジックに関するものをもたせることになっている。
ビジネスロジック、つまり何らかの値やそれに関連したアルゴリズムを適用した処理などを指す。

View の役割

一般的にViewは表示や、ユーザの入出力が行われるものを制御する。
特に表示、レンダリングに関する事を行う場合はModelを受け取って行うことが多い。

MVCのメリットとデメリット

メリット

MVCを使うことで、ロジック部分とレンダリング部分、それらを制御する部分という
役割がはっきりとしている状態に分離することができるので開発も効率的に行うことができる。

また、MVCはそれぞれModel View Controllerという分類もされるがModelならModelの中でもわけられていることが多く、それぞれに独立性が高くなるため
仕様が変更されても比較的柔軟に対応できる。

デメリット

プロダクトの規模が大きくなると抱える問題も増えてくる。

例えば、規模が大きくなるとビジネスロジックも多くなってしまうため
モデルがFat Modelになってしまいがちで、管理が大変。

これはControllerにおいても言えることで、必要な機能を増やしていくと
Controllerで行うべき処理も増えてしまうため、Fat Controller になってしまう。
この状況を作ると、ControllerとViewの依存性が始めからある程度強いMVCでは変更が難しくなってしまう。

また、ControllerとView、Modelの依存性が強くなってしまいがちで
修正が困難になってしまう場合がある。

MVCを使うためのベストプラクティス


上記に上げた、概要やメリットとデメリットを考慮した上でいくつかポイントを絞って
自分の思うWebサーバサイドに適用できる、MVCのベストプラクティスについてまとめていく。

Controller の使い方

まず、大事なのがModel View Controller で、特に Controller をどう切り出すか。
つまり、どのような処理を Controller にまとめるかということだと思っている。

これを意識することで、Model と Controller の境界をハッキリさせ必要以上に Model にロジックを集約させてしまうことを防ぐ。

では、どのように分けるのがよいかというと以下の一点を考えるとうまく行く。

  • HTTP に関する処理を Controller でのみ扱い、 Model に含めない。


これに尽きると思っている。

確かにビジネスロジックを Model にまとめるのは重要な話だが、Webサーバサイドにおいて
それらと HTTP では、DBなどから取ってくるという処理や、その結果として現れるデータと
Webサーバサイドの根幹にある通信技術というように、そもそもデータとしての属性が大きく異なる。


そのため、Controller では Cookieやセッションに関する処理、Query String や Json POST で渡ってきたデータのバリデーション、それに伴うエラーハンドリングを行う。


このようにすると、ModelにHTTP関連の処理を負わせないため比較的すっきりとまとまる上に
Controller と Model を確実に分離することができるため必要以上に Controller と Model を依存させないという利点をもたらしてくれる。

イメージは下記の画像の感じで
f:id:RabbitFoot141:20181016193500p:plain

URL設計を見なおしてみる。


これは、「MVCと関係無いのでは?」と思うかもしれないが実は上の Controller にHTTP関連の処理をまとめることにも関連している重要な点だと思っている。


上の記述で、HTTP関連のバリデーションを追加すると辛くなってくるのが
Query String を多用する場合で、そのような場合に Controller で実装するメソッドは規模が大きくなってしまいがちになる。
また、それらを多用するとそもそもにそのクエリがリクエストに存在するかどうかから確かめないと行けない場合もあって冗長になりやすい。


それを未然にルーティングで吸収してしまうことで、必要のないHTTPリクエストの時点でエラーをはいてしまうようなリクエストを Controller で扱うことがなくなる。


特に以下のような場合で有効。

ユーザプロフィールを表示するためのルーティングを前提とした場合

/profile?id=xxx&x=a&y=b  <= 一意に情報が定まるものもQuery Stringにしている


/profile/xxx?x=a&y=b <= 一意に定まるものはURLのパスに含めてしまう

この様に、必要となる一意で定まるものをパスに含めてしまうことで
それが確実に存在するように保証する、そしてそれの形式が異なる場合や存在しない場合は
その先、Controllerなどにリクエストを飛ばさないようにする。

これで必要のないバリデーションを行わないようにする。

Controller と Model でやりとりを増やし過ぎない

Controller はその名前の通り、View と Model の流れを管理しているわけだが
Model が増えてくると Controller で呼び出すべき Model が増えて依存性が高くなってしまう。


こうなってくると、後々の修正や変更が大変になってくる上にMVCのメリットである独立性を維持できていない状況が生まれてしまう。

そのような状況を増やさないためにも、ControllerからViewに渡すべき Model が多いと感じた場合はそもそも Model の設計が適切かどうかを見なおすか

Facade パターンを採用することが解決策として挙げられる。

Facade パターンを採用する

複数のModelを参照する必要がある場合、それらをひとつの処理と見なして Facede パターンを適用するのが得策の場合がある。

f:id:RabbitFoot141:20181016194118p:plain

Facede パターンとは、Modelの動作順を守ったうえでControllerで行っていた処理をひとまとめにしているだけだが
Model を多く呼び出す必要がある場合に Fat Controller になるのを防ぐことができる。

おわりに

MVC を Web サーバサイドに適用する場合の、自分が思うベストプラクティスに関してまとめてみたが
これが必ずしも正しいわけでもなく、あくまでも自分が経験してきた中でこれがよいのではという一つの提案程度に見てもらえると嬉しい