Curryの特徴
MVCアーキテクチャ
CurryはMVCアーキテクチャを基本としています。
Mはモデルを指し、システムを構築する上での事象を具現化した概念です。具体的にはメインロジックとデータを表します。
Vはビューを指し、ユーザーインターフェイスに関わる処理を受け持つ概念です。具体的にはHTMLの生成を行います。
Cはコントローラーを指し、クライアントからのリクエストを直接処理し、モデルやビューを制御します。
Curryではコントローラーがリクエストを受け、その情報をもとにモデルに処理を依頼し、コントローラーはその結果を受け取り、ビューにその結果を渡し、それをもとにビューがHTMLを生成する。という流れが基本となります。
コントローラーがモデルとビューの間に立ち、仲介をする役割を担うわけです。
また、クライアントよりのリクエストやセッションを直接操作するのはコントローラーのみで、モデルやビューはコントローラーよりその情報をパラメーターとして受け取って処理を行います。モデルやビューは直接リクエスト情報は参照しません。
本来のMVCはビューとモデルの間に直接のやり取りが発生します。
しかしCurryではビューにロジックが少しでも入り込む危険性のある手法を避けるため、上記のような概念を基本としています。
これは逆にコントローラーにロジックが入り込む危険性を生むことにもなるため、実装上は注意が必要なのも事実です。
軽量・シンプル
有力なフレームワークは基本となる処理部分が膨らんでおり、処理の軽快さに欠ける欠点があります。あらゆる場面に対応するため、機能を詰め込んでいるためです。
しかし多くの場合はそのような多彩な機能を必要としません。
Curryではリクエストからユーザーロジックへ到達するまでの処理を必要最小限に削ぎ、軽量化しています。
またWebでは必須と言えるデータベースアクセスの部分も処理を単純化し、処理コストを極力抑えています。
フレームワークを利用する場合でも、フレームワークのソースを追いかける必要のある場面は発生しがちです。
Curryでは可読性を意識してソースの記述を極力シンプルにしているため、解析のしやすさも一つの強みです。そのため、フレームワーク自体のカスタマイズもしやすくなっています。
規約と設定のバランス
フレームワークの使い勝手を左右する要素として、規約と設定が存在します。
Ruby on Railsの流れを汲む、CakePHPような規約を重視したフレームワークの場合、規約に従ったコーディングにより、迷いもなく、スムーズな開発が可能です。しかしこれは柔軟性に欠けるという弊害もあります。また大規模なシステムには対応がしにくいということもあります。
逆にZendFrameworkのような、規約に乏しいフレームワークの場合、ある程度実装者が考えてコーディングを行う必要があり、コーディング量も増え、開発速度は少し遅くなりがちですが、柔軟性に富み、実装者のコーディングスタイルに合わせたコーディングが出来、大規模なシステムにも対応しやすいメリットがあります。
フレームワークの柔軟性を高める要素として、設定を利用する方法があります。
設定ファイルやクラスの設定などを利用し、あらゆるコーディングスタイルに対応するという手法です。
Curryは開発効率と柔軟性の両立を目指し、またそのバランスをユーザーで決めることができるように設計しています。
基本は規則に従ったコーディングを行うことで開発効率を重視しています。
が、設定ファイルやクラスであらゆる設定が可能になっており、柔軟性も確保しています。
各種命名規則やディレクトリ構成まで設定で変えられるようになっています。
モデルの自由度
MVCにおいてモデルはメインロジックを担当するというのは動かない思想です。
しかし、メインロジックと一言に言っても幅が広く、モデルの設計が多様であることも事実です。逆に言えばモデルの設計は自由度が高く、その良し悪しがシステムの良し悪しに直結すると言っても過言ではありません。
フレームワークによってはモデルの実装も規約に則らなければならいようになっているものもあります。典型的なのはRuby on Railsで、モデルクラスはテーブルと1対1に結び付けられており、モデルクラス同士でリレーションを設定するような仕組みで、まさにテーブルを直接操作する感覚でモデルを利用します。
個々のモデルの設計の考え方としてはいくつかが考えられますが、一つは上記のRailsのような、モデルクラス=テーブルのような考え方です。データベースの物理的なテーブルと結び付けられており、モデル同士でリレーションを表す形となります。このような設計は一般的にO/Rマッパーとよばれています。
次に、テーブル単位のクラスではなく、事象の論理的単位にモデルクラスを設計する考え方です。リレーショナルデータベースは複数のテーブルのリレーションにより、論理的なオブジェクトの概念を構成するような性質のものです。そこでモデルクラスをその論理的な単位で作成し、それに関連する処理をまとめるような設計方法です。実はこれが最もオブジェクト指向実装の考え方に則しているといえます。オブジェクト指向においては論理モデル単位にクラス設計を行うため、それに最もマッチします。
その他に考えられるのは、コントローラー単位にモデルクラスを定義するというものです。
あまり好ましくない設計ということになるかもしれませんが、REST的な思想をモデルにまで持ち込み、どちらかと言うとコントローラー単位に付随するライブラリ的な位置づけになります。
Curryではモデルに関して絶対的な規定はありません。
しかし、推奨とするのはO/Rマッパー的な設計です。それを前提にした仕組みは用意されており、これに乗せることでデータアクセスが楽に行えるようになっています。
しかしO/Rマッパーにはいくつか問題があるのも事実です。
Curryではその一つをカバーする仕組みを取り入れていますが、それについては後述します。
O/Rマッパーの功罪とCurryでの位置付け
O/Rマッパーはモデルクラスをテーブルとして扱えるため、最終的なデータの格納場所であるテーブルとのデータのやり取りをプログラミング言語のレベルで記述できるというメリットがあります。これはSQL文を直接書かなくても良いということです。
しかしその事は良し悪しで、裏で発行されているSQL文が見えないという問題点があります。SQLに精通した実装者であれば、発行するSQLを想像した上で、それを書く手間を省く、または記述ミスをなくすという程度のメリットとしてO/Rマッパーを利用するでしょう。
しかし、SQLがあまり得意ではない実装者の場合は、クラスの仕組みに頼るという形になり、結果として得られるデータが正しければ問題を感じないため、実際に発行されているSQLが非効率的なものであったとしても問題を感じません。これが結果として世のWebアプリケーションの品質を下げる結果につながる懸念は捨て切れません。
また、フレームワークの用意する仕組みでは複雑なSQLには対応できず、少し複雑な問い合わせが必要になると、SQL文を直接書かなければならないという結果になります。そうすると、そもそも複雑なSQLを書かなくてもいいようにテーブル側を設計するという考え方に結びつき、結果、正規化からかけ離れた非効率なテーブル設計を生み出す事になりかねません。必ずしもそうではないかもしれませんが、これもWebシステムの低質化につながる可能性を含んでいるのではないかと思います。
Curryでは開発効率を考慮し、O/Rマッパーのような考え方を取り入れていますが、どちらかと言うとSQL生成の補助機能的なもので、最終的なSQL文を想像しながらそれをPHPのコーディングに落としていくような実装になるようになっています。
また簡単な設定のみで、発行されるSQLがログとして出力されるようになっており、常にSQLを確認しながら開発を行うことができます。
4層アーキテクチャ
CurryはMVCを基本としていますが、MVCも万能ではありません。
MVCで陥りやすい間違ったコーディングとして、コントローラーにメインロジックが入り込むということがよくあります。
そもそもMVCの概念や正しい実装を理解していない場合は別として、システムによっては必然的にそうなってしまう場合があります。
モデルはデータ操作を含めたロジックを担当するというのは前述のとおりです。
Curryでも推奨しているようにモデルをテーブルオブジェクトとしての位置づけにした場合、モデル=データという構図が出来てしまい、メインロジックの行き場がなくなります。そのロジックというのがテーブルより取得したデータを加工する意味合いのロジックであればモデルに含めてしまえばよいですが、データに一切かかわらない処理ロジックがある場合に困ります。そうするとロジックの行き場としてコントローラーという、MVCの本来の有るべき姿から逸脱してしまう事になってしまいがちです。
また複数のテーブルの更新を一連の処理として整合を保つ「トランザクション」という考え方が存在しますが、一つ一つのモデルに対する処理をトランザクションとして統括するのはモデルより一層上となるはずです。しかしモデルクラスとテーブルを1:1とした場合、それを統括するロジックはどこに書くかという問題が出てきます。そうすると最もメインとなりそうなモデルを選んでその中に記述するのか、あるいはコントローラーで統括するのか、という妥協を含んだ判断になってきます。これはどちらも得策とは言えません。
これらの事から、そもそもモデルとコントローラーという2層しかないことに無理がある事は否めません。そこで、モデルとコントローラーの中間に位置する層を置くことで対応するという事が考えられます。
Curryではこれを「サービス」と呼び、その仕組みを組み込んでいます。
前提として、モデルクラスはテーブルと結びつけます。その上でサービスはデータのトランザクション管理とロジックという2つの役割を持たせます。
Curryではサービスもクラスで表現し、それを実現する仕組みを用意しています。
本来のオブジェクト指向プログラミングにおけるモデリングはサービスという単位で設計し、モデルはデータという位置づけです。
RESTへの対応
RESTについての詳しい解説はここでは行いませんが、RESTにおいては「リソース」が重要なキーワードとなります。RESTで言うリソースとはWeb上に存在する個々の情報の事です。
Webの利用者はブラウザにURIを入力し、情報を得ようとします。URIとはWeb上に散らばる無数の情報の中から特定の一つを指し示すものです。これは当たり前の事のようですが、RESTを理解する上で重要な事です。
そしてHTTPにはリソースを操作するメソッドが存在します。全部でたった8つしかありません。
中でも頻繁に使われるのはGETとPOSTですが、この2つについても本来のメソッドの正しい意味合い通りに使われていることはあまりありません。これらも含め、メソッドを正しく使おうというのがRESTです。中でもGET、POST、PUT、DELETEの4つです。
リソースを一意に表現するのがURIですが、そのリソースに対して何を行うのかを示すのがメソッドの役割です。
GETは現時点のリソースを取得します。
POSTはリソースを追加します。
PUTはリソースを更新、または追加します。
DELETEはリソースを削除します。
実情はこれらすべての操作はGETまたはPOSTですべて行われていることがほとんどですが、RESTfulなアプリケーションを構築する場合はそうではなく、メソッドを正しく使います。
CurryにはRESTful実装を行うための簡易的な仕組みが存在します。
HTMLの仕様上、フォームでPUTやDELETEは送信できません。そのためCurryでは擬似的にPUTやDELETEが使えるように設計しています。リソースを表現するアクションという単位を更に4つに分割し、同一アクションに対するリクエストをHTTPメソッドで切り分けることができます。
通常はURIはコントローラーのアクションと1対1で結びつき、URIの指すパス指定の第2セグメントがアクションを表しますが、REST実装ではアクション名+HTTPメソッド名がアクソンメソッド名となり、例えばhogeアクションに対してPOSTメソッドでアクセスする場合は、hogePostというアクションメソッドが対応することになります。