SlideShare a Scribd company logo
Catalyst から Model を切り離せ - MVC の M のあるべき姿  - Dann [email_address]
アジェンダ WAF における MVC と WAF と M の関係 Catalyst(WAF) から M を切り離す方法 MVC の M を構築するためのアーキテクチャ
WAF における MVC と WAF と M の関係
WAF の役割 WAF は基本的に Web/Http の世界の仕事をする セッション管理 認証処理 URL ディスパッチャ フォームバリデーション Controller としての役割 M と V のノリとしての役割 モデルへ処理の委譲 モデルの処理結果を View にフォワード
WAF の役割でないこと モデルの管理 モデルのビジネスロジック モデルの Caching の機構 Etc.. Catalyst で良くない点 Catalyst::Model::* M が Catalyst に依存している Model をプラガブルに拡張するのは、 AF の役割
WAF と Model の関係のあるべき姿は? 基本は、 Model は Web の世界からは切り離されていること MVC の M は  POPO(Plain Old Perl Object)   で構成する Why ?
M を WAF から切り離すべき理由 Testability が向上する モデルの Unit Test が Catalyst を使わずに可能に テストの実行速度が早くなる Web のコンテキスト以外での再利用 が可能になる POPO Service POPO Model (Schema::* など) CLI (App::CLI, App::Cmd) WebAPI (Catalyst::Controller::Resources) Catalyst::Controller M
とはいっても、 WAF では一部 M を知る必要がある。 どうするか?
Catalyst(VC) と Model(M) の関係別の Model の切り離し方
M と VC の機能の共有関係 (a) M と VC 間に機能共有が必要でない場合 (b) M,V,C の全てで共有したい機能がある場合 Ex)  国際化の機構( Controller 、 View 、 POPO Model どこでも必要) ( c ) V, C の一部で M が必要な場合 Ex)  認証の機構 M のロジックはいらないが、データは必要 このパターン別に、モデルの切り離し方、 VC との繋ぎ方がある。
M でしか必要のない機能の場合 モデルの切り離し方 Catalyst の Model にせず 、 POPO だけを使う POPO Service POPO Model Catalyst Controller Catalyst View M V C
(b) WAF と APP( モデル ) で機能 を共有したい場合 例えば国際化 V でも C でも国際化したい  e.g (c.loc) M でも国際化したい  eg. loc(‘message’) モデルの切り離し方 アプリケーション (POPO Model) 側で共通の機能  ( 国際化)を実装 し、プラグインでアプリケーションの機能を参照する Plugin::I18N --> MyApp::I18N 国際化方法 POPO Model(MyApp::Model::Hoge) からは MyApp::I18N で国際化 . E.g) loc View では、 Plugin::I18N で国際化 . E.g) c.loc
(b) のパターン POPO Model Catalyst Controller Catalyst View MyApp::I18N Catalyst Plugin::I18Nx C M V
(c) WAF の一部でモデル が必要な場合 例えば、認証の機構 プラグイン側では認証の仕組みが必要で一部モデルが必要。モデル側では認証の仕組みは不要というケース。 ユーザークラスは Catalyst のモデルじゃないと Catalyst の Plugin から参照が… モデルの切り離し方 Catalyst(WAF) の世界にモデルを閉じ込めるのがいい Catalyst::Model::Adaptor が最適
(c)  のパターン POPO User Catalyst ::Model::Adaptor Catalyst::Plugin::Authentication ::Store::DBIC   M
MVC の M を構築する アーキテクチャ
Domain Logic Patterns Domain Model(+Service Layer) Service(workflow logic) Domain Model(domain logic) Logic とデータは同じところに ask ではなく、 tell.  Transaction Script Unit of Work が 1 メソッド Logic とデータの分離 Ask のモデル Table Module ※ 詳しくは PofEAA 参照
Service Layer + Domain Model  「モデル」を Service と Domain Model に分ける Service Layer Application logic(Workflow logic) Domain Model に対して問題を解決するよう処理を委譲 Publish するインターフェースの明示 Controller に見せる処理の明示 トランザクション境界 Domain Model Domain logic 問題領域のロジックは Domain Model に ここに主にロジックを記述
多くの Web アプリケーション における一つの仮定 複雑なビジネスロジックは少ない CRUD の処理をすることのほうが多い CLI が管理用スクリプトとして必要になるケースは多い 重い処理を Job Queue に委譲することは割にある
仮定に基づいた Domain model と Service Layer への疑問 DB の構造を見せないリッチな OO なドメインモデルが必要? 多くの場合不要。無駄に Domain Model をラップすることになる DB だけの場合、 Domain Model=ER モデル で十分 ドメインモデルでは、 DB に依存しない抽象化が必要 ? DB しか使わなければ、抽象化不要 常に Controller は Service Layer を介する必要あり? そんなことはない。直接 Domain Model を触ってもよい。 1 つのドメインモデルのロジックしか必要ないこともある
Service Layer + Domain Model の現実解 Service アプリケーションロジック ( ワークフローロジック)を書く 複数のモデルにまたがるロジック 複数のモデルへ処理をディスパッチする 薄い ロジック サービスは基本的に 1 ユースケースに対応させる Domain Model ドメインロジックを記述する DB のみの場合 一つのテーブル (Schema::X) に依存したビジネスロジック Domain Model= ER モデル とする Controller は、 Service or Model にアクセス Controller にはロジックは記述しない ユースケースが CRUD のみのオペレーションに対応する場合、 Domain Model のロジックに直接アクセスしてもよい
Catalyst にマッピングすると POPO Service POPO Models (With DBIC) Catalyst Controller Catalyst View POPO Models (With DBIC) M これが一番シンプルなマッピング モデルに対する要求は他にもあり、他のマッピング方法もある
モデル( Service+Domain Model) を構築するための色々な要求 POPO モデル毎に簡単な設定情報を渡したい POPO モデルを簡単に WAF から参照したい $c->model()  でのアクセスはなんだかんだで便利(インスタンス生成のロジックが隠蔽されてるから) モデルと WAF を簡単に繋ぎたい POPO モデル間の密な依存関係を断ち切りたい Testability 向上のため DI コンテナ  を使ってモデルを切り離す
DI コンテナ + POPO Model のアーキテクチャ POPO Service POPO Model (Schema::*  など参照 ) Catalyst Controller Catalyst View DIコンテナ Catalyst Plugin DI コンテナで POPO を Wiring Plugin or Controller で DI コンテナを参照し、 WAF と M を繋ぐ
DI コンテナを利用したモデル構築 DI コンテナ利用の利点 モデルは DI コンテナで Wiring されるため、モデルが Catalyst の context に依存することが無くなる モデル間の実装の依存関係が切れる モデル (Service, Model) の参照方法の一例 Controller から直に DI コンテナを参照する $self->model(“Service::Blog”)->create(); プラグイン化して Context から DI コンテナを参照する $c->pmodel(“Service::Blog”)->hello(); Catalyst::Model::Adaptor じゃできない? Catalyst::Model::Adaptor では、 POPO Model と Adaptor が 1:1 対応してしまう
まとめ Catalyst は WAF としては必要十分 モデル (Service, Domain Model) について モデルは WAF から切り離すべき ビジネスロジックはモデルに押し込む MVC を考えるときは、モデルの Testability を中心に考える モデルのアーキテクチャについて Web アプリケーションの多くは、 ServiceLayer + Domain Model の変形でよいのでは Catalyst に不足しているもの WAF と POPO Model を繋ぐ仕組み DI コンテナはそれに対する一つの答え MVC の M の拡張などは WAF ではなく、 AF で
ご清聴ありがとうございました  !
付録
Service Layer+Domain Model の構築に必要な仕組み (Application Framework としての仕組み) モデルを Wiring ・設定情報を Inject する仕組み DI コンテナ (Bread::Board. あと一歩 ) モデルの横断的な関心ごとを切り離す仕組み Caching, Transaction など Moose or Class::Trigger or Attribute で拡張 モデルをプラガブルに拡張する仕組み Class::Component
モデルを Catalyst から 切り離すのに実装が必要なもの ConfigLoader Logger Caching Exception I18N の仕組み

More Related Content

Separate Model from Catalyst

  • 1. Catalyst から Model を切り離せ - MVC の M のあるべき姿 - Dann [email_address]
  • 2. アジェンダ WAF における MVC と WAF と M の関係 Catalyst(WAF) から M を切り離す方法 MVC の M を構築するためのアーキテクチャ
  • 3. WAF における MVC と WAF と M の関係
  • 4. WAF の役割 WAF は基本的に Web/Http の世界の仕事をする セッション管理 認証処理 URL ディスパッチャ フォームバリデーション Controller としての役割 M と V のノリとしての役割 モデルへ処理の委譲 モデルの処理結果を View にフォワード
  • 5. WAF の役割でないこと モデルの管理 モデルのビジネスロジック モデルの Caching の機構 Etc.. Catalyst で良くない点 Catalyst::Model::* M が Catalyst に依存している Model をプラガブルに拡張するのは、 AF の役割
  • 6. WAF と Model の関係のあるべき姿は? 基本は、 Model は Web の世界からは切り離されていること MVC の M は POPO(Plain Old Perl Object) で構成する Why ?
  • 7. M を WAF から切り離すべき理由 Testability が向上する モデルの Unit Test が Catalyst を使わずに可能に テストの実行速度が早くなる Web のコンテキスト以外での再利用 が可能になる POPO Service POPO Model (Schema::* など) CLI (App::CLI, App::Cmd) WebAPI (Catalyst::Controller::Resources) Catalyst::Controller M
  • 8. とはいっても、 WAF では一部 M を知る必要がある。 どうするか?
  • 9. Catalyst(VC) と Model(M) の関係別の Model の切り離し方
  • 10. M と VC の機能の共有関係 (a) M と VC 間に機能共有が必要でない場合 (b) M,V,C の全てで共有したい機能がある場合 Ex) 国際化の機構( Controller 、 View 、 POPO Model どこでも必要) ( c ) V, C の一部で M が必要な場合 Ex) 認証の機構 M のロジックはいらないが、データは必要 このパターン別に、モデルの切り離し方、 VC との繋ぎ方がある。
  • 11. M でしか必要のない機能の場合 モデルの切り離し方 Catalyst の Model にせず 、 POPO だけを使う POPO Service POPO Model Catalyst Controller Catalyst View M V C
  • 12. (b) WAF と APP( モデル ) で機能 を共有したい場合 例えば国際化 V でも C でも国際化したい e.g (c.loc) M でも国際化したい eg. loc(‘message’) モデルの切り離し方 アプリケーション (POPO Model) 側で共通の機能 ( 国際化)を実装 し、プラグインでアプリケーションの機能を参照する Plugin::I18N --> MyApp::I18N 国際化方法 POPO Model(MyApp::Model::Hoge) からは MyApp::I18N で国際化 . E.g) loc View では、 Plugin::I18N で国際化 . E.g) c.loc
  • 13. (b) のパターン POPO Model Catalyst Controller Catalyst View MyApp::I18N Catalyst Plugin::I18Nx C M V
  • 14. (c) WAF の一部でモデル が必要な場合 例えば、認証の機構 プラグイン側では認証の仕組みが必要で一部モデルが必要。モデル側では認証の仕組みは不要というケース。 ユーザークラスは Catalyst のモデルじゃないと Catalyst の Plugin から参照が… モデルの切り離し方 Catalyst(WAF) の世界にモデルを閉じ込めるのがいい Catalyst::Model::Adaptor が最適
  • 15. (c) のパターン POPO User Catalyst ::Model::Adaptor Catalyst::Plugin::Authentication ::Store::DBIC M
  • 16. MVC の M を構築する アーキテクチャ
  • 17. Domain Logic Patterns Domain Model(+Service Layer) Service(workflow logic) Domain Model(domain logic) Logic とデータは同じところに ask ではなく、 tell. Transaction Script Unit of Work が 1 メソッド Logic とデータの分離 Ask のモデル Table Module ※ 詳しくは PofEAA 参照
  • 18. Service Layer + Domain Model 「モデル」を Service と Domain Model に分ける Service Layer Application logic(Workflow logic) Domain Model に対して問題を解決するよう処理を委譲 Publish するインターフェースの明示 Controller に見せる処理の明示 トランザクション境界 Domain Model Domain logic 問題領域のロジックは Domain Model に ここに主にロジックを記述
  • 19. 多くの Web アプリケーション における一つの仮定 複雑なビジネスロジックは少ない CRUD の処理をすることのほうが多い CLI が管理用スクリプトとして必要になるケースは多い 重い処理を Job Queue に委譲することは割にある
  • 20. 仮定に基づいた Domain model と Service Layer への疑問 DB の構造を見せないリッチな OO なドメインモデルが必要? 多くの場合不要。無駄に Domain Model をラップすることになる DB だけの場合、 Domain Model=ER モデル で十分 ドメインモデルでは、 DB に依存しない抽象化が必要 ? DB しか使わなければ、抽象化不要 常に Controller は Service Layer を介する必要あり? そんなことはない。直接 Domain Model を触ってもよい。 1 つのドメインモデルのロジックしか必要ないこともある
  • 21. Service Layer + Domain Model の現実解 Service アプリケーションロジック ( ワークフローロジック)を書く 複数のモデルにまたがるロジック 複数のモデルへ処理をディスパッチする 薄い ロジック サービスは基本的に 1 ユースケースに対応させる Domain Model ドメインロジックを記述する DB のみの場合 一つのテーブル (Schema::X) に依存したビジネスロジック Domain Model= ER モデル とする Controller は、 Service or Model にアクセス Controller にはロジックは記述しない ユースケースが CRUD のみのオペレーションに対応する場合、 Domain Model のロジックに直接アクセスしてもよい
  • 22. Catalyst にマッピングすると POPO Service POPO Models (With DBIC) Catalyst Controller Catalyst View POPO Models (With DBIC) M これが一番シンプルなマッピング モデルに対する要求は他にもあり、他のマッピング方法もある
  • 23. モデル( Service+Domain Model) を構築するための色々な要求 POPO モデル毎に簡単な設定情報を渡したい POPO モデルを簡単に WAF から参照したい $c->model() でのアクセスはなんだかんだで便利(インスタンス生成のロジックが隠蔽されてるから) モデルと WAF を簡単に繋ぎたい POPO モデル間の密な依存関係を断ち切りたい Testability 向上のため DI コンテナ を使ってモデルを切り離す
  • 24. DI コンテナ + POPO Model のアーキテクチャ POPO Service POPO Model (Schema::* など参照 ) Catalyst Controller Catalyst View DIコンテナ Catalyst Plugin DI コンテナで POPO を Wiring Plugin or Controller で DI コンテナを参照し、 WAF と M を繋ぐ
  • 25. DI コンテナを利用したモデル構築 DI コンテナ利用の利点 モデルは DI コンテナで Wiring されるため、モデルが Catalyst の context に依存することが無くなる モデル間の実装の依存関係が切れる モデル (Service, Model) の参照方法の一例 Controller から直に DI コンテナを参照する $self->model(“Service::Blog”)->create(); プラグイン化して Context から DI コンテナを参照する $c->pmodel(“Service::Blog”)->hello(); Catalyst::Model::Adaptor じゃできない? Catalyst::Model::Adaptor では、 POPO Model と Adaptor が 1:1 対応してしまう
  • 26. まとめ Catalyst は WAF としては必要十分 モデル (Service, Domain Model) について モデルは WAF から切り離すべき ビジネスロジックはモデルに押し込む MVC を考えるときは、モデルの Testability を中心に考える モデルのアーキテクチャについて Web アプリケーションの多くは、 ServiceLayer + Domain Model の変形でよいのでは Catalyst に不足しているもの WAF と POPO Model を繋ぐ仕組み DI コンテナはそれに対する一つの答え MVC の M の拡張などは WAF ではなく、 AF で
  • 29. Service Layer+Domain Model の構築に必要な仕組み (Application Framework としての仕組み) モデルを Wiring ・設定情報を Inject する仕組み DI コンテナ (Bread::Board. あと一歩 ) モデルの横断的な関心ごとを切り離す仕組み Caching, Transaction など Moose or Class::Trigger or Attribute で拡張 モデルをプラガブルに拡張する仕組み Class::Component
  • 30. モデルを Catalyst から 切り離すのに実装が必要なもの ConfigLoader Logger Caching Exception I18N の仕組み