こんにちは!Spring BatchでTasklet設計に悩んでる方へ
今回は「TaskletとChunkの使い分けどうしてる?」「責務の分け方ってどう考える?」って悩んでた頃の自分に向けて、実際の設計&改善ポイントを紹介します!
私も最初は、処理をどこにどれだけ詰めればいいのか、ExecutionContextっていつ使えばいいのか、とにかく混乱してました。
特に、ドメインロジックとバッチ制御ロジックの境目を考えずに実装すると、すぐに「処理が全部Taskletに詰まってる地獄コード」が出来上がります。
ちょっと重めの実装話になりますが、「Taskletって正直どう設計したらいいの?」って悩んでる方に、試行錯誤の末にたどり着いた“伝わるTasklet設計”をシェアします!
Taskletとは?Spring Batchにおける基本と使いどころ
Taskletは「Stepでやることを1回で完了させる処理」に向いています(※繰り返し処理を行いたい場合は RepeatStatus.CONTINUABLE
による制御も可能ですが、一般的には単発処理で使われることが多いです)。
一方、Chunkは「データの繰り返し処理」に使います。
種類 | 向いてる処理 |
---|---|
Tasklet | 単発処理(条件チェック、初期化など) |
Chunk | データの繰り返し処理(CSV/DB連携) |
ポイントは、Taskletは手続き的に書けるぶん、設計を意識しないとすぐに“肥大化”するところ。
だからこそ、DDDでいう「ユースケース層」と「ドメイン層」をしっかり分けておくのが重要です。
✅ Spring Batch実装例:Taskletの業務処理設計を解説
以下は、実際のSpring Batchプロジェクトで使われている JobPreparationTasklet
の例です。
チェック対象のジョブIDが渡されているか確認し、該当データがDBに存在するかをチェック。その後、後続ステップのために ExecutionContext
にIDを保持する処理です。
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
log.info("チェックジョブ開始ステップを実行");
// 入力パラメータの取得
Integer jobRecordId = JobParamUtils.getRequiredCheckJobId(chunkContext);
// ドメインサービスによる存在チェック(存在しない場合は例外)
jobValidationService.findByIdOrThrow(jobRecordId);
// JobExecutionContextに登録(後続ステップ用。Stepスコープではなくジョブスコープで共有)
val context = chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext();
BatchContextUtils.putStringToContext(context, "job.jobRecordId", jobRecordId.toString());
log.info("jobRecordId={} を ExecutionContext に設定しました", jobRecordId);
return RepeatStatus.FINISHED;
}
👇 DDD的な視点で見ると…
レイヤー | 担当する処理 |
---|---|
Tasklet(ユースケース層) | 処理の流れ・制御(パラメータ取得/ログ出力など) |
Service(ドメイン層) | ビジネスルールの実装(IDチェック、例外スロー) |
Util(インフラ/共通層) | ExecutionContextの操作、ログ整形など |
設計で気をつけたいポイント(実務から学んだ3つ)
1. ロジックは必ずServiceに寄せる
NGな例:
if (!db.exists(jobRecordId)) {
throw new RuntimeException("Not found");
}
OKな例:
jobValidationService.findByIdOrThrow(jobRecordId); // ドメイン責務へ委譲
2. ExecutionContextのユーティリティ化
JobParamUtils.getRequiredCheckJobId(chunkContext);
BatchContextUtils.putStringToContext(context, "job.jobRecordId", jobRecordId.toString());
→ 共通化することで後続Stepでもわかりやすく保守しやすい!
3. 例外は握りつぶさず“設計として”投げる
throw new IllegalStateException("チェック対象のジョブが存在しません: " + jobRecordId);
Taskletの実行フロー(Mermaid図)
責務分離の構成図(Mermaid)
Taskletのメリット・限界(DDDで整理)
観点 | 内容 |
---|---|
👍 メリット | - 処理の意図が明確(Step単位で責務が見える)- ドメインロジックと分離しやすい |
⚠ 限界 | - ロジックを詰め込むと崩壊- 状態共有が雑だと破綻しやすい- Flowや分岐にはChunkが適する |
まとめ|Spring BatchにおけるTasklet設計の最適解
- Taskletは「1回で終わる処理」を綺麗に書くための道具。
- DDDの視点でユースケース/ドメイン/共通ユーティリティを分けるとスッキリ!
- 状態管理・例外処理も明示的にやるべし!
🔗 関連記事(おすすめ)
- ▶️ [ExecutionContextの正しい使い方(予定)]
- ▶️ [バッチアーキテクチャにDDDをどう取り入れるか(予定)]
🗨 コメント・シェア歓迎!
もしこの記事が「役に立った!」と思ったら、ぜひ LGTM
や ストック
、コメント
をいただけると励みになります!
- チームでの設計共有に使いたい方はブックマークをどうぞ。
- 「うちはこうやってるよ!」という経験談も大歓迎です。
📚 シリーズ記事
この記事は以下の内容とも連携しています:
設定や構成の“見える化”は、報告や状態共有にもつながる重要な設計要素。Taskletの責務設計と合わせて読むと理解が深まります!
✉ 最後に
Spring BatchのTaskletって、「軽い処理だけど、設計は重く考えるべき」ってバランスが難しいんですよね。
でも、ドメインを意識して責務を分けるだけで、ほんとに“読みやすく・テストしやすく・壊れにくく”なります。
この記事が、今Taskletで迷ってる誰かのヒントになったら嬉しいです!
LGTM・コメント・チームへの共有も大歓迎です🙌
それじゃまた、次の記事でお会いしましょう〜!