🟦 導入:Struts2アプリが“分かりにくくなる”原因はアクション層かもしれない
Struts2で開発されたWebアプリで、こんなことを感じたことはないでしょうか?
-
Actionクラスの行数がどんどん増えていく
-
似たような処理をするメソッドがActionに散在している
-
画面の戻り先や遷移先のロジックがわかりにくく、メンテナンスが大変
-
入力チェックや状態遷移のルールがActionに埋もれてしまっている
Struts2は柔軟な設計が可能なフレームワークですが、「Action層が担う役割と設計の方針」があいまいなまま実装を進めると、徐々に肥大化・複雑化していくという落とし穴があります。
そこで本記事では、Struts2におけるアクション層の「本来の役割」 を整理しつつ、よく使われる設計パターンとその適用シーンを紹介します。
✅ 1. はじめに:Actionは“ただのリクエスト受信機”ではない
Struts2のActionクラスは、リクエストごとにインスタンスが生成され、execute()
(もしくは指定されたメソッド)が呼び出されます。これはフレームワークとしては非常に分かりやすい構造です。
しかし、多くのプロジェクトで以下のような設計が見られます。
public class UserAction extends ActionSupport {
private String name;
private int age;
public String execute() {
if (name == null || name.isEmpty()) {
addActionError("名前を入力してください");
return "input";
}
// DAOを直接呼び出して登録処理
new UserDao().insert(name, age);
return "success";
}
}
このような実装では、入力チェック・業務ロジック・遷移制御がすべて1つのexecute()に詰め込まれており、以下の問題が発生しやすくなります。
-
単体テストしにくい
-
処理の責任範囲があいまい
-
別の画面やユースケースで再利用しづらい
Actionを「単なるリクエスト受信口」と捉えるのではなく、「ユーザー操作をアプリケーションのユースケースに翻訳し、正しい処理の流れを制御するレイヤー」 と再定義することで、見通しの良い設計が可能になります。
✅ 2. Action層の理想的な役割とは?
Struts2のAction層は、MVCの「C(Controller)」に該当します。ただし、Spring MVCなどと違って、Struts2のActionはインターフェースや継承を使って多くの責任を負うため、よりアプリケーションロジックに近い位置にあります。
💡 Actionの本来の役割とは?
以下の5つが、Struts2におけるActionの“理想的な”役割です。
項目 | 説明 |
---|---|
① UIイベントの受信 | 画面のボタン操作やフォーム送信など、ユーザーの意図を受け取る |
② パラメータの自動バインディング | フォームの入力値をプロパティにマッピングする(OGNL) |
③ 入力バリデーションの制御 |
validate() やXML/アノテーションによるバリデーション |
④ Service層の呼び出し | 業務処理やDB操作はServiceに委譲する |
⑤ 遷移の判断と制御 | 業務処理の結果に応じて、次に表示すべき画面を選択する |
✅ 他のレイヤーとの関係
-
DAO層との直接接続は避ける(Service層を通す)
-
ビジネスロジックはService層に任せる
-
Actionは「画面単位」「ユースケース単位」で設計するのが理想
[View (JSP)]
↓ submit
[Action] ---(呼び出し)---> [Service] ---(データ操作)---> [DAO]
↑ 遷移判断
↑ バリデーション
✍️ Point:
「画面の操作」と「業務処理」を明確に切り分ける。Actionはその“橋渡し役”です。
次回は、以下の3つのアクション設計スタイルを具体例付きで紹介していきます:
-
1クラス=1ユースケース型
-
1クラス多メソッド型
-
共通BaseAction抽出型(責務分離)
次記事はこちら → 📘Vol.11.2.2 に続く予定です!
✨ シリーズまとめ(Vol.11.2.xx アクション層設計)
-
✅ Vol.11.2 UX改善に効く!アクション層設計と入力制御の極意(プロローグ) ― アクション層からはじめるStruts2の“わかりやすい画面遷移”と“操作性向上” ―
-
✅ Vol.11.2.2 アクションクラスの3設計スタイルと選定基準 ― 機能粒度・責務分離・保守性のバランスをどう取るか? ―
🧭 関連記事(Vol.11.xxxシリーズ)
連番 | タイトル | 内容分類 |
---|---|---|
Vol.11.0 | Struts2が提供してくれる便利機能まとめ9選 | 導入&概要 |
Vol.11.1 | 自動注入とステートレスActionの仕組み | フレームワーク内部理解 |
Vol.11.2 | UX改善に効く!アクション層設計と入力制御の極意(プロローグ) | UX設計・アクション層総論 |
Vol.11.2.2 | 【UX重視】戻るボタン・キャンセル処理の設計手法と「戻り先管理」のスマート実装 | 戻り先制御とUX設計 |
Vol.11.2.3 | 【設計ノウハウ】多画面遷移時のパラメータ受け渡し・保持戦略(セッション vs hidden vs URL) | パラメータ多重管理 |
Vol.11.2.4 | 【実践ガイド】ユーザー操作を考慮したアクション遷移設計(リダイレクト vs フォワードの使い分け) | UX遷移設計/Result制御 |
Vol.11.2.5 | 【トラブル防止】アクション層の共通処理とメンテナブルなBaseAction設計 | アクション共通化/保守性 |
Vol.11.3 | Struts2の defaultStack とは?Interceptorの中身を詳しく追ってみる | Interceptor |
Vol.11.3.2 | 🔧 Vol.11.3.2 独自Interceptorの作り方と使い所 Struts2のカスタマイズを“実践で使えるレベル”に一歩進めよう | Interceptor |
Vol.11.4 | 入門者のための「パラメータ自動バインディング」完全理解ガイド | 自動バインディング |
Vol.Vol.11.4.1 | 入門者のための自動バインディング実践パターン集 | 自動バインディング |
Vol.11.5 | Struts2の構成理解が“実務レベル”へと進化する決定版 | 設定ファイル構造 |
Vol.11.5.1 | Struts2の“画面遷移の全体像”を理解するためのマイルストーン記事 | 設定ファイル構造 |
Vol.11.6 | ActionSupport の便利メソッドと国際化対応 | ユーティリティ / i18n |
Vol.11.7 | validate() / input 戦略とUX設計 | 開発スタイルの違い |
Vol.11.8 | アノテーションベースの設定 vs XML定義の比較 | 開発スタイルの違い |
Vol.11.9 | バリデーション完全解説(XML / アノテーション) | バリデーション(※Vol.8補完) |
Vol.11.10 | Struts2の「OGNL」ってなに?しくみと使い方をやさしく解説 | 式言語OGNL解説 |
Vol.11.11 | ModelDriven インターフェースの使いどころと注意点 | Model駆動開発 |
Vol.11.12 | SessionAware と RequestAware でセッション/リクエスト管理 | セッション管理 |
Vol.11.13 | Resultタイプ完全解説(dispatcher / redirect / stream など) | 遷移パターン理解 |
Vol.11.14 | Vol.11.14 ValueStackの中身を理解する – 画面とActionの橋渡し役 | Action |
Vol.11.15 | Interceptorチェーンを自作してみよう(ログ / 認証フィルタ) | Interceptor |
Vol.11.16 | エラー処理と例外ハンドリングの実践パターン | ハンドリング設計 |
Vol.11.17 | Struts2でファイルアップロード機能を実装する方法 | 実装系Tips |
Vol.11.18 | 非同期通信(AJAX)とStruts2の連携方法 | フロント連携編 |
Vol.11.19 | セキュリティ対策(CSRF, XSS, パラメータ偽装) | セキュリティ対策 |
Vol.11.20 | 複数フォームを安全に扱う!セッション管理と動的フォームの考え方 | セッション管理 |
Vol.11.21 | Struts2でJSONを返す!REST API連携と非同期通信の基本設計 | セッション管理 |
Vol.11.22 | 本番環境構成とセキュリティ設計(Apache+Tomcat+Struts2連携) | 外部公開 |