Spring Batchを上手に使う設計

Spring Batchとは何か

業務系のアプリケーションでは、大量のデータを順次読み込んで、そのデータを処理して結果を出力する、いわゆる「バッチ処理」は欠かすことができません。バッチ(Batch)とは、英語の原義では「束」を意味しており、あくまで大量のデータを束にして処理することです。バッチ処理であることに、起動方法は関係ありません。

Spring Batchは、Spring Frameworkを適用して作成された、「バッチ処理」を効率的に開発できるようにするためのフレームワークです。Spring Batchは、もともとアクセンチュアが開発して2008年にオープンソース化したものから発展し、Java 7から導入されたJava Batchの元になっているフレームワークです。エンタープライズシステムへの適用を目的として生まれ、Javaの標準に組み込まれたので今後もっと使われていくようになるフレームワークであると考えられます。

Spring Frameworkもフレームワークですが、こちらの本質(というか本体)はソフトウェア部品であるJava Beanを結合できるようにする枠組み(依存性の注入:DIコンテナ)であって、特定のアプリケーション機能を実現するものではありません。Spring Frameworkのこの機能を用いて、さまざまなソフトウェア部品が部品として活用できるため、Spring Frameworkには、さまざまなソフトウェア部品が初めから含まれています。意識しないとSpring Frameworkと、Spring Frameworkで使うように作られた部品の区別が無くなって、よくわからないことになってしまうので注意が必要です。

Spring Batchの実体は、Spring Frameworkを適用して結合することを前提とした、バッチ処理用のソフトウェア部品集です。

Spring Batchを構成する部品は、おおざっぱに

  1. Spring batch自体を構成するクラス
  2. バッチ処理において汎用的に使える機能を持ったクラス
  3. ユーザが実現したい処理を実装する際のベースクラス

から構成されます。1.は開発者はあまり意識しません。2.はいわゆる共通部品なので古来からある概念で、Spring Batchにはバッチ処理でよく使われる機能がたくさんそろっています。3.こそが、Spring Batchを「フレームワーク」たらしめている重要な存在になります。

Spring Batchでは、バッチプログラムは以下の処理から構成されると定義しています

  1. データを1件入力
  2. 読み取ったデータを加工する
  3. 加工した結果のデータをためておいて、一気に出力

という構造です。Spring Batchでは、この構造に沿ったプログラムが作れるよう、データを読み取るItemReader, データを処理するItemProcessor、データを書き込むItemWriterをつくるためのベースクラスが提供されています。

バッチ処理を、Spring Batchが定義する処理に分解して、分解された入力、加工、出力の各処理をSpring Batchが提供するベースクラスを拡張する方法で作成することにより、いかなる処理を行うバッチプログラムも、同一の構造で構築されることになります。

Spring Batchをシステム開発に適用する最大のメリットは、たくさん作るバッチプログラムが均一の構造になることが保障されることです。バッチ処理用の充実した共通部品が使えることもメリットですが、それ以上に、構造が均一化されることによる品質の均一化や設計の単純化のメリットの方が大きいように思われます。

Spring Batchを上手に使う設計

Spring Batchのメリットは、バッチプログラムが均一の構造になることです。Spring Batchを適用して作成するバッチ処理の設計は、Spring Batchの構造に合わせた設計を行うことで、設計~実装の最大効率を得ることができるようになります。

Spring Batchが定義するバッチ処理の構造は、①データを1件入力し、②読み取ったデータを加工し、③加工した結果のデータをためておいて、一気に出力するというものです。つまり、バッチ処理の設計は、処理をこの3段階に分割するところから始まります。具体的には、処理の入力元と出力先を定義することが最初の設計になります。このとき、入力元と参照データを明確に区別する必要があります。

バッチ処理では、単一の入力で処理が実現できる場合は少なく、複数の情報源からの情報を結合・加工する処理の方が多いと考えられます。この情報源を全て入力と考えてしまうと、ItemReaderで読んだデータを処理するというSpring Batchの前提の構造に合致しなくなり、Spring Batchを使用するメリットが損なわれてしまいます。Spring Batchでは、あくまでItemReaderで読んだデータを処理することを前提に、プログラム構造を規定し、その既定の元に設計を行うべきです。

そもそも、一般に設計という行為は、多数設計されるものの構造がある程度限定された中での個々の製品の固有の情報を定義するものです。構造が強く限定されれば、個別の設計は最小化することができますが、構造が限定されない場合には、個々に設計する範囲が広がり、設計工数が増加します。Spring Batchなどのフレームワークのメリットの一つは、多数作るバッチの構造を均一にして、それを前提とした設計をすることで、個々のバッチ処理毎の設計事項を最小化し、個別設計自体を単純にできることです。構造の均一化の最初の前提として、Spring Batchがバッチ処理をRead → Process → Writeと分割すること、Readするのは1つであることを定義しているといえます。

Spring Batchが規定するバッチの構造に立脚すると、バッチ処理の設計での最初の重要ポイントは、何をReaderで読むかということになります。たとえば、ファイルを読み込んでデータベースに書くようするような、入力情報と参照情報の区別が容易につく場合は特に問題はないと思います。読んだファイルにデータベースのデータを組み合わせて格納するような場合は、ファイルにある項目をキーにして、データベースを検索する処理がItemProcessorに記述されるようにすればよいのです。難しいのは、以下の場合です。

  1. 複数に分かれた入力を組み合わせて1件の出力データを作る
  2. 統計分析など、複数件の入力データを集約して、1件の出力データを作る
  3. 1件の入力を元に、複数の出力データを作る

1.の場合、データをつなぐキーとなる項目を持つ情報源を順番に処理するのが適当なので、キー情報を持つファイルやテーブルがItemReaderで読む入力になり、その他の入力は参照情報とします。また、入力情報源が全てデータベースであれば、viewを用いて入力を一本化することも選択肢になります。複数ファイルを統合する場合は、処理を2段階に分割して、ファイルのデータベースへの読み込みと、読んだデータの処理に分割するのが良いです。

2.の場合、入力元の考え方は1.と同様でよいのですが、読み込み1件毎に処理を行うというSpring Batchの基本構造とは一見相いれないように思われますが、対応方法はあります。一つ目の方法は、処理の途中状態を保持するテンポラリテーブルを用いて、入力1件毎にテンポラリテーブルを更新する方法です。次のステップでテンポラリテーブルに出来上がった結果を、本来の出力先にコピーして、テンポラリテーブルを削除する処理を行うようにします。もう一つの方法は、ExecutionContextに途中状態を保持して、集計処理を行うSTEPと結果の書き込みを行うSTEPを分割することです。他にも方法はあると思います。

3.の場合は、ItemProcessorのリターン値でItemWriterに渡すデータ型を工夫し、ItemWriterが書き込み先毎に適切な処理を行うようにすればよいです。MapやListなどのJavaのコレクションを使用することで、データの集合を1つのリターン値に持たせることができます。Mapを使用すれば、異なるデータ型の処理結果を一つにまとめてItemWriterに渡すことができます。ItemWriterは、渡されたデータを適切に出力することが求められるので、渡されるデータの構造と出力先の定義がItemWriterの設計事項になります。

フレームワークを使いこなすということ

Spring Batchを上手に使う設計の本質は、Spring Batchが前提とするバッチ処理の構造に合わせて処理を設計するということに尽きます。バッチ処理の設計として考えると、Spring Batchの構造に合わせた設計とフレームワーク無しのゼロベースでの処理設計とでは処理手順の考え方が大きく異なるはずです。Spring Batch以前のバッチ処理は、COBOLやJavaなどのプログラミング言語や各種スクリプト言語、各DBMS製品のストアドプロシジャで自由に処理ロジックを構成して作成していたと思います。処理の手順は、フローチャートを代表とした、文字通りの処理の手順として設計されてきたことでしょう。
ここで提唱するSpring Batchを用いた設計とは、処理の手順の大枠は既に決まっていることが前提で、それに合わせて実装すべき処理を適切に分割することになっています。従来の、手順を組み立てる設計から、処理を分割する設計への転換が求められます。

この、手順組立型設計から処理分割型設計への転換は、Spring Batchに固有なものではなく、いわゆるフレームワークという種類のソフトウェア製品を活用する際には普遍的な考え方だと思います。
Spring Batchに限らず、Spring Framework自体も、Spring MVCも、Struts2も、それぞれのフレームワークが想定するソフトウェアの構造に立脚して、それぞれのフレームワークが規定する処理要素に該当する制作物を定義していく行為こそが設計作業の本質になります。
従来の設計に実装だけフレームワークを適用すると、プログラマはフレームワークの構造を無視して、フレームワークが許す部位に従来型のコードを全てまとめて記述するような構造になり、フレームワークのメリットが生かせません。

フレームワークを使いこなすには、プログラマが実装レベルでフレームワークの使い方をしるだけではなく、設計者がフレームワークの構造とできることを熟知し、フレームワークを生かした設計を行うことが肝要です。そのためには、設計者もしくは設計標準の作成者がフレームワークの特性を学び、その特性を生かした実装ルールを規定し、その実装ルールを前提とした設計文書の体系・書式を整備することが重要ということになるのではないかと思います。

参考情報

Accenture and SpringSource Team to Deliver Production Version of Open Source Framework for Batch Processing
Java EE 7,Spring Batchを標準化
ウィキペディア Spring Framework
Spring Batch - soracane
SpringBatchとは - omotenashi-mind
Spring Batch開発入門 | Opentone Labs.
概説 Springプロダクト(12) - Spring Batchで簡単にバッチを作る (1) Spring Batchとは | マイナビニュース

(2014/12)