モデルもしくはサービスレイヤーに関する補足

http://events.php.gr.jp/events/show/91での発表Zend_Aclの探究の中で、サービスレイヤーについて少し触れました。
id:m_noriiさん、感想ありがとうございます。

サービスレイヤーについては、発表内容とは全然違うけど、今設計的に悩んでる部分があって、サービスレイヤーって実は2種類あるのかなぁと。

コントローラーよりのサービスと、モデルよりのサービス。

コントローラーよりのサービスは、複数コントローラーに共通する機能を提供するもの。今回のAuth、ACLもそうだし、他の人の発表でもあったけど、CSRF対策コードの埋め込みなんかもそうかと。

モデルよりのサービスは、複数モデルにまたがる1トランザクションを扱うもの。

PofEAAにある、購買トランザクションの例なんかまさにそれかと。

これらをいっしょくたに「サービス」として扱うのはまずい・・・というかわかりにくい、美しくないかなぁと思ったりしている。

・・・発表とは全然関係ない、自分の設計に対する思惑ですが^^;;;

http://d.hatena.ne.jp/m_norii/20100306/1267890641

システムで、サービスという言葉で語られる内容はいろいろあります。
私が発表で言及していたのはPofEAAでいうサービスレイヤーですが、id:m_noriiさんが言及されている

  • アプリケーション層でのサービス
  • モデル層でのサービス

という切り口で考えると、

アプリケーション層での共有サービス

Zend Framework的には

  • 小さな共有機能ではアクションヘルパー
  • アプリケーション全体の動作を横断する処理部分はフロントコントローラーのプラグイン
  • 設定やモジュールに依存する内容については、Zend_Applicationのプラグインリソース
  • アプリケーションの動作にオリジナルの仕様を入れるときは独自ライブラリもしくは、モジュール

といった選択肢を検討することになると思います。

モデル層でのサービス

一方モデル層でのサービスについては、PofEAAやDomain-Driven Designなど、まとまったアーキテクチャ毎に定義が違っていたりするので、用語的には整理する必要があります。*1
まず、OOPでは、ビジネスモデルをモデリングしたオブジェクトに、振舞いを持たせるわけですが、明らかにオブジェクトに絡まない振舞いというのもあります。*2

例えば購入トランザクション

Domain-Driven Designでは、購入トランザクションドメインロジックそのものではあるが、Entityの振舞いではない。違うみかたをすると、Entityに対する振舞い。そういうときに、要素として"サービス"を使います。これは、データベースには現れない、つまりデータ抽象を持たないビジネスロジック。DDDでは、エンティティに含まれるべき振舞いは積極的にエンティティに含め、そうでないものはサービスとして実装していきます。そして、オブジェクト間の動作を含めて、DDD/Aggregateに集約するという考え方をします。
同じくモデル層で、サービスレイヤーに似た、トランザクションスクリプトというアーキテクチャもPofEAAでのメジャーな方法論の一つだと思います。これは、複数データモデル間の振舞いを、ユースケースモデリングしていく方法論かと。
いずれにしても、DDDに限らず、ビジネストランザクションとしてのサービスもまたドメイン層の中身であるということは言えます。これはPofEAAのサービスレイヤーでいうサービスとは分けて考えます。
ただ、トランザクションスクリプトは、サービスレイヤー的に見えるというか、サービスレイヤーのつもりが、ビジネスロジックを搭載して、トランザクションスクリプトになってしまうということはあるようです。

トランザクションスクリプト*3かサービスレイヤーか

PofEAAでトランザクションスクリプトの例とサービスレイヤーの例がありますが、モデル層でのサービスでも、DDDのサービスやDCIでのロールと、PofEAAのサービスレイヤーとははっきり区別しておく必要があると思います。

日本ではサービスレイヤーよりもトランザクションスクリプトがよく用いられるようですが、これは現在のビジネス仕様をそのままシステム化しようとするためだという指摘もあります。また、やっつけ仕事みたいにコントローラーで複数モデルを操作するトランザクションスクリプトin コントローラーもよく見かけます。モデル=DBという考え方をしてMVCで考えるという軽微なアプリ向けかな。

本題のサービスレイヤーですが、

基本的には、サービスレイヤーにはドメインロジックを含めないというのがセオリーになります。
つまり、アプリケーション層とドメイン層の分離を仲介するだけの極めて薄い層。そのため、ビジネスロジックを含んでしまうトランザクションスクリプトよりも、対象と主体が明確になるサービスレイヤーの方がアクセス制御に向くという話なんだろうと思われます。

*1:"モデル"がそもそも多様な意味で使われる典型ですが

*2:振舞いを分離して実装し、コンパイル時にインジェクションするというのも含めて

*3:データベースのトランザクションのことではなく複数モデルにまたがる処理を請け負う層