Spring Batchの複数ジョブ管理 - レベル別設計パターン
前置き
※ この記事は、Spring Batchで複数のバッチジョブを管理するときの設計パターンをまとめたものです。
- 単一ジョブから始まって、ジョブ数が増えていくときの進め方を段階的に説明します。
- 各パターンのコード例は、Spring Batch 4.x/5.x の標準的な書き方を基準にしています。
- 「将来のために全部実装する」のではなく、「1段ずつ進化できる設計」を重視しています。
この記事でわかること
- Spring Batchのジョブ管理が、なぜ複数のやり方に分かれるのか理解できます。
- 自分たちのケース(ジョブ数、運用形態)に合った設計パターンが分かります。
- 今のシステムから次のレベルへ進化させるときの判断軸が分かります。
この記事の要点
- 単一ジョブから複数ジョブへの移行は、5段階のレベルで考えられます。
- どのレベルを選ぶかは、ジョブ数と運用形態で自動的に決まります。
- 小さく始めて、1段ずつ進化させる設計が、Spring Batchの強みです。
複数ジョブ管理の5段階レベル
Spring Batchで複数のバッチジョブを扱うとき、システムの複雑さによって段階的に進化します。
その流れを整理するのが、次の5段階のレベルです。
| レベル | 方式 | 主な特徴 | 向いているジョブ数 |
|---|---|---|---|
| 0 | 単一ジョブ・直接起動 | 常に1本のジョブだけ実行 | 1本 |
| 1 | 起動引数で切り替え | 実行時の引数で、どのジョブを実行するか指定 | 1〜3本 |
| 2 | JobSelector / Factory | ビジネス要件に応じた分岐パターン | 3〜5本 |
| 3 | JobRegistry | Spring Batchの標準的な動的取得機能 | 10本以上 |
| 4 | DispatcherJob | 複数ジョブを統合する「制御ジョブ」を用意 | 基幹系・大型バッチ |
| 5 | Jar分割 | ジョブごとに実行可能Jar を分ける | チーム横断・大規模運用 |
「どれが正解」というわけではなく、ジョブ数と運用形態で、必要なレベルが自然に決まるという流れです。
レベル0:単一ジョブ・直接起動
// ジョブを直接取得して実行
Job job = context.getBean("xxxJob", Job.class);
jobLauncher.run(job, params);
レベル0の特徴
- ✅ 最もシンプルな実装
- ✅ コード量が最小
- ✅ 動作が予測しやすい
レベル0の向いているケース
- バッチは1本だけ運用している
- 手動実行 or cron で定期実行
- ジョブの切り替えは考えていない
レベル0の状態確認
今はこのままで問題ありません。複数ジョブが必要になるまで、シンプルに保つのが賢い選択です。
レベル1:起動引数で切り替え
複数のジョブがあり、実行時にどちらを実行するかを指定したいときの、最も現実的な解です。
レベル1の実装例
// コマンドライン引数で job 名を指定
// java -jar batch.jar --job=LOGIN
// java -jar batch.jar --job=USER_SYNC
String jobName = argsMap.getOrDefault("job", "defaultJob");
Job job = context.getBean(jobName, Job.class);
jobLauncher.run(job, params);
レベル1の特徴
- ✅ 追加するコストがほぼゼロ
- ✅ ワンショット運用と相性がいい
- ✅ 起動スクリプトで簡単に切り替えできる
- ❌ ジョブ名を利用者が知る必要がある
レベル1の向いているケース
- ジョブが 1〜3本程度
- 起動時に「どのジョブを実行するか」で判定したい
- 将来、ジョブが増える可能性がある
レベル1の実務的なおすすめ度
⭐⭐⭐⭐⭐ 最初に選ぶべき現実解です。
このレベルで「引数で job を受け取れる」という土台を作っておくだけで、後で上のレベルへ進化させやすくなります。
レベル2:JobSelector / Factory パターン
業務区分によってジョブを切り替えたいとき、単なる文字列比較より「業務意図を明確に」したい場合です。
レベル2の実装例
public enum BatchType {
LOGIN,
USER_SYNC
}
@Component
public class JobSelector {
@Autowired
private ApplicationContext context;
public Job select(BatchType type) {
switch (type) {
case LOGIN:
return context.getBean("loginJob", Job.class);
case USER_SYNC:
return context.getBean("userSyncJob", Job.class);
default:
throw new IllegalArgumentException("Unknown job type: " + type);
}
}
}
レベル2の使用例
BatchType type = BatchType.valueOf(argsMap.get("type"));
Job job = jobSelector.select(type);
jobLauncher.run(job, params);
レベル2の特徴
- ✅ 業務意図が明確(文字列より列挙型)
- ✅ ジョブ名の内部実装が変わっても、外側の影響が少ない
- ✅ IDE が型チェックしてくれる
- ❌ クラスが1つ増える
レベル2の向いているケース
- ジョブが 3〜5本程度
- ビジネス上の「種類」として切り替えたい(「ログイン」「同期」など)
- チーム内で「ジョブの役割」を明確に したい
レベル2の実務的なおすすめ度
⭐⭐⭐⭐ 業務要件が絡んできたら、ここへステップアップすることが多いです。
レベル3:JobRegistry で一元管理
Spring Batch の標準的な仕組みである JobRegistry を使い、ジョブを名前で動的に取得します。
レベル3の実装例
@Autowired
private JobRegistry jobRegistry;
// ジョブ名から動的に Job を取得
String jobName = argsMap.get("jobName");
Job job = jobRegistry.getJob(jobName);
jobLauncher.run(job, params);
レベル3の特徴
- ✅ ジョブを一元的に管理できる
- ✅ ジョブの追加・削除に強い
- ✅ Spring Batch の標準機能なので、フレームワークの思想に合致している
- ❌ 少し抽象度が高い(最初は理解コストが必要)
レベル3の向いているケース
- ジョブが 10本を超えている
- 定期的に新しいジョブが追加される
- ジョブの一覧を動的に確認したい
注意点
JobRegistry を使う場合は、すべてのジョブが Spring Batch の @EnableBatchProcessing で自動登録されていることを確認してください。
レベル3の実務的なおすすめ度
⭐⭐⭐ ジョブ数が 10本を超えたら、ここへの移行を検討する時期です。
レベル4:DispatcherJob(制御ジョブ)
複数のジョブをまとめて管理し、「条件に応じて実行ジョブを切り替える」という複雑なフローが必要な場合です。
レベル4のイメージ
DispatcherJob(制御ジョブ)
↓
├─ LoginJob
├─ SyncJob
└─ CleanupJob
レベル4の実装の考え方
@Bean
public Job dispatcherJob(JobBuilderFactory jobBuilderFactory,
Step selectStep,
Step loginStep,
Step syncStep) {
return jobBuilderFactory.get("dispatcherJob")
.flow(selectStep) // 1. 実行対象を判定
.on("LOGIN").to(loginStep) // 2. LOGIN なら loginStep へ
.from(selectStep)
.on("SYNC").to(syncStep) // 3. SYNC なら syncStep へ
.end()
.build();
}
レベル4の特徴
- ✅ 業務フロー全体を1本のジョブで制御できる
- ✅ 状態管理がジョブ履歴に記録される
- ✅ 基幹系やバッチ基盤に向いている
- ❌ 設計と理解のコストが大きい
- ❌ ジョブの依存関係が複雑になりやすい
向いているケース
- 複数のバッチ処理が依存関係を持っている(「Aの後にB」など)
- 基幹系・夜間バッチで、全体の流れを管理したい
- ジョブの実行状況を一元的に監視したい
レベル4の実務的なおすすめ度
⭐⭐ バッチ基盤化が進み、「全体を1つの制御」で動かしたいときに選ぶレベルです。
レベル5:Jar を分ける(物理分離)
ジョブごとに異なる実行可能Jar を用意し、完全に独立させます。
レベル5の構成イメージ
login-batch.jar (ログイン処理)
sync-batch.jar (同期処理)
cleanup-batch.jar (クリーンアップ)
レベル5の特徴
- ✅ デプロイの責任範囲が明確
- ✅ 障害の影響を隔離できる(1つの Jar が落ちても他に影響しない)
- ✅ チーム間の所有権が明確
- ❌ 共通部品(DB接続、共通ライブラリ)の管理が複雑になる
- ❌ 複数Jar の依存関係管理が必要
レベル5の向いているケース
- 大規模システム
- チーム横断的な運用
- ジョブの責任範囲を明確に分けたい
レベル5の実務的なおすすめ度
⭐⭐ 大規模運用や組織的に分散したい場合に選ぶレベルです。
どのレベルを選ぶ? 実務的な判断軸
ジョブ数が1〜3本?
→ レベル1(起動引数で切り替え)
業務区分で切り替え必要?
→ レベル2(JobSelector)
ジョブが10本以上?
→ レベル3(JobRegistry)
複数ジョブの依存・統制必要?
→ レベル4(DispatcherJob)
複数チームで完全に分ける?
→ レベル5(Jar分割)
よくあるケース別ガイド
| 状況 | 現在のレベル | おすすめ |
|---|---|---|
| 単一ジョブ、今後増える予定 | レベル0 | レベル1へ移行しやすい設計を今から仕込む |
| ジョブ3本、業務で切り替え | レベル1 | レベル2(JobSelector)を検討 |
| ジョブ5本以上、定期追加される | レベル2 | レベル3(JobRegistry)への移行を検討 |
| バッチ基盤、全体統制必要 | レベル3 | レベル4(DispatcherJob)の導入を検討 |
| 複数チーム、責任分離必要 | レベル4 | レベル5(Jar分割)で完全に独立 |
例:今はレベル1(起動引数)だけど
// 引数で job 名を受け取れるように「設計だけ」しておく
String jobName = argsMap.getOrDefault("job", "defaultJob");
Job job = context.getBean(jobName, Job.class);
// 将来、レベル2へ進化させるときも、ここは変わらない
このように設計しておくと、将来の拡張が容易になります。
まとめ
- Spring Batchのジョブ管理は、5つのレベルで段階的に進化します。
- 最初に必要なのは「現在のレベル」だけです。将来のために過剰設計する必要はありません。
- 1段ずつ進化できる設計を心がけることが、Spring Batchの強みを活かすコツです。
次に深掘りするとしたら
- 「1つのJarに複数Jobはアリか?」 (実務的な判断軸)
- 「JobParametersのベストプラクティス」 (パラメータ設計)
- 「DispatcherJobの具体例」 (複雑なフロー管理)
Spring Batchは設計の自由度が高いので、「小さく始めてスケールされる場合の設計思想も用意されている」のが大きな利点です。