この文書では、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を利用することで、このようなユニットテストの作成と実施は容易になります。