Web アプリケーションのロジックを手続きとして実装する話

yusukebe のObject::Containerを応用したModel呼び出し のあたりをよんでの感想ですが。

一時期 Catalyst の影響もあって、モデルのインスタンスつくったりなんだりもしていたのですが、結局のところウェブアプリケーションにおいては、モデルをがんばってオブジェクトにモデリングしようとか考えるよりも、つまり手続として扱った方がしっくりくるな、ということに自分の中でなりまして、最近は、ごちゃごちゃ考えるのをやめて、手続きとして実装しています。

yusukebe の例をベースにはなしをすすめると、コントローラを以下のようにかきます。

package MyApp::Web::Controller::Root;
use Mojo::Base 'Mojolicious::Controller';
use MyApp::M::Entry;

sub index {
    my $self = shift;
    my $entries = MyApp::M::Entry->get_recent_entries();
    $self->stash->{entries} = $entries;
    $self->render();
}

1;

モデルはこんなかんじ。モデルといっても実態は手続きであって、状態はもちません。必要な情報はすべて引数でわたします。 引数は Data::FormValidator をラップしたものでバリデーションされます。

package MyApp::Model::Entry;
use My::Great::Validator;

sub get_recent_entries {
    my $valid = validate(state $r = {
        required => [qw(page rows)],
        defaults => {
            rows => 30,
            page => 1,
        },
    });
    ...;
    return $entries;
}

1;

なぜこういう風にしているかというと、以下のような理由からです。

  • モデルオブジェクトを作成して永続化すると、他のユーザーの情報などが別のリクエストセッションにひきついでしまうケースがままある
    • そんな奴いないだろ、と思うかもしれませんが、わりとやっちゃう事例をたまにみます。
    • そして発生すると大ダメージですね
  • そもそも綺麗にモデルをオブジェクトとして設計するのは大変
  • 高品質に一貫性をもって設計できれば勿論いいのですが、現実的にはそんな暇はないし、そんな技量のある人ばかりではないですよね
  • この方針だと、クオリティのばらつきは最小限で抑えられる気がしています。

シャレオツ感は薄いのですが、堅実で、ぼくは好きです。