# 通知設計書 1-EmailNotification

## 概要

本ドキュメントは、Symfony NotifierコンポーネントにおけるEmail通知（EmailNotification）の設計仕様を記述する。EmailNotificationInterfaceを実装した通知オブジェクトがEmailChannelを通じてメール送信を行う仕組みについて、送信フロー・テンプレート・エラー処理等の観点から詳細に定義する。

### 本通知の処理概要

EmailNotificationは、Symfony Notifierコンポーネントが提供するメール送信チャネルを通じた通知機能である。Notificationオブジェクトに対してEmailNotificationInterfaceを実装することで、カスタムメールメッセージの生成が可能となり、EmailChannelがそのメッセージをSymfony Mailer（Transport）またはSymfony Messenger（MessageBus）経由で送信する。

**業務上の目的・背景**：アプリケーションにおいて、ユーザーや管理者に対する重要な通知をメールで確実に届ける必要がある。メール通知は最も広く利用される通知手段であり、パスワードリセット、注文確認、アラート通知など多様な業務シナリオで使用される。Symfony Notifierは、メール通知を統一的なAPIで扱うことを可能にし、他の通知チャネル（SMS、Chat等）と同列に管理できる抽象化を提供する。

**通知の送信タイミング**：アプリケーションコードからNotifier::send()が呼び出されたとき、またはNotificationオブジェクトのchannels設定に"email"が含まれている場合にEmailChannelが選択され、メール送信が実行される。送信はアプリケーションの任意のタイミング（画面操作、バッチ処理、イベント発火など）で行われる。

**通知の受信者**：EmailRecipientInterfaceを実装したRecipientオブジェクトがメール受信者となる。このインターフェースはgetEmail()メソッドを提供し、受信者のメールアドレスを返す。supports()メソッドにより、RecipientがEmailRecipientInterfaceを実装している場合のみEmailChannelが利用される。

**通知内容の概要**：件名（subject）、本文（content）、重要度（importance）、例外情報（exception）が通知に含まれる。Twig Bridgeが利用可能な場合はNotificationEmailテンプレートが使用され、リッチなHTML形式のメールが自動生成される。

**期待されるアクション**：受信者は通知メールの内容を確認し、通知の種別に応じた適切なアクション（例：パスワードリセットリンクのクリック、アラートへの対応、注文内容の確認など）を行うことが期待される。

## 通知種別

メール（Email）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（Transport直接送信）または非同期（Messenger Bus経由） |
| 優先度 | Notificationのimportanceに依存（urgent/high/medium/low） |
| リトライ | Notifierコンポーネント自体にはリトライ機構なし。Messenger利用時はMessengerのリトライ機構に依存 |

### 送信先決定ロジック

1. Notifier::send()に渡されたRecipientオブジェクトがEmailRecipientInterfaceを実装しているかをEmailChannel::supports()で判定する（EmailChannel.php 行88-91）
2. 通知オブジェクトがEmailNotificationInterfaceを実装している場合、asEmailMessage()メソッドでカスタムEmailMessageを生成する（EmailChannel.php 行53-55）
3. カスタムメッセージがnullの場合、EmailMessage::fromNotification()によりデフォルトのEmailMessageを生成する（EmailChannel.php 行57）
4. メールのToヘッダが未設定の場合、RecipientのgetEmail()から宛先を設定する（EmailChannel.php 行68-70）

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | EmailChannelコンストラクタの$fromパラメータ、またはEnvelopeのsenderから取得 |
| 送信元名称 | Addressオブジェクトで指定可能 |
| 件名 | Notification::getSubject()の値 |
| 形式 | Twig Bridge利用時: HTML（NotificationEmail）、未利用時: テキスト（Email） |

### 本文テンプレート

```
【Twig Bridge利用時（NotificationEmail）】
件名: {subject}
重要度: {importance}
本文: {content}
※例外情報がある場合は添付ファイルとして含まれる

【Twig Bridge未利用時（Email）】
件名: {subject}
本文: {content} または {subject}（contentが空の場合）
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| 例外情報 | テキスト | Notification::getException()がnullでない場合 | FlattenExceptionオブジェクトの情報（NotificationEmail利用時のみ） |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| subject | メール件名 | Notification::getSubject() | Yes |
| content | メール本文 | Notification::getContent() | No（空の場合subjectが使用される） |
| importance | 重要度 | Notification::getImportance() | Yes（デフォルト: high） |
| exception | 例外情報 | Notification::getException() | No |
| recipientEmail | 宛先メールアドレス | EmailRecipientInterface::getEmail() | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | Notifier::send()の実行 | Notificationのchannelsに"email"が含まれる、またはChannelPolicyでemailが選択される | アプリケーションコードから直接呼び出し |
| 画面操作 | ユーザーアクションによるNotifier::send()の実行 | 同上 | コントローラ等からの呼び出し |
| バッチ | バッチ処理からのNotifier::send()の実行 | 同上 | バッチジョブからの呼び出し |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| RecipientがEmailRecipientInterfaceを実装していない | EmailChannel::supports()がfalseを返し、送信されない |
| Recipientのメールアドレスが空文字 | EmailMessage::fromNotification()でInvalidArgumentExceptionがスローされる |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Notifier::send呼び出し] --> B[getChannels: チャネル決定]
    B --> C[EmailChannel::supports チェック]
    C -->|EmailRecipientInterface実装| D[EmailChannel::notify 実行]
    C -->|未実装| E[スキップ]
    D --> F{EmailNotificationInterface?}
    F -->|Yes| G[asEmailMessage でカスタムメッセージ生成]
    F -->|No| H[EmailMessage::fromNotification でデフォルト生成]
    G --> I[From/Toヘッダ設定]
    H --> I
    I --> J{Bus設定あり?}
    J -->|Yes| K[Messenger Bus経由で非同期送信]
    J -->|No| L[Transport直接送信]
    K --> M[終了]
    L --> M
    E --> M
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| 該当なし | - | Notifierコンポーネント自体はデータベースを直接参照しない |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| 該当なし | - | Notifierコンポーネント自体はデータベースを直接更新しない |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| LogicException | TransportもBusも両方nullの場合（EmailChannel.php 行40-42） | EmailChannelのコンストラクタでTransportまたはBusの少なくとも一方を指定する |
| LogicException | From未設定かつコンストラクタのfromもnull（EmailChannel.php 行61-63） | EmailChannel構築時にfromを設定するか、asEmailMessage()でFromヘッダを設定する |
| InvalidArgumentException | 受信者のメールアドレスが空文字（EmailMessage.php 行38-39） | 受信者に有効なメールアドレスを設定する |
| LogicException | RawMessageインスタンスにTransportを設定しようとした場合（EmailMessage.php 行108） | Emailインスタンスを使用する |
| TransportExceptionInterface | メール送信時のトランスポートエラー | アプリケーション側でcatchして適切に処理する |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Notifierコンポーネント自体にはリトライ機構なし |
| リトライ間隔 | Messenger利用時はMessengerのリトライ設定に依存 |
| リトライ対象エラー | Messenger利用時はMessengerのリトライポリシーに依存 |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | Notifierコンポーネント自体にはレート制限なし。Mailer Transportの設定に依存 |
| 1日あたり上限 | 同上 |

### 配信時間帯

Notifierコンポーネント自体に配信時間帯の制限機能はない。アプリケーション側で制御する必要がある。

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

- メールアドレスは個人情報であるため、ログ出力時にはマスキングを検討すること
- メール本文に機密情報を含む場合はTLS/SSL暗号化されたSMTP接続を使用すること
- Envelope設定により送信元アドレスの偽装を防止すること
- 例外情報をメール本文に含む場合、スタックトレースに機密情報が含まれないことを確認すること

## 備考

- EmailChannelはSymfony Mailerコンポーネントに依存しており、Transport（SMTP、sendmail等）の設定はMailer側で行う
- Messenger Bus経由の非同期送信を利用する場合は、symfony/messengerパッケージが必要
- NotificationEmailテンプレート（Twig Bridge）を使用する場合は、symfony/twig-bridgeパッケージが必要
- EmailMessage::transport()メソッドでX-Transportヘッダを設定することにより、Mailerの特定のTransportを指定可能

---

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

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

### 推奨読解順序

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

まず、通知の核となるデータモデルとインターフェースを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Notification.php | `src/Symfony/Component/Notifier/Notification/Notification.php` | 通知の基本データ構造（subject, content, importance, channels, exception）を理解する |
| 1-2 | EmailNotificationInterface.php | `src/Symfony/Component/Notifier/Notification/EmailNotificationInterface.php` | asEmailMessage()メソッドのシグネチャを確認。EmailRecipientInterfaceとEmailMessageの関係を把握する |
| 1-3 | EmailRecipientInterface.php | `src/Symfony/Component/Notifier/Recipient/EmailRecipientInterface.php` | getEmail()メソッドを提供する受信者インターフェースを確認 |
| 1-4 | EmailMessage.php | `src/Symfony/Component/Notifier/Message/EmailMessage.php` | fromNotification()（行36-65）でNotificationからEmailMessageへの変換ロジックを理解する |

**読解のコツ**: EmailMessage::fromNotification()（行42-59）では、NotificationEmailクラスの存在有無で分岐する点に注意。Twig Bridgeがインストールされている場合はNotificationEmailが使用され、リッチなテンプレートメールが生成される。

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

処理の起点となるNotifierクラスを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Notifier.php | `src/Symfony/Component/Notifier/Notifier.php` | send()メソッド（行40-51）が通知送信のエントリーポイント。getChannels()（行69-104）でチャネル選択ロジックを理解する |

**主要処理フロー**:
1. **行40-51**: send()でRecipientごとにgetChannels()を呼び、各チャネルのnotify()を実行
2. **行69-104**: getChannels()でNotificationのchannels設定またはChannelPolicyからチャネル一覧を取得し、supports()チェック後にyield

#### Step 3: チャネル処理を理解する

EmailChannelの具体的な送信処理を読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ChannelInterface.php | `src/Symfony/Component/Notifier/Channel/ChannelInterface.php` | notify()とsupports()の2メソッドを持つインターフェースを確認 |
| 3-2 | EmailChannel.php | `src/Symfony/Component/Notifier/Channel/EmailChannel.php` | コンストラクタ（行34-45）でTransport/Bus/From/Envelopeの初期化、notify()（行50-86）でメール送信処理の全体を理解する |

**主要処理フロー**:
- **行34-45**: コンストラクタでTransportまたはBusの少なくとも一方が必要。fromはEnvelopeのsenderからフォールバック
- **行53-55**: EmailNotificationInterface実装時はasEmailMessage()でカスタムメッセージ生成
- **行57**: カスタムメッセージがnullの場合はEmailMessage::fromNotification()でデフォルト生成
- **行59-70**: EmailインスタンスのFrom/Toヘッダが未設定の場合に自動設定
- **行81-85**: Busがnullの場合はTransport直接送信、それ以外はBus経由で非同期送信

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

```
Notifier::send()
    |
    +-- getChannels()
    |       +-- Notification::getChannels()
    |       +-- ChannelPolicy::getChannels() [Notificationにchannels未設定時]
    |
    +-- EmailChannel::supports()
    |       +-- recipient instanceof EmailRecipientInterface
    |
    +-- EmailChannel::notify()
            |
            +-- [EmailNotificationInterface] notification->asEmailMessage()
            |       または
            +-- EmailMessage::fromNotification()
            |       +-- [Twig Bridge存在時] new NotificationEmail()
            |       +-- [Twig Bridge未存在時] new Email()
            |
            +-- Email::from() / Email::to() [ヘッダ自動設定]
            |
            +-- [Bus == null] Transport::send()
            +-- [Bus != null] Bus::dispatch(SendEmailMessage)
```

### データフロー図

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

Notification                    Notifier::send()
  - subject          -------->    |
  - content                       v
  - importance                  EmailChannel::notify()
  - channels                      |
  - exception                     v
                               EmailMessage生成
EmailRecipientInterface           |
  - email            -------->    v                    -------->  メール送信
                               From/Toヘッダ設定                   (Transport or Bus)
EmailChannel設定                  |
  - transport        -------->    v
  - bus                        送信実行
  - from
  - envelope
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Notification.php | `src/Symfony/Component/Notifier/Notification/Notification.php` | ソース | 通知の基本クラス。subject, content, importance等を保持 |
| EmailNotificationInterface.php | `src/Symfony/Component/Notifier/Notification/EmailNotificationInterface.php` | ソース | Email通知のインターフェース。asEmailMessage()を定義 |
| EmailChannel.php | `src/Symfony/Component/Notifier/Channel/EmailChannel.php` | ソース | メール送信チャネル。notify()でメール送信を実行 |
| ChannelInterface.php | `src/Symfony/Component/Notifier/Channel/ChannelInterface.php` | ソース | チャネルの共通インターフェース |
| EmailMessage.php | `src/Symfony/Component/Notifier/Message/EmailMessage.php` | ソース | メールメッセージのラッパークラス |
| MessageInterface.php | `src/Symfony/Component/Notifier/Message/MessageInterface.php` | ソース | メッセージの共通インターフェース |
| EmailRecipientInterface.php | `src/Symfony/Component/Notifier/Recipient/EmailRecipientInterface.php` | ソース | メール受信者インターフェース |
| RecipientInterface.php | `src/Symfony/Component/Notifier/Recipient/RecipientInterface.php` | ソース | 受信者の基底インターフェース |
| Notifier.php | `src/Symfony/Component/Notifier/Notifier.php` | ソース | 通知送信のエントリーポイント |
| TransportInterface.php | `src/Symfony/Component/Notifier/Transport/TransportInterface.php` | ソース | トランスポートの共通インターフェース（Mailer側） |
