Catalyst ベースのアプリケーション設計(構想)
Catalyst は Web アプリケーションを開発するときに、フレームワークとしてとても便利です。
ひとつの Web サービスを作ろうとすると、以下のような様々な処理が必要になってきます。
これを、すべて Catalyst にやらせてしまうと、プロセスが太りすぎてしまいますし、アプリケーション設計として不自然です。
結論から先に言ってしまうと、モデルやロジックだけでなく、ヴァリデーション処理なども Catalyst から分離してしまい、Catalyst はリクエストのディスパッチと View だけを担当するのがスマートで、拡張・メンテナンス・テストがしやすいやり方なのかなと、最近うっすらと考えています。Catalyst 在りきではなく、アプリケーションの一部分を Catalyst が担うといった感じですね。
本題とは逸れますが、現在 mst 氏が Reaction という Catalyst ベースのフレームワークを開発中のようなので、また新しい動きが期待されます。
Catalyst を薄くするという点については、jshirley 氏も Catalyst の Controller は薄ければ薄いほど良いと言っています。*1
そこで、Catalyst を使って何個かアプリケーションを作ってるうちに、なんとなく辿り着いた(今のところ)クラスの分け方を以下に紹介します。
- ユーティリティクラス
- path_to() など、ユーティリティ関数もろもろ。自前で実装。Catalyst から分離。
- コンフィグクラス
- 全てのクラスから使えるように、Catalsyt から分離。自前で実装。Catalyst::Plugin::ConfigLoader を参考に作成。データベースアクセスクラスや API クラス、Catalsyt から呼べるようにします。実装については別エントリに書きます。
- ロギングクラス(Log::Dispatch / Log::Log4perl など)
- 全てのクラスから使うログ出力用クラス。
- データベースアクセスクラス
- なんらかのキーを受け取って、ハンドラや O/R マッパーのインスタンスを返します。マスター/スレーブ構成や複数コネクションへの対応はここでします。呼び出し側で接続先を気にしなくも済むようなインターフェイスを用意してあげます。
- CRUD 処理クラス
- データベースアクセスと API の中間層です。データベースアクセスクラスのインスタンスをメンバに持ち、各エンティティの CRUD 処理を担当します。渡されたデータ形式のチェックと、登録・読込・更新・削除だけしかしません。その他のいかなるロジックも持たせません。データを直接いじるのはこのクラスだけです。
- API クラス
- CRUD 処理クラスのインスタンスを持ちます。CRUD 処理クラスの各メソッドを叩き、ロジック部分を担当します。コマンドラインプログラムと Catalyst と両方から呼ばれます。出来る限りブラックボックス化します。
- ウェブインターフェイスクラス(Catalyst)
- Catalyst の担当部分です。Catalyst::Model::MultiAdaptor を使って API をインスタンスに持ちます。主にディスパッチ、認証、セッション、View などを担当します。コントローラーはとにかく薄くし、ロジックは API に追い出します。
- ジョブキュークラス(TheSchwartz など)
- メール送信、画像変換など、時間のかかる処理を Catalyst でやってしまうと、送信先の SMTP サーバーが重かったりすると、ブラウザのレスポンスが遅くなってしまいますし、FCGI のプロセスが占有されてしまいますので、バックグラウンドで非同期に行い、処理の終了時になんならかの通知を出す、という実装にします。
と、大体このような感じです。変な部分や、「こうした方がもっといいよ」などありましたら、ご意見いただければと思います。
- [追記 06/02]
- ロギングクラスを忘れていたので加筆しました。
- [追記 06/03]
- データベースアクセスクラスではなく、データアクセスクラスにして、その子クラスでデータベースやファイルなど色々なストレージに対応出来るようにした方が良いですね。