# 機能設計書 77-通知送信

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおけるメール・SMS等の通知送信機能について記載する。

### 本機能の処理概要

通知送信機能は、メール、SMS等の各種チャネルを通じてユーザーに通知を送信する機能である。通知プロバイダー（SendGrid、Twilio等）と連携し、テンプレートベースまたはカスタムコンテンツの通知を送信する。冪等性キーによる重複送信防止や、送信ステータスの追跡機能も提供する。

**業務上の目的・背景**：
ECサイトでは、注文確認、発送通知、パスワードリセット、プロモーション告知など、様々な場面でユーザーへの通知が必要となる。本機能は、これらの通知を一元的に管理し、チャネルやプロバイダーを抽象化することで、通知基盤の柔軟性と拡張性を確保する。

**機能の利用シーン**：
- 注文確認メールの送信
- 発送完了通知の送信
- パスワードリセットメールの送信
- ユーザー招待メールの送信
- プロモーション通知の送信
- SMS による注文ステータス通知

**主要な処理内容**：
1. 通知データの受信とバリデーション
2. 冪等性キーによる重複送信チェック
3. チャネルに対応するプロバイダーの特定
4. プロバイダー経由での通知送信
5. 送信結果の記録と更新

**関連システム・外部連携**：
- 通知モジュール（Notification Module）
- 外部通知プロバイダー（SendGrid、Twilio等）
- イベントシステム（注文イベント等からの連携）

**権限による制御**：
- 内部APIとして使用（ワークフローステップからの呼び出し）
- 管理APIでの通知一覧取得は管理者権限必須

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | パスワードリセット画面 | 補助機能 | パスワードリセットメールの送信 |
| 120 | ユーザー招待画面 | 補助機能 | 招待メールの送信 |

## 機能種別

通知処理 / 外部連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| to | string | Yes | 通知先（メールアドレス、電話番号等） | チャネルに応じた形式 |
| from | string | No | 送信元 | - |
| channel | string | Yes | 通知チャネル（email、sms等） | プロバイダーでサポート |
| template | string | No | テンプレートID | - |
| content | NotificationContent | No | 通知コンテンツ | - |
| data | object | No | テンプレートデータ | - |
| provider_data | object | No | プロバイダー固有データ（cc, bcc等） | - |
| trigger_type | string | No | トリガー種別 | - |
| resource_id | string | No | 関連リソースID | - |
| resource_type | string | No | 関連リソースタイプ | - |
| receiver_id | string | No | 受信者ID | - |
| original_notification_id | string | No | 再送時の元通知ID | - |
| idempotency_key | string | No | 冪等性キー | - |
| attachments | Attachment[] | No | 添付ファイル | - |

### 入力データソース

- ワークフローステップ（sendNotificationsStep）からの呼び出し
- イベントハンドラからの呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | 通知ID（noti_プレフィックス） |
| to | string | 通知先 |
| from | string | 送信元 |
| channel | string | 通知チャネル |
| template | string | テンプレートID |
| status | string | 送信ステータス（pending/success/failure） |
| external_id | string | プロバイダー側ID |
| provider_id | string | 使用プロバイダーID |
| created_at | Date | 作成日時 |
| updated_at | Date | 更新日時 |

### 出力先

- notificationテーブル
- 外部通知プロバイダー

## 処理フロー

### 処理シーケンス

```
1. 通知リクエスト受信
   └─ createNotifications()
2. 冪等性キーチェック
   └─ 既存通知の確認（失敗時は再試行可能）
3. プロバイダー特定
   └─ チャネルに対応するプロバイダー検索
4. 通知レコード作成
   └─ ステータス: PENDING
5. プロバイダー送信
   └─ NotificationProviderService.send()
6. 結果更新
   └─ ステータス: SUCCESS/FAILURE、external_id設定
7. 結果返却
   └─ 作成された通知情報
```

### フローチャート

```mermaid
flowchart TD
    A[通知リクエスト受信] --> B{冪等性キーあり?}
    B -->|Yes| C[既存通知検索]
    B -->|No| D[プロバイダー特定]
    C --> E{送信済み?}
    E -->|Yes, Success| F[スキップ]
    E -->|No or Failure| D
    D --> G{プロバイダー有効?}
    G -->|No| H[ステータス: FAILURE]
    G -->|Yes| I[通知レコード作成]
    I --> J[プロバイダー送信]
    J --> K{送信成功?}
    K -->|Yes| L[ステータス: SUCCESS]
    K -->|No| M[ステータス: FAILURE]
    L --> N[結果更新]
    M --> N
    H --> N
    F --> O[結果返却]
    N --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-77-01 | 冪等性保証 | 同一冪等性キーの通知は重複送信しない | idempotency_key指定時 |
| BR-77-02 | 失敗時再試行 | 失敗した通知は同一キーで再試行可能 | status=FAILUREの場合 |
| BR-77-03 | プロバイダー必須 | 有効なプロバイダーがない場合はFAILURE | 送信時 |
| BR-77-04 | チャネルマッチング | 通知チャネルに対応するプロバイダーを使用 | 送信時 |

### 計算ロジック

該当なし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 作成 | notification | INSERT | 通知レコード作成 |
| 更新 | notification | UPDATE | ステータス・external_id更新 |
| 確認 | notification | SELECT | 冪等性キーで既存確認 |

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

#### notification

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動生成（noti_プレフィックス） | - |
| INSERT | to | 入力値 | 検索可能 |
| INSERT | from | 入力値 | - |
| INSERT | channel | 入力値 | - |
| INSERT | template | 入力値 | - |
| INSERT | data | 入力値（JSON） | - |
| INSERT | status | PENDING | 初期値 |
| INSERT | provider_id | 特定されたプロバイダーID | - |
| INSERT | idempotency_key | 入力値 | ユニーク制約 |
| UPDATE | status | SUCCESS/FAILURE | 送信結果 |
| UPDATE | external_id | プロバイダーからのID | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | NOT_FOUND | チャネルに対応するプロバイダーなし | プロバイダー設定追加 |
| 400 | INVALID_DATA | プロバイダーが無効 | プロバイダー有効化 |
| 500 | UNEXPECTED_STATE | プロバイダー送信エラー | 再試行/設定確認 |

### リトライ仕様

- 送信失敗はステータスFAILUREで記録
- 同一冪等性キーで再試行可能
- 補償アクション（ロールバック）は不可（送信済み通知は取り消せない）

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

- 通知レコード作成はトランザクション内で実行
- 送信処理は外部呼び出しのためトランザクション外
- 結果更新は非同期でも実行可能

## パフォーマンス要件

- 通知送信は promiseAll で並列実行
- aggregateErrors オプションで部分失敗を許容

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

- 通知データの機密情報取り扱い
- プロバイダー認証情報の安全管理
- GDPR等のデータ保持期間への対応（TTL検討）

## 備考

- 通知テンプレートはプロバイダー側で管理可能
- 通知データはGDPR対応でTTL設定を検討中
- 補償アクション未実装（送信済み通知は取り消せない）

---

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

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

### 推奨読解順序

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

通知のデータモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | notification.ts | `packages/modules/notification/src/models/notification.ts` | 通知モデル定義 |
| 1-2 | notification-provider.ts | `packages/modules/notification/src/models/notification-provider.ts` | プロバイダーモデル |

**読解のコツ**:
- idempotency_keyのユニーク制約に注意
- statusはNotificationStatus enum（PENDING/SUCCESS/FAILURE）
- providerとの関連（belongsTo）を確認

#### Step 2: ワークフローステップを理解する

通知送信の起点を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | send-notifications.ts | `packages/core/core-flows/src/notification/steps/send-notifications.ts` | 送信ステップ |

**主要処理フロー**:
1. **12-75行目**: SendNotificationsStepInput型定義（詳細なコメント付き）
2. **81-94行目**: ステップ定義
3. **84-86行目**: NotificationModuleService解決
4. **87行目**: createNotifications呼び出し

#### Step 3: サービス層を理解する

通知モジュールサービスの実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | notification-module-service.ts | `packages/modules/notification/src/services/notification-module-service.ts` | 通知サービス |

**主要処理フロー**:
- **60-94行目**: createNotifications - 公開メソッド
- **96-234行目**: createNotifications_ - 内部実装
- **107-109行目**: 冪等性キー抽出
- **118-124行目**: 既存通知検索
- **130-137行目**: 再送可能判定（未送信 or FAILURE）
- **139-143行目**: チャネルに対応するプロバイダー取得
- **183-217行目**: プロバイダー送信と結果処理

#### Step 4: プロバイダーサービスを理解する

プロバイダーとの連携を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | notification-provider.ts | `packages/modules/notification/src/services/notification-provider.ts` | プロバイダーサービス |

**主要処理フロー**:
- **75-97行目**: getProviderForChannels - チャネルからプロバイダー特定
- **99-105行目**: send - プロバイダー呼び出し

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

```
sendNotificationsStep
    │
    └─ NotificationModuleService.createNotifications()
           │
           ├─ 冪等性チェック
           │      └─ notificationService_.list()
           │
           ├─ プロバイダー特定
           │      └─ NotificationProviderService.getProviderForChannels()
           │
           ├─ 通知レコード作成
           │      └─ notificationService_.create()
           │
           ├─ プロバイダー送信
           │      └─ NotificationProviderService.send()
           │             └─ INotificationProvider.send()
           │
           └─ 結果更新
                  └─ notificationService_.update()
```

### データフロー図

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

{                         createNotifications_
  to: "user@...",           │
  channel: "email",         ├─> 冪等性チェック                 notification
  template: "order_conf",   │   idempotency_key検索           テーブル
  data: {...}               │                                  │
}                           ├─> プロバイダー特定               │
                            │   channels → provider            │
                            │                                  │
                            ├─> 通知レコード作成               │
                            │   status: PENDING                │
                            │                                  │
                            ├─> プロバイダー送信  ──────────>  外部プロバイダー
                            │   (SendGrid, Twilio)             (メール/SMS送信)
                            │
                            └─> 結果更新
                                status: SUCCESS/FAILURE
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| send-notifications.ts | `packages/core/core-flows/src/notification/steps/send-notifications.ts` | ソース | 送信ステップ |
| notification-module-service.ts | `packages/modules/notification/src/services/notification-module-service.ts` | ソース | 通知サービス |
| notification-provider.ts | `packages/modules/notification/src/services/notification-provider.ts` | ソース | プロバイダーサービス |
| notification.ts | `packages/modules/notification/src/models/notification.ts` | ソース | 通知モデル |
| notification-provider.ts | `packages/modules/notification/src/models/notification-provider.ts` | ソース | プロバイダーモデル |
| route.ts | `packages/medusa/src/api/admin/notifications/route.ts` | ソース | 管理API（一覧取得） |
