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 に追い出します。
コマンドラインインターフェイスクラス(App::Cmd / Moose::App::Cmd など)
管理用のプログラムや、cron から実行する処理などを担当します。
ジョブキュークラス(TheSchwartz など)
メール送信、画像変換など、時間のかかる処理を Catalyst でやってしまうと、送信先SMTP サーバーが重かったりすると、ブラウザのレスポンスが遅くなってしまいますし、FCGI のプロセスが占有されてしまいますので、バックグラウンドで非同期に行い、処理の終了時になんならかの通知を出す、という実装にします。


と、大体このような感じです。変な部分や、「こうした方がもっといいよ」などありましたら、ご意見いただければと思います。

[追記 06/02]
ロギングクラスを忘れていたので加筆しました。

[追記 06/03]
データベースアクセスクラスではなく、データアクセスクラスにして、その子クラスでデータベースやファイルなど色々なストレージに対応出来るようにした方が良いですね。