はじめに
前回の記事でMVCアーキテクチャについて学んだ皆さんは、Java Web開発における「モデル1」と「モデル2」という用語を耳にしたことがあるでしょう。この記事では、この2つのアプローチを詳細に比較し、それぞれの特徴、利点、欠点、そして適切な使用場面について解説します。
モデル1とモデル2の基本概念
モデル1アーキテクチャ

モデル1は、JSPを中心としたシンプルなアーキテクチャです。主な特徴は:
- JSPがリクエスト処理と表示の両方を担当
- ビジネスロジックがJSP内に直接記述されるか、JavaBeansを利用
- 小規模なアプリケーションに適している
- 開発が簡単で習得しやすい
モデル2アーキテクチャ

モデル2は、MVCパターンに基づいたより構造化されたアーキテクチャです。主な特徴は:
- ServletがControllerとして機能
- JSPはView専用として使用
- 明確な役割分担(MVCの分離)
- 中~大規模アプリケーションに適している
- 保守性と拡張性が高い
コード比較:ユーザー登録機能
モデル1での実装例 (register.jsp)
<%@ page import="java.sql.*" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
// ビジネスロジックがJSP内に直接記述(アンチパターン)
if ("POST".equals(request.getMethod())) {
String name = request.getParameter("name");
String email = request.getParameter("email");
String password = request.getParameter("password");
Connection conn = null;
PreparedStatement pstmt = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb", "user", "password");
String sql = "INSERT INTO users (name, email, password) VALUES (?, ?, ?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
pstmt.setString(2, email);
pstmt.setString(3, password);
int result = pstmt.executeUpdate();
if (result > 0) {
out.println("登録成功しました
");
} else {
out.println("登録に失敗しました
");
}
} catch (Exception e) {
e.printStackTrace();
out.println("エラーが発生しました: " + e.getMessage() + "
");
} finally {
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
}
}
%>
<!DOCTYPE html>
<html>
<head>
<title>ユーザー登録</title>
</head>
<body>
<h1>ユーザー登録</h1>
<form method="post">
名前: <input type="text" name="name"><br>
メール: <input type="email" name="email"><br>
パスワード: <input type="password" name="password"><br>
<button type="submit">登録</button>
</form>
</body>
</html>
モデル2での実装例
1. Controller (RegisterServlet.java)
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
private UserService userService = new UserService();
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.getRequestDispatcher("/register.jsp").forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
String email = request.getParameter("email");
String password = request.getParameter("password");
try {
boolean success = userService.registerUser(name, email, password);
if (success) {
request.setAttribute("message", "登録成功しました");
} else {
request.setAttribute("error", "登録に失敗しました");
}
} catch (Exception e) {
request.setAttribute("error", "エラーが発生しました: " + e.getMessage());
}
request.getRequestDispatcher("/register.jsp").forward(request, response);
}
}
2. Model (UserService.java)
public class UserService {
private UserDAO userDao = new UserDAO();
public boolean registerUser(String name, String email, String password)
throws SQLException {
// バリデーションなどのビジネスロジック
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("名前は必須です");
}
// パスワードの複雑性チェックなど...
return userDao.insertUser(new User(name, email, password));
}
}
3. View (register.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>ユーザー登録</title>
</head>
<body>
<h1>ユーザー登録</h1>
<%-- メッセージ表示 --%>
<c:if test="${not empty message}">
<p style="color:green">${message}</p>
</c:if>
<c:if test="${not empty error}">
<p style="color:red">${error}</p>
</c:if>
<form method="post">
名前: <input type="text" name="name"><br>
メール: <input type="email" name="email"><br>
パスワード: <input type="password" name="password"><br>
<button type="submit">登録</button>
</form>
</body>
</html>
比較表:モデル1 vs モデル2
| 項目 | モデル1 | モデル2 |
|---|---|---|
| 構造 | JSP中心 | MVCパターン |
| 役割分担 | 不明確(JSPがすべて処理) | 明確(Model-View-Controller) |
| 開発速度 | 速い(初期段階) | やや遅い(設計が必要) |
| 保守性 | 低い(コードが散らばる) | 高い(責務が分離) |
| 拡張性 | 低い(大規模化に不向き) | 高い(大規模対応可) |
| テスト容易性 | 困難(ロジックがJSP内) | 容易(各層を個別にテスト可能) |
| 適応規模 | 小規模・プロトタイプ | 中~大規模・本番システム |
| チーム開発 | 不向き(競合が発生) | 適している(役割分担可能) |
| 技術的負債 | 蓄積しやすい | 蓄積しにくい |
移行のベストプラクティス:モデル1からモデル2へ
既存のモデル1アプリケーションをモデル2にリファクタリングする場合の手順:
- ビジネスロジックの抽出
- JSP内のJavaコードを特定
- 該当コードをJavaBeansやサービスクラスに移動
- Controllerの導入
- リクエスト処理用のServletを作成
- JSPは表示専用に変更
- データアクセス層の分離
- DAOパターンを導入
- データベース操作を専用クラスに移動
- フロントコントローラーの導入(オプション)
- 全リクエストを処理する中央Servletを配置
- リクエストルーティングを一元管理
リファクタリング例
Before (モデル1):
<%-- JSP内に直接データベース操作 --%>
<%
String id = request.getParameter("id");
Connection conn = ...;
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM products WHERE id=" + id);
// ...
%>
After (モデル2):
// Controller
@WebServlet("/product")
public class ProductServlet extends HttpServlet {
private ProductService productService = new ProductService();
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String id = request.getParameter("id");
Product product = productService.getProductById(id);
request.setAttribute("product", product);
request.getRequestDispatcher("/productDetail.jsp").forward(request, response);
}
}
<%-- JSPは表示のみ --%>
<p>商品名: ${product.name}</p>
<p>価格: ${product.price}円</p>
現代的な視点:モデル1の位置付け
モデル2が主流となった現在でも、モデル1には以下のような用途があります:
- プロトタイピング
- コンセプト検証のための迅速な開発
- 小規模な管理画面
- 社内用ツールなど限定的な利用
- 学習用途
- JSPの基本を理解するためのステップ
しかし、以下の点に注意が必要です:
- セキュリティリスク(SQLインジェクションなど)
- 長期メンテナンスの困難さ
- 技術的負債の蓄積
結論:どちらを選ぶべきか
プロジェクトの要件に基づいて適切なアーキテクチャを選択してください。
モデル1が適している場合:
- 1~2週間で完了するような短期プロジェクト
- 1人で開発する個人プロジェクト
- 学習目的やデモ用アプリケーション
モデル2が必須な場合:
- 企業向けの本番システム
- 長期にわたってメンテナンスされるプロジェクト
- 複数人で開発するチームプロジェクト
- 将来的な機能拡張が予想される場合
Java Web開発のベストプラクティスとしては、モデル2(MVC)アーキテクチャを採用することが強く推奨されます。特に、Spring Frameworkなどの現代的なフレームワークはすべてMVCパターンに基づいて設計されているため、モデル2を理解することはプロフェッショナルなJava開発者になるための重要なステップです。
次のステップ
モデル2の理解を深めたら、以下のトピックに進むことをお勧めします:
- Front Controllerパターン – 複数のServletを統合的に管理
- Spring MVCフレームワーク – より高度なMVC実装
- DAOとRepositoryパターン – データアクセス層の設計
- 依存性注入(DI) – コンポーネント間の結合度低減
MVCアーキテクチャの完全な理解は、現代のJava Web開発における成功への鍵です。小さなプロジェクトから始めて、徐々に複雑なアプリケーションへとスキルを拡張していきましょう。