Webアプリケーションのサービスクラスとは何か

JSPサーブレット、サービス、DAO、DTOといった構成のアプリケーションにおけるサービスクラスとは何か」、という問いに自分なりの答えを出すのが、今日の宿題です。

自分の解答

※注意: 以下の文章は言い切り形式で書いていますが、間違っているかもしれません。

ちょろっと調べたのですが、サービスについてそのものズバリの単一な定義を見つけることはできませんでした。なので、いくつかの観点から理解することを試みます。

モデル、ビュー、コントローラにおける「サービス」

アプリケーションをモデル、ビュー、コントローラに分けて見たとき、サービスはモデルに属します。モデルには、データベースアクセスを行うクラスと、システム固有の処理を行うクラスがあります。

サービスは、後者のシステム固有の処理を行うクラスに当たります。

なお、データベースアクセスを行うクラスは、データを保持するクラスと、データベースアクセスを行うクラスに分けられます。前者がDTO*1で、後者がDAOです。

プレゼンテーション層、ロジック層、データアクセス層における「サービス」

アプリケーションをプレゼンテーション層、ロジック層、データアクセス層に分けて見たとき、モデルは、ロジック層とデータアクセス層にまたがっています。

サービスは、主にロジック層のクラスを指します。ただし、DAOをサービスの一部とみなす文脈では、サービスはデータアクセス層にも属しているといえます。

ロジックには、画面作成のためのロジックと、内部処理のためのロジックがあります。前者はレスポンスを作成するため、後者はリクエストを処理するためにあります。どちらのロジックもサービスに含まれます。しかし、画面作成と内部処理のロジックを1つのクラスにまとめると、処理が複雑になるので避けた方がよいといわれます。

ヘルパーとしての「サービス」

サービスは、ヘルパーコンポーネントの一種です。ヘルパーコンポーネントは、ビューやコントローラの補助をし、ビューまたはコントローラから呼び出されます。画面作成のためのロジックを持ち、ビューから呼び出されるヘルパーと、内部処理のためのロジックを持ち、コントローラから呼び出されるヘルパーがあります。

Facadeパターンにおける「サービス」

上のページでは、データベースアクセスから画面の制御まで何もかもを一緒に入れてしまった「マジックアクション」*2クラスに、Facadeパターンを適用する解説が行われています。

まず、肥大したサーブレットから、データベースアクセスの操作をDAO、コネクション管理の操作をユーティリティとして、分離します。DAOとユーティリティは、どちらも汎用性があり、他のクラスからも使われる処理であるという共通の特徴を持ちます。次に、サーブレットに残った部分から、コアの処理と考えられる部分を別のクラスに分離し、インタフェースを介してアクセスするように変更しています。

逆説的ですが、ここで切り離される部分が、サービスです。まとめると次のようになります。

  • サービスは、呼び出すためのインタフェースの仕様が明確である
  • サービスは、コントローラからサービスインタフェースを介して呼び出される
  • サービスは、内部にデータベースアクセスを行うクラスへの呼び出しを隠蔽している

サーブレットに残るのは、サービスインタフェースの呼び出しと、画面の制御のみです。

余談ですが、サービスインタフェースを導入するメリットが、上記のサイトで次のように紹介されています。

1. ルールの明確化
2. モックオブジェクトが使える

1.のルールは、「アクションクラスからの業務処理の呼び出しはサービスインタフェース経由でのみ行うこと」 というシンプルなものになります。ルールがないと、アクションクラスから直接BookDaoを呼び出したいという誘惑には勝てず、サービスレイヤを構築することは難しいはずです。

2.の「モックオブジェクト」とは、テスト用の仮の実装を持たせたオブジェクトのことです。BookDaoやBookServiceの実装がまだない状態の時に、BookServiceの仮の実装をモックオブジェクトとして用意することで、BookDaoやBookServiceの実装を待たずにアクションクラスや画面の実装・テストができるようになります。

1.については、たとえルール化されていなくても、コントローラから直接データベースにアクセスしようとは思わないでしょう。しかし、間違ってもアクセスできないように、アーキテクチャによって制約を課した方が親切、という意味だと自分は解釈しました。

2.についても、モックを使ったテストをしたいのでない限り、享受できないメリットです。とはいえ、インタフェースにサービスが分かりやすい名前で一覧化されていれば、アプリケーションの内容を簡単に把握できて、きっとうれしいと思われます。

オマケ1: ユーティリティと比較した「サービス」

アプリケーション内で複数のクラスから使用される共通処理を記述するクラスといえば、サービス以外にユーティリティがあります。そこで、「サービスとユーティリティはどう違うか」という問いへの解答です。

アプリケーションの目的達成に直接的に貢献するクラスがサービス、間接的に貢献するクラスがユーティリティ、と捉えています。たとえば、図書検索システムで、利用者一覧を更新するクラスはサービスになり得ますが、利用者一覧を得る際にデータベースにアクセスするために使ったコネクションを管理するクラスはユーティリティであろうということです。

しかし、次のように、

ユーティリティークラスはさまざまな場所で利用する共通的なコードをまとめたものです。
ファサードはもう少し大きな範囲で、複雑なAPIを適切な順番で実行するための、シンプルな入り口を作るという目的があります。

サルでもわかる 逆引きデザインパターン 第3章 逆引きカタログ J2EE編 Facade(ファサード)

入り口をまとめて実行順序を保持する、という役割に着目した考え方もありうるかもしれません*3

オマケ2: ロジックと比較した「サービス」

「クラスに対して、なぜロジックではなくサービスという名前が採用されたか」という問いもあったので、そっちも。

サービスはコンポーネントの種類の名前であり、ロジックはコンポーネントが配置される層の名前です。クラスに付ける名前としては、前者が妥当だったのでは? と推測しました。

# 全然違うかも・・・

ところで、このようなことを常に意識すべきなのか

アプリケーションの規模や目的によっては、特に気にしなくても大きな問題にはならないようです。たとえば、ごく小さいアプリやサンプルアプリ、(アーキテクチャ検討を目的としない)プロトタイプなどであれば、「なんとなくフロントエンド、なんとなくバックエンド、その間」くらいの感覚で作っても、大丈夫ではないでしょうか。

ギブアップ!

誤解を晒した、みたいになってしまいました・・・よかったら、どなたでも、間違った認識にツッ込んでください。

*1:他の文脈では、DO、Bean、POJOなどと呼ばれる?

*2:リンク先がStrutsの話だからアクションと呼んでいるけれど、今の文脈でいうとサーブレット。コントローラの役割を持たせるクラス

*3:上記はファサードの説明であり、サービスの説明ではありませんが・・・