0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IBM Bob作成: アーキテクチャ説明文書:PostgreSQL → Db2 移行

0
Posted at

経緯は「IBM Bobを使って、PostgreSQLのToDoアプリをDb2へ変換してみよう」を参照下さい。
こちらはIBM Bobが作成したアーキテクチャ説明文書です。


アーキテクチャ説明文書:PostgreSQL → Db2 移行

作成日: 2026-03-04
プロジェクト: PERN TODO → Db2 TODO 移行


📋 目次

  1. 概要
  2. 変更前のアーキテクチャ(PostgreSQL)
  3. 変更後のアーキテクチャ(Db2)
  4. 主要な変更点
  5. データフロー
  6. コネクションプール設計
  7. エラーハンドリング戦略

概要

本ドキュメントでは、PostgreSQLベースのTodoアプリケーションをDb2ベースに移行する際のアーキテクチャ変更について説明します。

移行の目的

  • PostgreSQL依存からDb2への移行
  • コネクションプール実装による性能向上
  • エラーハンドリングの強化
  • ユーザー体験の向上(ローディング表示)

変更前のアーキテクチャ(PostgreSQL)

システム構成図

主要コンポーネント

1. クライアント層

  • React + Vite: フロントエンドフレームワーク
  • 機能: Todo CRUD操作のUI提供

2. アプリケーション層

  • Express: Node.jsウェブフレームワーク
  • CORS: クロスオリジンリクエスト対応
  • Todo Routes: CRUD APIエンドポイント

3. データアクセス層

  • pg Pool: PostgreSQL接続プール
  • 特徴:
    • 自動接続管理
    • トップレベルで初期化
    • pool.query() で直接SQL実行

4. データベース層

  • PostgreSQL: リレーショナルデータベース
  • テーブル: todos (id, title, done)

PostgreSQL版の特徴

// db.js - シンプルな初期化
export const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20,
  idleTimeoutMillis: 30000
});

// routes/todos.js - 直接クエリ実行
const { rows } = await pool.query(
  'INSERT INTO todos (title) VALUES ($1) RETURNING *',
  [title]
);

利点:

  • シンプルな実装
  • RETURNING句でINSERT結果を即座に取得
  • 自動的な接続管理

課題:

  • PostgreSQL専用(他DBへの移行困難)
  • エラーハンドリングが不十分

変更後のアーキテクチャ(Db2)

システム構成図

主要コンポーネント

1. クライアント層(拡張)

  • React + Vite: フロントエンドフレームワーク
  • Loading State: 処理中の表示管理
  • Error Display: エラーメッセージ表示
  • 新機能:
    • ローディング表示(⏳ 処理中、お待ちください...)
    • ボタンのdisable制御
    • エラーメッセージ表示

2. アプリケーション層(拡張)

  • Express: Node.jsウェブフレームワーク
  • Pool Initializer: プール初期化処理
  • 新機能:
    • 起動時のプール初期化
    • グレースフルシャットダウン

3. データアクセス層(新設計)

  • Connection Pool: ibm_db.Pool
  • executeQuery Helper: クエリ実行ヘルパー関数
  • Connection Manager: 接続の取得・返却管理
  • Error Handler: エラーハンドリング
  • 特徴:
    • 明示的な接続管理(open/close)
    • タイムアウト処理
    • エラーハンドリング強化

4. データベース層

  • IBM Db2 v12.1: エンタープライズデータベース
  • スキーマ: 環境変数で指定
  • テーブル: SCHEMA.TODOS (ID, TITLE, DONE)

Db2版の特徴

// db.js - 明示的な初期化
export const pool = new ibm_db.Pool();
pool.setMaxPoolSize(MAX_CONNECTIONS);

export async function initializePool() {
  return new Promise((resolve, reject) => {
    pool.open(connStr, (err, conn) => {
      if (err) reject(err);
      else {
        poolInitialized = true;
        conn.close(() => resolve());
      }
    });
  });
}

// executeQuery - ヘルパー関数
export async function executeQuery(sql, params = []) {
  return new Promise((resolve, reject) => {
    const timeout = setTimeout(() => {
      reject(new Error('Query timeout after 30 seconds'));
    }, 30000);
    
    pool.open(connStr, (err, conn) => {
      if (err) {
        clearTimeout(timeout);
        return reject(err);
      }
      
      conn.query(sql, params, (err, result) => {
        clearTimeout(timeout);
        conn.close(() => {
          if (err) reject(err);
          else resolve(result);
        });
      });
    });
  });
}

// routes/todos.js - 2段階クエリ
// 1. INSERT実行
await executeQuery(
  `INSERT INTO ${TABLE} (TITLE, DONE) VALUES (?, ?)`,
  [title, false]
);

// 2. 挿入されたデータを取得
const rows = await executeQuery(
  `SELECT ID, TITLE, DONE FROM ${TABLE}
   ORDER BY ID DESC
   FETCH FIRST 1 ROW ONLY`
);

利点:

  • Db2に最適化された実装
  • 明示的なリソース管理
  • 強化されたエラーハンドリング
  • タイムアウト処理

課題:

  • 実装が複雑
  • RETURNING句が使えない(2段階クエリ必要)

主要な変更点

1. データベースドライバ

項目 PostgreSQL Db2
パッケージ pg ibm_db
接続方法 new Pool({ ... }) new ibm_db.Pool() + pool.open()
クエリ実行 pool.query(sql, params) conn.query(sql, params, callback)
接続管理 自動 手動(open/close)

2. SQL構文

項目 PostgreSQL Db2
パラメータ $1, $2, $3 ?, ?, ?
RETURNING句 サポート 非サポート
カラム名 小文字 大文字
スキーマ指定 オプション 推奨
LIMIT句 LIMIT n FETCH FIRST n ROWS ONLY

3. データ型

項目 PostgreSQL Db2
BOOLEAN true/false 1/0
SERIAL SERIAL INT GENERATED ALWAYS AS IDENTITY
TEXT TEXT VARCHAR(n) または CLOB

4. 接続管理

PostgreSQL(自動管理)

// 接続取得・返却は自動
const { rows } = await pool.query('SELECT * FROM todos');

Db2(手動管理)

// 接続取得
pool.open(connStr, (err, conn) => {
  // クエリ実行
  conn.query('SELECT * FROM TODOS', (err, result) => {
    // 接続返却(必須)
    conn.close(() => {
      // 処理完了
    });
  });
});

データフロー

CREATE(Todo作成)のフロー

READ(Todo取得)のフロー


コネクションプール設計

プール設定

// 環境変数から設定を読み込み
const MAX_CONNECTIONS = parseInt(process.env.DB2_MAX_CONNECTIONS || '5', 10);
const MIN_CONNECTIONS = parseInt(process.env.DB2_MIN_CONNECTIONS || '1', 10);

// プール作成
export const pool = new ibm_db.Pool();
pool.setMaxPoolSize(MAX_CONNECTIONS);

// 初期化(最小接続数を確保)
pool.init(MIN_CONNECTIONS, connStr);

プールのライフサイクル

接続の取得・返却フロー


エラーハンドリング戦略

エラーの種類と対応

エラーハンドリングの実装

// executeQuery内のエラーハンドリング
export async function executeQuery(sql, params = []) {
  return new Promise((resolve, reject) => {
    // タイムアウト設定
    const timeout = setTimeout(() => {
      console.error('Query timeout after 30 seconds');
      reject(new Error('Query timeout after 30 seconds'));
    }, 30000);
    
    pool.open(connStr, (err, conn) => {
      if (err) {
        clearTimeout(timeout);
        console.error('Failed to get connection from pool:', err);
        return reject(err);
      }
      
      conn.query(sql, params, (err, result) => {
        clearTimeout(timeout);
        
        // 必ず接続を返却
        conn.close(() => {
          if (err) {
            console.error('Query execution error:', err);
            console.error('SQL:', sql);
            console.error('Params:', params);
            reject(err);
          } else {
            resolve(result);
          }
        });
      });
    });
  });
}

クライアント側のエラー表示

// App.jsx
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

async function addTodo(e) {
  e.preventDefault();
  if (!title.trim()) return;
  
  setLoading(true);
  setError(null);
  
  try {
    const response = await fetch(`${API}/todos`, {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({ title })
    });
    
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    
    setTitle('');
    await load();
  } catch (err) {
    console.error(err);
    setError('Todo の追加に失敗しました');
  } finally {
    setLoading(false);
  }
}

まとめ

移行による改善点

  1. エンタープライズDB対応: Db2 v12.1への完全対応
  2. リソース管理の明示化: 接続の取得・返却を明示的に管理
  3. エラーハンドリング強化: タイムアウト、接続エラー等の適切な処理
  4. UX向上: ローディング表示、エラーメッセージ表示

技術的な学び

  1. ドライバの違い: PostgreSQLとDb2のドライバAPIの違いを理解
  2. SQL方言: データベースごとのSQL構文の違いを理解
  3. 接続管理: 明示的な接続管理の重要性を理解
  4. エラーハンドリング: 多層的なエラーハンドリングの実装

作成者: Bob (AI Assistant)
最終更新: 2026-03-04
バージョン: 1.0

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?