# 機能設計書 20-マイグレーション

## 概要

本ドキュメントは、Payload CMSにおけるデータベースマイグレーション機能の設計を定義する。Drizzle ORMおよびDrizzle Kitを使用して、スキーマ変更の検出、マイグレーションファイルの生成・実行・ロールバックを提供する。

### 本機能の処理概要

マイグレーション機能は、Payload CMSのデータベーススキーマ変更を管理する。開発環境ではスキーマをデータベースに直接プッシュし、本番環境ではマイグレーションファイルを使用して安全にスキーマを更新する。

**業務上の目的・背景**：データベーススキーマの変更を追跡可能かつ再現可能な形で管理することで、チーム開発やCI/CDパイプラインでの安全なデプロイを実現する。開発環境と本番環境で異なる戦略を採用し、開発効率と運用安定性を両立する。

**機能の利用シーン**：
- コレクション/フィールド追加・変更時のスキーマ更新
- 開発環境でのスキーマ即時反映（pushDevSchema）
- 本番デプロイ時のマイグレーション実行
- スキーマ変更のロールバック
- マイグレーション状態の確認

**主要な処理内容**：
1. マイグレーション生成：スキーマ差分からUP/DOWNスクリプト生成
2. マイグレーション実行：バッチ単位での順次適用
3. ロールバック：最新バッチのマイグレーション取り消し
4. 開発環境プッシュ：スキーマの直接適用

**関連システム・外部連携**：
- Drizzle ORM
- Drizzle Kit
- payload-migrationsコレクション

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | （該当なし） | - | CLIコマンドのため画面連携なし |

## 機能種別

CLIコマンド / データベース管理

## 入力仕様

### マイグレーションファイル構造

```typescript
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'

export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
  await db.execute(sql`ALTER TABLE posts ADD COLUMN new_field text;`)
}

export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
  await db.execute(sql`ALTER TABLE posts DROP COLUMN new_field;`)
}
```

### マイグレーション引数

| パラメータ | 型 | 説明 |
|-----------|-----|------|
| db | DrizzleTransaction / LibSQLDatabase / PostgresDB | Drizzleインスタンス |
| payload | Payload | Payloadインスタンス |
| req | PayloadRequest | リクエストオブジェクト（トランザクション含む） |

## 出力仕様

### payload-migrationsコレクション

| フィールド | 型 | 説明 |
|-----------|-----|------|
| name | string | マイグレーション名（ファイル名） |
| batch | number | バッチ番号（-1は開発プッシュ） |
| createdAt | timestamp | 作成日時 |
| updatedAt | timestamp | 更新日時 |

## 処理フロー

### マイグレーション生成（createMigration）

```
1. buildCreateMigration()でcreateMigration関数を生成

2. 現在のスキーマからDrizzle JSONを生成
   └─ generateDrizzleJson(this.schema)

3. 最新のスナップショットを読み込み
   └─ migrations/*.json を検索

4. スキーマ差分を計算
   └─ generateMigration(before, after) → UP文
   └─ generateMigration(after, before) → DOWN文

5. マイグレーションファイルを生成
   └─ {timestamp}_{name}.ts
   └─ {timestamp}_{name}.json（スナップショット）

6. migrations/index.tsを更新
   └─ writeMigrationIndex()
```

### マイグレーション実行（migrate）

```
1. マイグレーションファイルを読み込み
   └─ readMigrationFiles({ payload })

2. payload-migrationsから既存マイグレーションを取得
   └─ 最新バッチ番号を特定

3. 未実行マイグレーションを順次実行
   └─ initTransaction()
   └─ migration.up({ db, payload, req })
   └─ payload-migrationsにレコード作成
   └─ commitTransaction()

4. エラー時はロールバック
   └─ killTransaction()
```

### マイグレーションロールバック（migrateDown）

```
1. 最新バッチのマイグレーションを取得
   └─ getMigrations({ payload })

2. バッチ内の各マイグレーションを逆順に実行
   └─ initTransaction()
   └─ migrationFile.down({ db, payload, req })
   └─ payload-migrationsからレコード削除
   └─ commitTransaction()
```

### 開発環境プッシュ（pushDevSchema）

```
1. スキーマ変更の有無を確認
   └─ dequal(previousSchema, currentSchema)

2. Drizzle Kitでスキーマをプッシュ
   └─ pushSchema(schema, drizzle, schemaName, tablesFilter)

3. 警告がある場合はユーザーに確認
   └─ データロスの可能性がある場合は警告

4. apply()でスキーマを適用

5. payload_migrationsにdevレコードを作成/更新
   └─ batch = -1（開発プッシュを示す）
```

## ビジネスルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | バッチ管理 | マイグレーションはバッチ単位で管理 | migrate時 |
| BR-02 | 順次実行 | マイグレーションは名前順に順次実行 | migrate時 |
| BR-03 | 開発プッシュ | batch=-1は開発環境でのプッシュを示す | pushDevSchema時 |
| BR-04 | トランザクション | 各マイグレーションはトランザクション内で実行 | migrate/migrateDown時 |
| BR-05 | エラー時終了 | マイグレーションエラー時はprocess.exit(1) | エラー発生時 |

## CLIコマンド

| コマンド | 説明 |
|---------|------|
| payload migrate | 未実行マイグレーションを実行 |
| payload migrate:down | 最新バッチをロールバック |
| payload migrate:status | マイグレーション状態を表示 |
| payload migrate:create | 新しいマイグレーションを生成 |
| payload migrate:fresh | 全テーブル削除後、全マイグレーション実行 |
| payload migrate:refresh | ロールバック後、全マイグレーション実行 |
| payload migrate:reset | 全マイグレーションをロールバック |

## 環境変数

| 変数名 | 説明 |
|--------|------|
| NODE_ENV | production時はmigrate、それ以外はpushDevSchema |
| PAYLOAD_MIGRATING | マイグレーション実行中フラグ |
| PAYLOAD_FORCE_DRIZZLE_PUSH | trueでスキーマ変更チェックをスキップ |

---

## コードリーディングガイド

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| migrate.ts | `packages/drizzle/src/migrate.ts` | ソース | マイグレーション実行 |
| migrateDown.ts | `packages/drizzle/src/migrateDown.ts` | ソース | ロールバック |
| migrateStatus.ts | `packages/drizzle/src/migrateStatus.ts` | ソース | 状態表示 |
| migrateFresh.ts | `packages/drizzle/src/migrateFresh.ts` | ソース | 全削除後マイグレーション |
| buildCreateMigration.ts | `packages/drizzle/src/utilities/buildCreateMigration.ts` | ソース | マイグレーション生成 |
| pushDevSchema.ts | `packages/drizzle/src/utilities/pushDevSchema.ts` | ソース | 開発環境スキーマプッシュ |

### 推奨リーディング順序

1. **pushDevSchema.ts** - 開発環境での動作を理解
2. **buildCreateMigration.ts** - マイグレーション生成の流れを確認
3. **migrate.ts** - マイグレーション実行の流れを確認
4. **migrateDown.ts** - ロールバックの流れを確認
5. **migrateStatus.ts** - 状態確認の実装を確認

### 主要処理フロー

#### migrate.ts

- **18-21行目**: migrate関数のエントリーポイント
- **23-27行目**: マイグレーションファイルの読み込み
- **37-44行目**: payload-migrationsから既存マイグレーションを取得
- **46-68行目**: 開発プッシュ（batch=-1）の警告処理
- **75-87行目**: 新規マイグレーションの順次実行
- **90-118行目**: runMigrationFile関数（個別マイグレーション実行）

#### migrateDown.ts

- **16-27行目**: 既存マイグレーションの取得
- **33-73行目**: 最新バッチのマイグレーションを逆順実行
- **48行目**: migration.down()呼び出し
- **56-60行目**: payload-migrationsからレコード削除

#### buildCreateMigration.ts

- **13-21行目**: buildCreateMigration関数のファクトリ
- **32-35行目**: Drizzle KitのgenerateDrizzleJson呼び出し
- **85-99行目**: 最新スナップショットの読み込み
- **101-116行目**: UP/DOWN SQL文の生成
- **143-156行目**: マイグレーションファイルの書き込み

#### pushDevSchema.ts

- **21-41行目**: スキーマ変更の有無確認（dequal）
- **48-56行目**: pushSchema呼び出し（Drizzle Kit）
- **58-86行目**: 警告表示とユーザー確認
- **88行目**: apply()でスキーマ適用
- **102-113行目**: devレコードの作成/更新（batch=-1）

### プログラム呼び出し階層図

```
[payload migrate]
└── migrate()
    ├── readMigrationFiles()
    ├── migrationTableExists()
    ├── payload.find('payload-migrations')
    └── runMigrationFile()
        ├── initTransaction()
        ├── migration.up()
        ├── payload.create('payload-migrations')
        └── commitTransaction()

[payload migrate:create]
└── createMigration()
    ├── generateDrizzleJson()
    ├── generateMigration()
    ├── getMigrationTemplate()
    └── writeMigrationIndex()

[開発環境起動時]
└── connect()
    └── pushDevSchema()
        ├── dequal()
        ├── pushSchema()
        ├── apply()
        └── payload_migrations更新
```

### データフロー図

```
[Payload Config]
     │
     ▼
[buildRawSchema]
     │
     ▼
[Schema (Drizzle)]
     │
     ├──[開発環境]─────────┐
     │                     │
     │                     ▼
     │              [pushDevSchema]
     │                     │
     │                     ├──[pushSchema]
     │                     │       │
     │                     │       ▼
     │                     │   [Database]
     │                     │
     │                     └──[payload_migrations]
     │                            (batch=-1)
     │
     └──[本番環境]─────────┐
                          │
                          ▼
                   [migrate]
                          │
                          ├──[migration.up()]
                          │       │
                          │       ▼
                          │   [Database]
                          │
                          └──[payload_migrations]
                                 (batch=N)
```

### マイグレーションファイル構造

```
migrations/
├── 20260301_120000_initial.ts      # マイグレーションコード
├── 20260301_120000_initial.json    # スキーマスナップショット
├── 20260301_130000_add_field.ts
├── 20260301_130000_add_field.json
└── index.ts                         # エクスポートファイル
```

### 開発環境と本番環境の違い

| 観点 | 開発環境 | 本番環境 |
|------|---------|---------|
| スキーマ適用 | pushDevSchema（直接適用） | migrate（マイグレーション実行） |
| バッチ番号 | -1（固定） | 1, 2, 3...（インクリメント） |
| 確認プロンプト | データロス時のみ | なし（自動実行） |
| ロールバック | 不可（直接変更） | migrateDown |
