Name
Password

アプリケーションアーキテクチャ標準

  • 2010-02-25 17:27:34

この文書では、Viliで作成した空のYmirプロジェクトを基にアプリケーションを開発する際の標準的なアプリケーションアーキテクチャについて説明します。なお以下の内容はあくまで標準であり、他の規則に従ってアプリケーションを作ることももちろんできます。

アプリケーションの構成クラス

アプリケーションの構成クラスには以下のものがあります。

Page

リクエストを処理するクラスです。通常Pageの_で始まるメソッド(アクションメソッド)がリクエストの処理のエントリポイントとなります。アクションメソッドからはServiceクラスのメソッドが呼び出されます。

インスタンスは1リクエストにつき1つ生成されます。

Service

プレゼンテーションと関連の強いロジックを記述するためのクラスです。通常Pageと1対1対応します。ServiceクラスのメソッドからはLogicクラスのメソッドが呼び出されます。

アプリケーションの規模によっては、Serviceクラスを省略してServiceクラスに書くべき処理をPageクラスに記述しても構いません。

インスタンスはシングルトンです。

Logic

画面と直接結びつかないビジネスロジックを記述するためのクラスです。LogicクラスのメソッドからはHelperクラスのメソッドまたはDaoクラスのメソッドまたはDBFluteのBhvクラスのメソッドが呼び出されます。

インスタンスはシングルトンです。

Helper

ビジネスロジックのうち、データベースアクセスを行なわないロジックを記述するためのクラスです。

アプリケーションの規模によっては、Helperクラスを省略して処理をHelperクラスに書くべき処理をLogicクラスに記述しても構いません。

インスタンスはシングルトンです。

Dao

データベースにアクセスするためのクラスです。S2Daoを直接利用する場合に使用します。DBFluteを利用する場合は基本的に使用することはありません。

インスタンスはシングルトンです。

Entity

データベースのテーブルの行に対応するクラスです。

インスタンスの生成は通常DIコンテナの外で行なわれます。

Dto

画面で指定されたリクエストパラメータを保持したり、画面の動的な表示要素を保持するためのクラスです。

インスタンスの生成は通常DIコンテナの外で行なわれます。

Converter

DtoとデータベースのEntityとの値の変換を行なうためのクラスです。

インスタンスはシングルトンです。

Dxo

リフレクションベースのデータ変換を行なうためのクラスです。リフレクションベースのデータ変換は挙動をカスタマイズしにくい、リファクタリングに弱いという特徴があるため、Ymirの標準アプリケーションアーキテクチャでは使用を推奨しません。

インスタンスはシングルトンです。

Handler

例外がスローされた場合の処理を記述するためのクラスです。

インスタンスは1リクエストにつき1つ生成されます。

Constraint

制約チェックを行なうためのクラスです。

インスタンスはシングルトンです。

Interceptor

S2Containerのインターセプタクラスです。

インスタンスはシングルトンです。

Bhv

データベースにアクセスするためのクラスです。DBFluteを利用する場合に使用します。

インスタンスはシングルトンです。

種類 サフィックス パッケージ名 ライフサイクル HOT Deploy対応 自動登録 自動生成 インタフェースと実装の分離 備考
Bhv Bhv ROOT.dbflute.exbhv singleton × しない DBFluteと組み合わせた場合のみ
Constraint Constraint ROOT.constraint singleton × しない 対応するアノテーションはROOT.constraint.annotationパッケージに置かれる
Converter Converter ROOT.converter singleton しない
Dao Dao ROOT.dao singleton × しない
Dto Dto ROOT.dto prototype × しない
Dxo Dxo ROOT.dxo singleton × しない
Entity(S2Dao) (なし) ROOT.entity prototype × × × しない
Entity(DBFlute) (なし) ROOT.dbflute.exentity prototype × × しない DBFluteと組み合わせた場合のみ
Handler Handler ROOT.handler request × しない
Helper Helper ROOT.helper singleton × しない
Interceptor Interceptor ROOT.interceptor singleton × しない
Logic Logic ROOT.logic singleton × しない
Page Page ROOT.web request しない
Service Service ROOT.service singleton × しない

リクエストの処理の流れ

リクエストを受け取った場合、処理はPage (→Service) →Logic→ (Helper, ) Dao, Bhvのように呼び出されます。

トランザクション境界

デフォルトのトランザクション境界はPageクラスのGETまたはPOSTリクエストに対応するアクションメソッドです。具体的には名前が「_get」または「_post」で開始されるpublicメソッドです。

それ以外の処理、例えば制約チェック等のフレームワークによって自動的に実行される処理についてはトランザクションが開始されませんので、必要に応じて明示的にトランザクションを開始する必要があります。

ある種別のクラスについてトランザクション境界をまとめて設定するには、ymir-customizer.diconの設定をオーバライドして下さい。例えばLogicクラスが持つ全てのpublicメソッド(Objectクラスに属しているものを除く)についてトランザクションを開始したい場合は次のようなファイルをsrc/main/resources/ymir-customizer+logicCustomizer.diconとして追加して下さい:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
  "http://www.seasar.org/dtd/components24.dtd">
<components>
  <component name="logicCustomizer"
    class="org.seasar.framework.container.customizer.CustomizerChain">
    <initMethod name="addCustomizer">
      <arg>
        <component
          class="org.seasar.framework.container.customizer.AspectCustomizer">
          <property name="interceptorName">"j2ee.requiredTx"</property>
        </component>
      </arg>
    </initMethod>
  </component>
</components>

また、Pageクラスにトランザクション境界を設定したくない場合は、次のようなsrc/main/resources/ymir-customizer+pageCustomizer.diconを追加して下さい:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
  "http://www.seasar.org/dtd/components24.dtd">
<components>
  <component name="pageCustomizer"
    class="org.seasar.framework.container.customizer.CustomizerChain" />
</components>

ユニットテスト

ユニットテストでは極力モッククラスを作らずに、ユニットテスト用のデータをデータベースに登録して実際にデータベースアクセスすることで実際のロジックを通しで検証するやり方を推奨します。これはモッククラスを使ったテストは思いの他コストがかかるためです。モックを使ったテストを作成したとしても実際のロジックを通すテストはどの道作成する必要があるため、純粋にモックを使ったテスト分の工数を削減することができます。

DBFluteを利用することで、このようなユニットテストの作成と実施は容易になります。