# 機能設計書 87-データ同期

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおけるデータ同期機能の設計について記述する。インデックスモジュールの中核機能として、検索インデックスへのデータ同期を行う。

### 本機能の処理概要

**業務上の目的・背景**：大規模ECサイトでは、商品検索、注文検索、顧客検索など、高速な全文検索が必要である。データ同期機能は、各モジュールのエンティティデータを検索インデックス（Postgres内部インデックスまたは外部検索エンジン）に同期し、検索性能を向上させる。リアルタイム性、スケーラビリティ、データ整合性を確保しながら、ユーザー体験を向上させる。

**機能の利用シーン**：アプリケーション起動時のインデックス初期構築、エンティティ更新時のリアルタイム同期、手動でのインデックス再構築、インデックス状態の監視などで利用される。

**主要な処理内容**：
1. スキーマ構築（buildSchemaObjectRepresentation）：GraphQLスキーマからインデックス対象エンティティを解析
2. イベントリスナー登録（registerListeners）：エンティティ変更イベントを購読
3. 継続同期（continueSync）：ペンディング状態のエンティティを同期再開
4. フル同期（fullSync）：すべてのエンティティを最初から同期
5. リセット同期（resetSync）：インデックスデータを全削除して再同期
6. インデックス情報取得（getInfo）：各エンティティの同期状態を取得

**関連システム・外部連携**：イベントバスモジュール（エンティティ変更通知）、ストレージプロバイダー（インデックス保存先）と連携する。

**権限による制御**：インデックス管理操作には管理者権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | （画面未実装）| - | 本機能はAPI/バックグラウンド処理で利用 |

## 機能種別

データ連携 / バックグラウンド処理 / イベント駆動

## 入力仕様

### 入力パラメータ

#### 同期実行（sync）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| strategy | string | No | 同期戦略（full, reset, または未指定で継続） | full/reset/undefined のみ |

#### イベント購読（自動）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| event_name | string | Yes | エンティティ変更イベント名 | スキーマ定義に基づく |
| data | object | Yes | 変更されたエンティティデータ | - |

### 入力データソース

イベントバスからのエンティティ変更通知、APIからの手動同期リクエスト

## 出力仕様

### 出力データ

#### IndexInfo

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | インデックスメタデータID |
| entity | string | エンティティ名 |
| status | string | 同期状態（pending/processing/done/error） |
| fields | string[] | インデックス対象フィールド |
| updated_at | Date | 最終更新日時 |
| last_synced_key | string/null | 最後に同期したキー |

### 出力先

インデックスストレージ（Postgresテーブルまたは外部検索エンジン）

## 処理フロー

### 処理シーケンス

```
1. アプリケーション起動
   └─ スキーマからインデックス対象を解析
   └─ ストレージプロバイダーを初期化
   └─ イベントリスナーを登録
   └─ 既存インデックス設定との差分をチェック
   └─ 差分がある場合は同期を開始

2. エンティティ変更イベント受信
   └─ ストレージプロバイダーがイベントを消費
   └─ インデックスデータを更新

3. 手動同期
   └─ 戦略に応じて同期処理を実行
   └─ イベントバス経由でワーカーに通知
```

### フローチャート

```mermaid
flowchart TD
    A[アプリ起動] --> B[スキーマ解析]
    B --> C[ストレージプロバイダー初期化]
    C --> D[イベントリスナー登録]
    D --> E{ワーカーモード?}
    E -->|Yes| F[DataSynchronizer起動]
    E -->|No| G[リスナーのみ]
    F --> H[設定差分チェック]
    H --> I{差分あり?}
    I -->|Yes| J[同期開始]
    I -->|No| K[待機]

    L[sync要求] --> M{戦略}
    M -->|continue| N[continueSync]
    M -->|full| O[fullSync]
    M -->|reset| P[resetSync]
    N --> Q[ステータスをpendingに]
    O --> R[last_keyをnullに + pending]
    P --> S[テーブルTRUNCATE + 同期]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-87-1 | ワーカーモード制限 | データ同期はworkerモードでのみ実行 | 同期処理実行時 |
| BR-87-2 | イベント駆動更新 | エンティティ変更はイベント経由でインデックスに反映 | エンティティ変更時 |
| BR-87-3 | 増分同期 | last_synced_keyを使用して増分同期を実現 | 継続同期時 |
| BR-87-4 | ステータス管理 | pending→processing→done/errorの遷移 | 同期処理中 |
| BR-87-5 | スキーマ変更検知 | フィールド構成変更時は自動で再同期 | アプリ起動時 |

### 計算ロジック

fields_hashの計算：
- インデックス対象フィールドをソートしてハッシュ化
- 既存のhashと比較して変更を検知

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| onApplicationStart | index_metadata, index_sync | UPSERT | メタデータ同期 |
| sync (continue) | index_metadata | UPDATE | status=pending |
| sync (full) | index_metadata, index_sync | UPDATE | status=pending, last_key=null |
| sync (reset) | index_data, index_relation, index_metadata, index_sync | TRUNCATE | 全データ削除 |
| getInfo | index_metadata, index_sync | SELECT | 同期状態取得 |
| consumeEvent | index_data, index_relation | INSERT/UPDATE/DELETE | エンティティ同期 |

### テーブル別操作詳細

#### index_metadata

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPSERT | entity | エンティティ名 | 一意 |
| UPSERT | fields | カンマ区切りフィールド名 | - |
| UPSERT | fields_hash | フィールド構成ハッシュ | 変更検知用 |
| UPDATE | status | pending/processing/done/error | 同期状態 |

#### index_sync

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPSERT | entity | エンティティ名 | 一意 |
| UPDATE | last_key | 最後に同期したキー | 増分同期用 |

#### index_data

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPSERT | id | エンティティID | - |
| UPSERT | name | エンティティ名 | - |
| UPSERT | data | インデックス対象データ（JSON） | - |
| UPDATE | staled_at | 古くなった日時 | ソフト無効化 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| INVALID_DATA | 検証エラー | 無効な同期戦略を指定 | full/resetを指定 |
| MODULE_ERROR | 初期化エラー | EventBusModuleが未設定 | EventBusを設定 |
| SYNC_ERROR | 同期エラー | 同期処理中にエラー発生 | ログ確認、再同期 |

### リトライ仕様

- 同期エラー発生時はステータスがerrorに更新される
- 次回sync実行時またはアプリ再起動時に再試行可能

## トランザクション仕様

- resetSync時のTRUNCATEは単一トランザクションで実行
- イベント処理はストレージプロバイダーのトランザクション管理に依存
- メタデータ更新は@InjectManagerで管理

## パフォーマンス要件

- リアルタイム同期：イベント発生から1秒以内にインデックス反映
- バッチ同期：1000件/秒以上のスループット
- インデックス情報取得：100ms以内

## セキュリティ考慮事項

- インデックス管理操作には管理者権限が必要
- 機密データはインデックス対象から除外すべき
- 検索結果にアクセス制御を適用

## 備考

- ワーカーモードはmoduleDeclaration.worker_modeで判定
- ストレージプロバイダーはPostgresProviderがデフォルト
- GraphQLスキーマからインデックス対象を自動解析

---

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

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

インデックス関連のエンティティ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index-metadata.ts | `packages/modules/index/src/models/index-metadata.ts` | entity/fields/fields_hash/statusフィールド |
| 1-2 | index-sync.ts | `packages/modules/index/src/models/index-sync.ts` | entity/last_keyフィールド |
| 1-3 | index-data.ts | `packages/modules/index/src/models/index-data.ts` | id/name/data/staled_atフィールド |

**読解のコツ**: index_metadataはエンティティごとの同期設定を、index_syncは同期進捗を、index_dataは実際のインデックスデータを管理する。

#### Step 2: サービス初期化を理解する

アプリケーション起動時の初期化処理。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index-module-service.ts | `packages/modules/index/src/services/index-module-service.ts` | コンストラクタ、onApplicationStart_ |

**主要処理フロー**:
- **102-131行目**: コンストラクタでEventBus、ストレージプロバイダーを初期化
- **115行目**: ワーカーモード判定（worker_mode !== "server"）
- **133-137行目**: `__hooks.onApplicationStart`定義
- **139-187行目**: `onApplicationStart_`メソッド
  - **141行目**: スキーマ解析
  - **143-150行目**: ストレージプロバイダーインスタンス化
  - **152行目**: イベントリスナー登録
  - **164-182行目**: ワーカーモード時のみDataSynchronizer起動

#### Step 3: イベントリスナー登録を理解する

エンティティ変更イベントの購読処理。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index-module-service.ts | `packages/modules/index/src/services/index-module-service.ts` | registerListenersメソッド |

**主要処理フロー**:
- **195-230行目**: `registerListeners`メソッド
  - **196-198行目**: ワーカーモード以外はスキップ
  - **204-218行目**: スキーマ定義の各エンティティに対してリスナー登録
  - **214-217行目**: `eventBusModuleService_.subscribe()`でイベント購読
  - **222-228行目**: 同期関連イベント（continueSync, fullSync, resetSync）を購読

#### Step 4: 同期処理を理解する

3種類の同期戦略の実装。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index-module-service.ts | `packages/modules/index/src/services/index-module-service.ts` | sync, continueSync, fullSync, resetSyncメソッド |

**主要処理フロー**:
- **308-326行目**: `sync`メソッド（戦略振り分け）
- **333-377行目**: `continueSync`メソッド
  - **335-362行目**: 非ワーカーモード時はイベント発行のみ
  - **365-376行目**: ワーカーモード時は実際の同期実行
- **379-431行目**: `fullSync`メソッド
  - **398-404行目**: last_keyをnullにリセット
- **433-474行目**: `resetSync`メソッド
  - **437-443行目**: 4テーブルをTRUNCATE

#### Step 5: インデックス情報取得を理解する

同期状態の取得処理。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | index-module-service.ts | `packages/modules/index/src/services/index-module-service.ts` | getInfoメソッド |

**主要処理フロー**:
- **269-306行目**: `getInfo`メソッド
  - **279-286行目**: index_metadataとindex_syncを並列取得
  - **288-292行目**: last_keyをエンティティ名でマップ化
  - **294-303行目**: IndexInfo形式に変換

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

```
アプリケーション起動
    │
    └─ IndexModuleService.__hooks.onApplicationStart
           │
           ├─▶ buildSchemaObjectRepresentation_()
           │
           ├─▶ new StorageProvider()
           │
           ├─▶ registerListeners()
           │      └─▶ eventBusModuleService_.subscribe()
           │
           └─▶ (ワーカーモードのみ)
                  ├─▶ DataSynchronizer.onApplicationStart()
                  │
                  ├─▶ ConfigurationChecker.checkChanges()
                  │
                  └─▶ DataSynchronizer.syncEntities()

イベント発生
    │
    └─ storageProvider_.consumeEvent()
           │
           └─▶ インデックスデータ更新
```

### データフロー図

```
[入力]                          [処理]                                    [出力]

GraphQLスキーマ ───▶ buildSchemaObjectRepresentation_() ───▶ schemaObjectRepresentation_
                              │
                              └─▶ スキーマ解析、エンティティマップ作成

エンティティ変更イベント ───▶ storageProvider_.consumeEvent() ───▶ index_data更新
        │                              │
        └─ event_name, data            └─▶ INSERT/UPDATE/DELETE

sync要求 ───▶ IndexModuleService.sync() ───▶ 同期実行
     │                  │
     └─ strategy        ├─▶ index_metadata.status更新
                        │
                        └─▶ DataSynchronizer.syncEntities()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index-module-service.ts | `packages/modules/index/src/services/index-module-service.ts` | サービス | インデックス管理、同期制御 |
| data-synchronizer.ts | `packages/modules/index/src/services/data-synchronizer.ts` | サービス | データ同期実行 |
| postgres-provider.ts | `packages/modules/index/src/services/postgres-provider.ts` | プロバイダー | Postgresストレージ実装 |
| index-metadata.ts | `packages/modules/index/src/models/index-metadata.ts` | モデル | 同期メタデータ |
| index-sync.ts | `packages/modules/index/src/models/index-sync.ts` | モデル | 同期進捗 |
| index-data.ts | `packages/modules/index/src/models/index-data.ts` | モデル | インデックスデータ |
| configuration.ts | `packages/modules/index/src/utils/sync/configuration.ts` | ユーティリティ | 設定変更検知 |
