# 機能設計書 3-商品オプション管理

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおける商品オプション（Product Option）の作成・取得・更新・削除機能の設計を記述する。

### 本機能の処理概要

**業務上の目的・背景**：
商品オプションは、サイズ、色、素材などの商品属性を定義するためのエンティティである。オプションは商品に紐付き、各オプションには複数のオプション値（Option Values）を持つことができる。バリアントはこれらのオプション値の組み合わせとして構成されるため、オプションはバリアント管理の基盤となる。

**機能の利用シーン**：
- 新規商品登録時のオプション（サイズ、色等）定義
- 既存商品への新しいオプション追加（例：素材オプションの追加）
- オプション値の追加・変更（例：新サイズの追加）
- 不要になったオプションの削除

**主要な処理内容**：
1. オプションの新規作成（タイトルと初期値の設定）
2. オプションの一覧取得（商品IDによるフィルタリング）
3. オプションの個別取得
4. オプションの更新（タイトル変更、値の追加・削除）
5. オプションの削除（カスケードで値も削除）

**関連システム・外部連携**：
- 商品モジュール（Product Module）：親商品との関連管理
- バリアントとの連携：バリアントはオプション値を参照

**権限による制御**：
- Admin API経由のアクセスには認証が必要（AuthenticatedMedusaRequest）
- オプションの作成・更新・削除は管理者権限を持つユーザーのみ実行可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | 商品詳細画面 | 主画面 | オプション一覧の表示 |
| 13 | オプション編集画面 | 主画面 | オプションの作成・更新処理 |

## 機能種別

CRUD操作（Create / Read / Update / Delete）

## 入力仕様

### 入力パラメータ

#### オプション作成（POST /admin/products/:id/options）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| title | string | Yes | オプション名 | 必須、商品内でユニーク |
| values | array | No | オプション値配列 | 各値は文字列 |
| metadata | object | No | メタデータ | JSON形式 |

#### オプション更新（POST /admin/products/:id/options/:option_id）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| title | string | No | オプション名 | 商品内でユニーク |
| values | array | No | オプション値配列 | - |
| metadata | object | No | メタデータ | - |

### 入力データソース

- 管理画面（Admin Dashboard）からのフォーム入力
- REST API経由のJSON/リクエストボディ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | オプションID（opt_xxx形式） |
| title | string | オプション名 |
| product_id | string | 親商品ID |
| values | array | オプション値配列 |
| values[].id | string | オプション値ID（optval_xxx形式） |
| values[].value | string | オプション値 |
| values[].option_id | string | 親オプションID |
| metadata | object | メタデータ |
| created_at | datetime | 作成日時 |
| updated_at | datetime | 更新日時 |
| deleted_at | datetime | 削除日時 |

### 出力先

- REST APIレスポンス（JSON形式）
- 管理画面への表示

## 処理フロー

### 処理シーケンス

```
1. オプション作成フロー
   ├─ オプション作成（createProductOptionsStep）
   │     └─ オプション値の同時作成
   ├─ フック実行（productOptionsCreated）
   └─ イベント発行（ProductOptionWorkflowEvents.CREATED）

2. オプション取得フロー
   ├─ 商品IDによるフィルタリング
   ├─ refetchEntitiesによるデータ取得
   └─ レスポンス整形

3. オプション更新フロー
   ├─ セレクタによるオプション特定
   ├─ オプション更新（updateProductOptionsStep）
   ├─ フック実行（productOptionsUpdated）
   └─ イベント発行（ProductOptionWorkflowEvents.UPDATED）

4. オプション削除フロー
   ├─ オプション削除（deleteProductOptionsStep）
   │     └─ カスケードでオプション値も削除
   ├─ リモートリンク削除（removeRemoteLinkStep）
   ├─ フック実行（productOptionsDeleted）
   └─ イベント発行（ProductOptionWorkflowEvents.DELETED）
```

### フローチャート

```mermaid
flowchart TD
    A[オプション作成リクエスト] --> B{title重複チェック}
    B -->|重複なし| C[オプション作成]
    B -->|重複あり| ERR[エラー]
    C --> D{values指定あり?}
    D -->|Yes| E[オプション値作成]
    D -->|No| F[完了]
    E --> G[イベント発行]
    G --> F
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-O01 | タイトル一意性 | 同一商品内でオプションタイトルはユニーク | オプション作成・更新時 |
| BR-O02 | オプション値一意性 | 同一オプション内でオプション値はユニーク | オプション値追加時 |
| BR-O03 | カスケード削除 | オプション削除時にオプション値もカスケード削除 | オプション削除時 |
| BR-O04 | バリアント参照チェック | バリアントが参照中のオプション値は削除不可（要検討） | オプション値削除時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 作成 | product_option | INSERT | オプションレコードの挿入 |
| 作成 | product_option_value | INSERT | オプション値レコードの挿入 |
| 取得 | product_option | SELECT | オプションの取得 |
| 取得 | product_option_value | SELECT | オプション値の取得 |
| 更新 | product_option | UPDATE | オプションの更新 |
| 更新 | product_option_value | UPDATE/INSERT/DELETE | オプション値の更新 |
| 削除 | product_option | UPDATE | deleted_atの設定（論理削除） |
| 削除 | product_option_value | UPDATE | カスケード論理削除 |

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

#### product_option

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動生成（opt_xxx形式） | generateEntityId使用 |
| INSERT | title | リクエストの値 | 必須 |
| INSERT | product_id | URLパラメータの値 | 必須 |
| DELETE | deleted_at | 現在日時 | 論理削除 |

#### product_option_value

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動生成（optval_xxx形式） | generateEntityId使用 |
| INSERT | value | リクエストの値 | 必須 |
| INSERT | option_id | 親オプションのID | 必須 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| NOT_FOUND | 404 | 指定IDのオプションが存在しない | 正しいIDを指定 |
| INVALID_DATA | 400 | 同一商品内で重複するtitleを指定 | ユニークな値を指定 |
| INVALID_DATA | 400 | 同一オプション内で重複するvalueを指定 | ユニークな値を指定 |

### リトライ仕様

トランザクション失敗時は自動ロールバック。明示的なリトライ機構はなし。

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

- オプション作成・更新・削除はトランザクション内で処理
- オプション値の操作は親オプションと同一トランザクションで処理
- ワークフロー内の各ステップは補償関数を持ち、失敗時にロールバック

## パフォーマンス要件

- オプション一覧取得：ページネーション対応
- (product_id, title)にユニークインデックス（IDX_option_product_id_title_unique）
- (option_id, value)にユニークインデックス（IDX_option_value_option_id_unique）

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

- Admin API経由のアクセスには認証必須（AuthenticatedMedusaRequest）
- title・valueの入力値サニタイズ

## 備考

- オプションのIDプレフィックスは「opt」
- オプション値のIDプレフィックスは「optval」
- オプションは商品削除時にカスケード削除される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | product-option.ts | `packages/modules/product/src/models/product-option.ts` | オプションモデル定義 |
| 1-2 | product-option-value.ts | `packages/modules/product/src/models/product-option-value.ts` | オプション値モデル定義 |

**主要処理フロー（product-option.ts）**:
- **5-28行目**: ProductOptionモデル定義
- **7行目**: IDプレフィックス「opt」
- **8行目**: titleフィールド（searchable, translatable）
- **10-12行目**: 商品との紐付け（belongsTo）
- **13-15行目**: オプション値との1対多リレーション
- **17-19行目**: カスケード削除設定（values）
- **20-27行目**: ユニークインデックス（product_id, title）

**主要処理フロー（product-option-value.ts）**:
- **4-27行目**: ProductOptionValueモデル定義
- **6行目**: IDプレフィックス「optval」
- **7行目**: valueフィールド（translatable）
- **9-13行目**: オプションとの紐付け（belongsTo）
- **14-16行目**: バリアントとの多対多リレーション
- **18-25行目**: ユニークインデックス（option_id, value）

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | route.ts | `packages/medusa/src/api/admin/products/[id]/options/route.ts` | 一覧・作成エンドポイント |
| 2-2 | [option_id]/route.ts | `packages/medusa/src/api/admin/products/[id]/options/[option_id]/route.ts` | 詳細・更新・削除 |

**主要処理フロー（route.ts）**:
- **12-31行目**: GET - オプション一覧取得
- **17-23行目**: refetchEntitiesによるクエリ実行
- **33-62行目**: POST - オプション作成
- **43-53行目**: createProductOptionsWorkflow呼び出し

**主要処理フロー（[option_id]/route.ts）**:
- **14-28行目**: GET - オプション詳細取得
- **30-57行目**: POST - オプション更新
- **41-47行目**: updateProductOptionsWorkflow呼び出し
- **59-84行目**: DELETE - オプション削除
- **67-69行目**: deleteProductOptionsWorkflow呼び出し

#### Step 3: ワークフローを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | create-product-options.ts | `packages/core/core-flows/src/product/workflows/create-product-options.ts` | 作成ワークフロー |
| 3-2 | update-product-options.ts | `packages/core/core-flows/src/product/workflows/update-product-options.ts` | 更新ワークフロー |
| 3-3 | delete-product-options.ts | `packages/core/core-flows/src/product/workflows/delete-product-options.ts` | 削除ワークフロー |

**主要処理フロー（create-product-options.ts）**:
- **58-85行目**: createProductOptionsWorkflow定義
- **61行目**: createProductOptionsStep呼び出し
- **62-65行目**: フック定義（productOptionsCreated）
- **76-79行目**: イベント発行（ProductOptionWorkflowEvents.CREATED）

**主要処理フロー（update-product-options.ts）**:
- **58-89行目**: updateProductOptionsWorkflow定義
- **61行目**: updateProductOptionsStep呼び出し
- **80-83行目**: イベント発行（ProductOptionWorkflowEvents.UPDATED）

**主要処理フロー（delete-product-options.ts）**:
- **48-76行目**: deleteProductOptionsWorkflow定義
- **51行目**: deleteProductOptionsStep呼び出し
- **62-70行目**: リモートリンク削除とイベント発行の並列実行

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

```
Admin API Route
    │
    ├─ GET /admin/products/:id/options
    │      └─ refetchEntities()
    │             └─ Query.graph()
    │                    └─ ProductOption取得
    │
    ├─ POST /admin/products/:id/options
    │      └─ createProductOptionsWorkflow.run()
    │             ├─ createProductOptionsStep
    │             │      └─ ProductModuleService.createProductOptions()
    │             └─ emitEventStep
    │
    ├─ POST /admin/products/:id/options/:option_id
    │      └─ updateProductOptionsWorkflow.run()
    │             ├─ updateProductOptionsStep
    │             │      └─ ProductModuleService.updateProductOptions()
    │             └─ emitEventStep
    │
    └─ DELETE /admin/products/:id/options/:option_id
           └─ deleteProductOptionsWorkflow.run()
                  ├─ deleteProductOptionsStep
                  │      └─ ProductModuleService.deleteProductOptions()
                  ├─ removeRemoteLinkStep
                  └─ emitEventStep
```

### データフロー図

```
[オプション作成]

Input                    Processing                         Output
──────────────────────────────────────────────────────────────────────
title           ──▶   createProductOptionsStep    ──▶   option_id
values                         │                         option_values
product_id                     ▼
                      emitEventStep               ──▶   イベント発行
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| product-option.ts | `packages/modules/product/src/models/product-option.ts` | モデル | オプションエンティティ定義 |
| product-option-value.ts | `packages/modules/product/src/models/product-option-value.ts` | モデル | オプション値エンティティ |
| product-module-service.ts | `packages/modules/product/src/services/product-module-service.ts` | サービス | オプション操作メソッド |
| route.ts | `packages/medusa/src/api/admin/products/[id]/options/route.ts` | API | 一覧・作成エンドポイント |
| [option_id]/route.ts | `packages/medusa/src/api/admin/products/[id]/options/[option_id]/route.ts` | API | 詳細・更新・削除 |
| create-product-options.ts | `packages/core/core-flows/src/product/workflows/create-product-options.ts` | ワークフロー | 作成処理 |
| update-product-options.ts | `packages/core/core-flows/src/product/workflows/update-product-options.ts` | ワークフロー | 更新処理 |
| delete-product-options.ts | `packages/core/core-flows/src/product/workflows/delete-product-options.ts` | ワークフロー | 削除処理 |
