投稿日
更新日
Nablarchシステム開発ガイド
もくじ
- はじめに
- このドキュメントのねらい
- このドキュメントの対象読者
- 読み方の注意点
- 全体像
- プロジェクト計画
- 要件定義工程
- 要件定義
- 方式設計
- プロジェクト成果物の確認
- テスト計画
- プロジェクト用の開発ガイド作成
- 設計工程
- PGUT工程
- Nablarchプロジェクト初期構築
- チーム開発環境構築
- 開発環境構築ガイドの作成
- 開発標準の整備
- 結合テスト工程
- 結合テスト準備(疎通確認)
- システムテスト工程
- 保守運用
- Nablarchパターン集
- Nablarchでよく使われるパターン
- Nablarchアンチパターン
- Nablarchバッチ処理パターン
- 起動方法による分類
- 入出力による分類
- 注意点
- ファイルの移動、コピー
- Nablarchでの非同期処理
- メール送信を行う場合
- Nablarchアンチパターン
- Webアプリケーション
- Nablarchバッチ
- JSR352バッチ
- UI標準のカスタマイズ
- UI標準(画面)
- UI標準(画面)別冊_UI部品カタログ
- テスト項目の検討
- テスト観点の収集
- テスト観点の抽出
- Nablarch環境構築
- アーキタイプからのプロジェクト生成
- コンポーネント設定
- 静的解析ツールの導入
- Javaスタイルガイド
- チーム開発環境構築
- 開発環境構築ガイドの作成
- xxxプロジェクト 開発環境構築ガイド
- 前提条件
- 開発環境構築手順
- パッケージ構成検討
- パッケージ構成の考え方
- 業務的な機能での分割例
- クラスの役割での分割例
Nablarchアンチパターン
Nablarchが想定する使用方法を踏まえずに設計、製造すると、 大きな手戻りや性能不良、最悪の場合は本番障害となる恐れがあります。
ここでは実際に発生した誤りの事例を紹介します。
Webアプリケーション
コンポーネントライフサイクルの誤解によるマルチスレッドバグ
NablarchのシステムリポジトリはDIコンテナ機能を持ちますが、他のDIコンテナとコンポーネントライフサイクルが異なります。
Nablarchのシステムリポジトリで管理されるコンポーネントのライフサイクルはsingleton
になります。 デフォルトのライフサイクルをprototype
あるいはrequest
と誤解し、コンポーネントの状態を書き換えてしまうと、 同じコンポーネントを使用する他のスレッド、リクエストに影響を与えてしまいます。
このようなバグを埋め込まないように、システムリポジトリで管理されるコンポーネントのライフサイクルを把握しておく必要があります。
まず、Nablarchを使用する場合、通常の業務アプリケーションコードを作成する際にシステムリポジトリからコンポーネントを 取得しなければならないケースは多くありません(システム基盤部品などを作成する場合は別です)。 さらに、コンポーネントライフサイクルがsingleton
であるので、初期化処理以外でコンポーネントの状態を書き換える使い方はしません。 このため、もしコンポーネントの状態を書き換えるコードを見かけたら、上記のような誤解によるバグである可能性を疑う必要があります。
Nablarchバッチ
バッチを設計・実装する際、フレームワークの仕組みを理解していないと、誤った構造のバッチを作ってしまう恐れがあります。 その場合でも、バッチ処理としては業務要件は満たせることもあるのですが、件数が増えた時に 性能劣化を起こしたり、異常終了してしまうこともあります。 少ない件数でテストする単体テスト工程では、そのような不具合に気づくことができず、 大量のデータでテストできるプロジェクト終盤まで発覚しない恐れがあります。 フレームワークの仕組みを理解して、誤った設計・実装を行わないようにしましょう。
以下に誤った実装例を示します。
N+1問題
「N+1」の「N」はcreateReaderメソッドで作成したリーダが持っているデータ件数(SELECTヒット件数)のことです。 Nablarchバッチでは、handleメソッド内で、入力データを元に再度SELECTを発行ことで発生します。
処理対象件数が増加するほど性能が劣化します。 createReaderで発行するSQLで、1回のSQLで取得する(JOIN)ことで回避可能です。
1回のSQLでデータを取得できれば、データ取得に必要なSQLは処理対象件数に関わらず最初の1件のみですが、 N+1問題のあるバッチでは深刻な性能劣化を起こす恐れがあります。 処理対象件数が100件の場合は101件、10000件の場合は10001件のSQLが発行されることになります。
■NG例
以下の例では、売上SQLの取得件数+1回、売上明細SQLを実行することになります。
–createReaderメソッド
SELECT
売上ID,
売上日
FROM
売上
WHERE 売上日 = ?
–handleメソッド
SELECT
売上明細ID,
金額
FROM
売上明細
WHERE 売上ID = ?
■OK例
JOINすることで1回のSQLで必要なデータが取得でき、handleメソッド内でSQLを発行する必要がなくなります。
-createReader
SELECT
売上.売上ID,
売上.売上日,
売上明細.売上明細ID,
売上明細.金額
FROM 売上
INNER JOIN 売上明細 ON 売上.売上ID = 売上明細.売上ID
WHERE 売上.売上日 = ?
フレームワーク制御下にないループ処理
「handleメソッドにて、自前でSELECTE文を発行しループして登録更新処理をする」というアンチパターンです。 フレームワークでのループは一定間隔でコミットが行われるようになっていますが、自前でループした場合はコミットは実行されません。 このため、更新件数が増えるとトランザクションログを逼迫することになります。
■NG例
public Result handle(ExecutionContext context) {
// 検索実行
SqlResultSet sqlResultSet = search("SEARCH");
// 自前でループ処理
for (SqlRow row : sqlResultSet) {
// :
// 更新処理
}
}
検索結果が大量になると、トランザクション内で大量のUPDATE文が実行されることになります。 特に、NoInputDataBatchActionを 使って上記のようなループ処理をしているのは典型的な誤りです。
また、大量件数を処理できるように、ループの一定回数毎にコミットを実行するといったトランザクション制御を自前で行うケースも過去に見られました。これはフレームワークで行っている処理を独自に再実装することになるため、品質・生産性を低下させる要因となります。
解決法
自前のループ処理ではなく、フレームワーク管理のループ処理で実現できるようにします。 上記の例ですと、handle内で発行しているSQLをcreateReaderで行うようにします。
JSR352バッチ
Batchletの誤用
前述の「フレームワーク制御下にないループ処理」(Nablarchバッチ)と同様のアンチパターンになります。 Chunkで設計・実装すべきバッチをBatchletで実装することで同様の問題が発生します。
バッチの種類にあるとおり、それぞれの用途を理解して適切に使い分ける必要があります。
バッチの種類 | 用途 |
---|---|
Batchlet | 外部システムからのファイル取得や、SQL1つで処理が完結するような処理 |
Chunk | ファイルやデータベースなどの入力データソースからレコードを読み込み業務処理を実行するような処理 |