Hatena::Groupcatalyst

dann@catalyst このページをアンテナに追加 RSSフィード

2008-04-04

Catalyst+ DIでアプリケーションのCatalystへの依存を切り離す

| 00:31 |  Catalyst+ DIでアプリケーションのCatalystへの依存を切り離す - dann@catalyst を含むブックマーク はてなブックマーク -  Catalyst+ DIでアプリケーションのCatalystへの依存を切り離す - dann@catalyst  Catalyst+ DIでアプリケーションのCatalystへの依存を切り離す - dann@catalyst のブックマークコメント

Catalyst中でDIコンテナを使い、CatalystからService、ModelクラスのCatalyst依存部分を切り離すようにする方法を以下に示します。

やることは至って単純で、

  • BaseのControllerでServiceContainerからServiceクラスを取得
  • ServiceContainerではモジュールのロードタイミングでDIコンテナを初期化しWiringをする

の二つだけです。

これによって、利用者はDIコンテナからServiceを取得すればよくなります。

BaseのController

package MyApp::Base::Web::Controller;
use base 'Catalyst::Controller';

sub service: Private {
    my ($self, $service_name) = @_;
    return MyApp::ServiceContainer->get($service_name);
}

1;

ServiceContainer.

package MyApp::ServiceContainer;
use strict;
use warnings;

use Bread::Board;

our $container;
BEGIN {
    $container = container 'AppContainer' => as {
         service 'Service::Blog' => (
             class     => 'MyApp::Service::Blog',
             lifecycle => 'Singleton',
         );
    };
}

sub get {
    my $class= shift;
    my $service_name = shift;
    $container->fetch($service_name)->get;
}

1;

利用する側のコード

Controller中で以下のように参照する。

$self->service('Service::Blog')->create_entry($title, $content);

これで、ServiceクラスはCatalystに依存することがなくなります。これにより、CatalystはWAFとしての役割にだけ集中すればよく、アプリケーション側はPOPOベースでドメインモデルを構築することに専念することができます。これによって、アプリケーションはCatalystからの依存関係を断ち切ることが出来るようになるため、Testabilityが高くなります。DIコンテナから参照できるオブジェクト(Service、Model)は、Catalystには依存していないため、CLIからもWebAPIからも使うことが出来ます。

ServiceContainerは、今は手で書いてますが、ここの部分は工夫の余地があります。Bread::Boardは旧世代のDIコンテナだという話を前に書きましたが、その理由はこのWiringする部分を一箇所で集中して書かなければいけないからです。JavaでいうAnnotationを使うと綺麗に解決できるので、おそらくPerlでもAttributeで解決できることなんだろうと思います。

DIコンテナはどのような形であるべきかについては、また後のエントリで書きたいと思います。自分がもう少しPerlが分かっていたら、解決策までコードベースで提示できそうなんですが、まだ大分Perlの知識が足りないですね...

トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080404