# Spring BatchのTasklet設計ガイド｜実務で使えるDDD設計と注意点まとめ

## こんにちは！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を保持する処理です。

```java
@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な例：

```java
if (!db.exists(jobRecordId)) {
  throw new RuntimeException("Not found");
}
```

OKな例：

```java
jobValidationService.findByIdOrThrow(jobRecordId); // ドメイン責務へ委譲
```

### 2. ExecutionContextのユーティリティ化

```java
JobParamUtils.getRequiredCheckJobId(chunkContext);
BatchContextUtils.putStringToContext(context, "job.jobRecordId", jobRecordId.toString());
```

→ 共通化することで後続Stepでもわかりやすく保守しやすい！

### 3. 例外は握りつぶさず“設計として”投げる

```java
throw new IllegalStateException("チェック対象のジョブが存在しません: " + jobRecordId);
```

---

## Taskletの実行フロー（Mermaid図）

```mermaid
sequenceDiagram
    actor SpringBatch
    participant Tasklet
    participant Service
    participant ContextUtil

    SpringBatch->>Tasklet: execute()
    Tasklet->>Service: findByIdOrThrow(jobRecordId)
    Service-->>Tasklet: return entity or throw
    Tasklet->>ContextUtil: putStringToContext()
    ContextUtil-->>Tasklet: void
    Tasklet-->>SpringBatch: RepeatStatus.FINISHED
```

---

## 責務分離の構成図（Mermaid）

```mermaid
graph TD
    A[JobPreparationTasklet<br/>（ユースケース層）]
    B[JobValidationService<br/>（ドメイン層）]
    C[JobRecordRepository]
    D[JobParamUtils<br/>（共通ユーティリティ）]
    E[BatchContextUtils<br/>（共通ユーティリティ）]

    A --> B
    B --> C
    A --> D
    A --> E
```

---

## Taskletのメリット・限界（DDDで整理）

| 観点      | 内容                                                 |
| ------- | -------------------------------------------------- |
| 👍 メリット | - 処理の意図が明確（Step単位で責務が見える）- ドメインロジックと分離しやすい         |
| ⚠ 限界    | - ロジックを詰め込むと崩壊- 状態共有が雑だと破綻しやすい- Flowや分岐にはChunkが適する |

---

## まとめ｜Spring BatchにおけるTasklet設計の最適解

- Taskletは「1回で終わる処理」を綺麗に書くための道具。
- DDDの視点でユースケース／ドメイン／共通ユーティリティを分けるとスッキリ！
- 状態管理・例外処理も明示的にやるべし！

---

## 🔗 関連記事（おすすめ）

- ▶️ [ExecutionContextの正しい使い方（予定）]
- ▶️ [バッチアーキテクチャにDDDをどう取り入れるか（予定）]

---

## 🗨 コメント・シェア歓迎！

もしこの記事が「役に立った！」と思ったら、ぜひ `LGTM` や `ストック`、`コメント` をいただけると励みになります！

- チームでの設計共有に使いたい方はブックマークをどうぞ。
- 「うちはこうやってるよ！」という経験談も大歓迎です。

---

## 📚 シリーズ記事

この記事は以下の内容とも連携しています：

- ▶️ [🌟Spring Boot設定のベストプラクティス｜画面名やパスを外部ファイルで管理する理由と方法💡](https://qiita.com/doruboxshiki/items/c5cc5e6be2720fb7ed33)

設定や構成の“見える化”は、報告や状態共有にもつながる重要な設計要素。Taskletの責務設計と合わせて読むと理解が深まります！

---

## ✉ 最後に

Spring BatchのTaskletって、「軽い処理だけど、設計は重く考えるべき」ってバランスが難しいんですよね。

でも、ドメインを意識して責務を分けるだけで、ほんとに“読みやすく・テストしやすく・壊れにくく”なります。

この記事が、今Taskletで迷ってる誰かのヒントになったら嬉しいです！\
LGTM・コメント・チームへの共有も大歓迎です🙌

それじゃまた、次の記事でお会いしましょう〜！