Java | 例外階層の設計テンプレート(アプリ全体で統一する例外方針)

Java Java
スポンサーリンク

例外階層の設計テンプレート(アプリ全体で統一する例外方針)」は、
チーム開発や長期運用の現場で必須の“例外ポリシー設計”です。

ここでは、

  • アプリ全体で共通化する例外階層図
  • 分類ルール(業務/システム/基盤)
  • 例外ハンドリング方針
    を、Spring Boot を前提に図+コード+表でまとめます。

1. 目的:例外を“伝える”仕組みを整理する

┌──────────────────────────┐
│  目的:どの層で例外を扱い、どう返すかを統一する   │
│──────────────────────────│
│  Controller :レスポンス変換(catch)          │
│  Service     :業務・システム例外をthrow       │
│  Repository  :DB系例外をwrapしてthrow        │
└──────────────────────────┘

2. 例外階層全体図(アプリ共通設計)

BaseAppException (抽象基底クラス)
├── BusinessException        // 業務ロジック例外(想定内)
│    ├── ValidationException // 入力・形式エラー
│    ├── RuleViolationException // 業務ルール違反
│    └── AuthorizationException // 権限不足
│
└── SystemException          // システム障害(想定外)
     ├── DatabaseException   // DB接続・SQL障害
     ├── ExternalApiException // 外部連携失敗
     └── ConfigException     // 設定欠落・環境異常

3. コード例:ベースクラスと派生クラス

BaseAppException(全ての共通基底)

public abstract class BaseAppException extends RuntimeException {
    private final String errorCode;

    protected BaseAppException(String message, String errorCode) {
        super(message);
        this.errorCode = errorCode;
    }

    public String getErrorCode() {
        return errorCode;
    }
}
Java

BusinessException(想定内の業務例外)

public class BusinessException extends BaseAppException {
    public BusinessException(String message, String code) {
        super(message, code);
    }
}
Java

SystemException(想定外・技術的例外)

public class SystemException extends BaseAppException {
    public SystemException(String message, String code, Throwable cause) {
        super(message, code);
        initCause(cause);
    }
}
Java

サブクラス例(業務・技術別)

public class ValidationException extends BusinessException {
    public ValidationException(String message) {
        super(message, "VALIDATION_ERROR");
    }
}

public class DatabaseException extends SystemException {
    public DatabaseException(String message, Throwable cause) {
        super(message, "DB_ERROR", cause);
    }
}
Java

4. ハンドリング方針(Spring Bootでの統一例)

Springの @ControllerAdvice を使い、
例外を一括でレスポンス変換します。

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    // --- 業務例外 ---
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<?> handleBusiness(BusinessException e) {
        log.warn("[業務例外] {} : {}", e.getErrorCode(), e.getMessage());
        return ResponseEntity
                .badRequest()
                .body(Map.of("error", e.getErrorCode(), "message", e.getMessage()));
    }

    // --- システム例外 ---
    @ExceptionHandler(SystemException.class)
    public ResponseEntity<?> handleSystem(SystemException e) {
        log.error("[システム例外] {} : {}", e.getErrorCode(), e.getMessage(), e);
        return ResponseEntity
                .internalServerError()
                .body(Map.of("error", e.getErrorCode(), "message", "内部エラーが発生しました"));
    }

    // --- 想定外のその他例外 ---
    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> handleUnknown(Exception e) {
        log.error("[想定外例外] {}", e.getMessage(), e);
        return ResponseEntity
                .internalServerError()
                .body(Map.of("error", "UNKNOWN", "message", "予期しないエラーが発生しました"));
    }
}
Java

5. 各層の責務と例外方針

役割投げる例外キャッチ備考
Controller入出力・レスポンス変換なし✅ 例外をキャッチエラーレスポンス返却
Service業務ロジック中心BusinessException / SystemException想定外はthrow
RepositoryDB・外部APIアクセスDatabaseException / ExternalApiExceptionSQLException等をwrap

6. 安全設計ポイント

原則内容
✅ 「想定内」=業務エラーBusinessException(400系)
✅ 「想定外」=技術障害SystemException(500系)
⚠️ 「全部Exceptionでまとめない」→ 障害分析が困難になる
🧱 例外は上層で変換(wrap)→ 下層の実装に依存しない
🧩 @ControllerAdviceで一括変換→ レスポンス仕様を統一

7. 実務パターン図(例外の流れ)

[Controller]
    ↓
  call
    ↓
[Service]
    ↓
  throw new ValidationException(...)
    ↓
@ControllerAdvice
    ↓
  return HTTP 400
[Repository]
    ↓
  SQLException発生
    ↓
catch → throw new DatabaseException(...)
    ↓
[Service] → throw上位へ
    ↓
@ControllerAdvice → return HTTP 500

8. 運用ベストプラクティスチェックリスト

チェック項目推奨
例外の共通基底クラスがある
errorCode で一意に識別可能
例外ハンドラが一括管理
「想定内」「想定外」を明確に区別
ログレベルが適切(warn / error)
呼び出し層で再throwせず、wrapで伝達

9. まとめ:統一設計の狙い

┌────────────────────────────┐
│ 例外階層の統一で得られる効果                        │
│────────────────────────────│
│ ✅ 障害原因の分類が明確                           │
│ ✅ ログ解析・監視が容易                           │
│ ✅ チーム内で設計基準が統一                        │
│ ✅ 新人が例外処理で迷わない                        │
└────────────────────────────┘

Java
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました