0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

📘 Vol.10.6:Service層の設計方針と例外処理戦略 〜複数DAO呼び出し・トランザクション・例外管理の責務を明確に〜

Last updated at Posted at 2025-05-25

✅ この回で学べること

  • Service層の役割と設計のポイント
  • DAOとServiceの責務の分離
  • トランザクション管理の実装例
  • 例外処理の設計とベストプラクティス

🧩 Service層の役割とは?

Service層は、「ビジネスロジックを統合」するための層です。
主な責務は以下の通り:

役割 内容
複数DAOの統合 1つの業務処理に複数テーブルの更新が必要なときにまとめて呼び出す
トランザクション制御 成功時のコミット/失敗時のロールバックなど
例外管理 DAO層のSQLExceptionなどをまとめて扱い、アプリ層に適した例外へ変換

✅ シンプルな構成図

Controller(Action)
    ↓ 呼び出し
Service(業務ロジック・トランザクション制御)
    ↓ 呼び出し
DAO(DBアクセス)

📌 実装例:Service層でのトランザクション管理

UserService.java(複数DAO+トランザクション制御)

public class UserService {
    private UserDao userDao;
    private UserRoleDao userRoleDao;
    private DataSource dataSource;

    public UserService(UserDao userDao, UserRoleDao userRoleDao, DataSource dataSource) {
        this.userDao = userDao;
        this.userRoleDao = userRoleDao;
        this.dataSource = dataSource;
    }

    public void registerUser(User user, String role) throws ServiceException {
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            conn.setAutoCommit(false); // トランザクション開始

            userDao.insert(conn, user);
            userRoleDao.assignRole(conn, user.getUserId(), role);

            conn.commit(); // 成功時コミット
        } catch (SQLException e) {
            if (conn != null) {
                try {
                    conn.rollback(); // エラー時ロールバック
                } catch (SQLException ex) {
                    // ロールバック失敗ログ
                }
            }
            throw new ServiceException("ユーザー登録失敗", e);
        } finally {
            if (conn != null) {
                try {
                    conn.setAutoCommit(true); // 元に戻す
                    conn.close();
                } catch (SQLException ex) {
                    // ログ記録など
                }
            }
        }
    }
}


UserDaoImpl.java(Connectionは引数で受け取る)

public class UserDaoImpl implements UserDao {
    public void insert(Connection conn, User user) throws SQLException {
        String sql = "INSERT INTO users (user_id, username, email) VALUES (?, ?, ?)";
        try (PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setInt(1, user.getUserId());
            stmt.setString(2, user.getUsername());
            stmt.setString(3, user.getEmail());
            stmt.executeUpdate();
        }
    }
}

✅ Service層を設けるメリット

  • 業務ロジックを集約できる(Actionに書かない)

  • トランザクション開始・終了の制御を一元化できる

  • エラーハンドリング戦略を統一できる(例:ServiceExceptionにラップ)


☑ よくあるパターン

パターン 使用例
単一DAO処理 Service層を設けず、Action → DAO直呼びでもOK
複数DAO使用 Service層でまとめて処理・トランザクション制御が必要
ロジック分岐あり DAOの呼び出し順序や分岐ロジックがある場合はService層で制御

💡 例外処理設計のTips

  • DAO層ではSQLExceptionなど「技術例外」をスロー

  • Service層では独自例外(例:ServiceException)でラップ

  • Action層ではServiceExceptionをキャッチして画面遷移制御やログ出力


✅ 今回のまとめ

項目 ポイント
Service層の目的 DAOの統合呼び出し・トランザクション・例外管理
トランザクション制御 Service層でConnection開始→DAOに渡す方式が王道
例外管理 DAOでSQLException → Service層で独自例外にラップ
設計方針 単純な処理ならDAOだけ。複雑な処理はService層を設ける

📘 次回予告:「Vol.10.7:Service層×JUnitテストの基本とパターン」を予定しています!


✨ シリーズまとめ(Vol.10.x バリデーション編)


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?