# 機能設計書 40-Mailer

## 概要

本ドキュメントは、Symfony Mailerコンポーネントの機能設計を記述する。Mailerコンポーネントは、SMTP、Sendmail、各種サードパーティAPI経由でのメール送信機能を提供する。

### 本機能の処理概要

MailerコンポーネントはTransport抽象化により、様々なメール送信プロバイダ（SMTP、Amazon SES、Mailgun、Postmark、SendGrid等）への統一的なメール送信インターフェースを提供する。Messengerコンポーネントとの統合により、メール送信の非同期化にも対応する。

**業務上の目的・背景**：Webアプリケーションにおいてメール送信は最も基本的な機能の一つである。ユーザー登録確認、パスワードリセット、注文確認、通知メール等、多様なメール送信要件がある。Mailerコンポーネントは、DSN（Data Source Name）ベースのトランスポート設定により、送信プロバイダの切り替えを容易にし、開発環境ではNullTransport/MailerAssert、本番環境では実際のSMTPやAPIプロバイダといった環境に応じた柔軟な設定を可能にする。

**機能の利用シーン**：ユーザー登録メール、パスワードリセットメール、注文確認メール、通知メール、ニュースレター配信、アラートメール等、あらゆるメール送信場面で使用される。Notifierコンポーネントと連携してメール通知チャネルとしても機能する。

**主要な処理内容**：
1. Mailer::send()によるメール送信（同期・非同期）
2. DSNベースのトランスポートファクトリ（Transport::fromDsn）
3. SMTP（EsmtpTransport）による直接送信
4. API経由送信（各Bridge: Amazon SES, Mailgun, Postmark, SendGrid等）
5. Messengerバスとの統合による非同期送信（SendEmailMessage）
6. MessageEventによる送信前フック（リスナーによるメール内容の変更・送信拒否）
7. FailoverTransport / RoundRobinTransport による耐障害性・負荷分散
8. Envelope管理（送信者・受信者のオーバーライド）
9. SentMessage情報の返却（メッセージID等）
10. DataCollectorによるWebProfilerでのメール送信情報の可視化

**関連システム・外部連携**：Messengerコンポーネント（非同期送信）、Mimeコンポーネント（メールメッセージの構造化）、Notifierコンポーネント（メール通知チャネル）、Twig Bridge（メールテンプレートレンダリング）、EventDispatcherコンポーネント（送信イベント）と連携する。外部サービスとしてAmazon SES、Mailgun、Postmark、SendGrid、Gmail、Brevo等の多数のメール送信サービスに対応する。

**権限による制御**：メール送信自体には権限制御はない。送信トランスポートの接続情報の保護が重要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 30 | メーラーパネル | 主機能 | Mailerコンポーネントによる送信メール（宛先、件名、本文等）の表示 |

## 機能種別

メール送信処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| message | RawMessage | Yes | 送信するメールメッセージ | RawMessageインスタンスであること |
| envelope | Envelope\|null | No | 送信エンベロープ（送信者・受信者のオーバーライド） | Envelopeインスタンスであること |

### 入力データソース

- アプリケーションコードから構築されたEmailオブジェクト
- Twig Bridgeのテンプレートから生成されたTemplatedEmail
- Notifierコンポーネントからのメール通知

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| void | void | send()メソッドの戻り値はvoid |
| SentMessage | SentMessage | トランスポートレベルでは送信結果情報を返す |

### 出力先

- メール送信トランスポート（SMTP/API）
- Messengerバス（非同期送信時）

## 処理フロー

### 処理シーケンス

```
■ 同期送信（bus未設定）
1. Mailer::send() の呼び出し
   └─ TransportInterface::send() で直接送信

■ 非同期送信（bus設定済み）
1. Mailer::send() の呼び出し
   ├─ EventDispatcher::dispatch(MessageEvent) - queued=trueで送信前イベント
   │   ├─ メッセージのクローン作成
   │   ├─ リスナーによる修正（クローンに対して）
   │   └─ isRejected() のチェック
   ├─ MessageBus::dispatch(SendEmailMessage) - メッセージバスに送信
   └─ HandlerFailedException のハンドリング
       └─ TransportExceptionInterface のアンラップ

■ メッセージ消費（Messenger Worker経由）
1. SendEmailMessageHandler による処理
   └─ TransportInterface::send() で実際の送信実行
       ├─ AbstractTransport::send() でMessageEvent(queued=false)ディスパッチ
       └─ SMTPまたはAPI経由で実際に送信
```

### フローチャート

```mermaid
flowchart TD
    A[Mailer::send] --> B{bus設定あり?}
    B -->|No| C[Transport::send 直接送信]
    B -->|Yes| D[メッセージクローン作成]
    D --> E[MessageEvent dispatch queued=true]
    E --> F{rejected?}
    F -->|Yes| G[送信中止]
    F -->|No| H[MessageBus::dispatch SendEmailMessage]
    H --> I{HandlerFailedException?}
    I -->|Yes| J{TransportException?}
    J -->|Yes| K[TransportException再スロー]
    J -->|No| L[HandlerFailedException再スロー]
    I -->|No| M[完了]
    C --> N[SentMessage返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-40-01 | 同期/非同期の切り替え | MessageBusInterfaceがnullの場合は同期送信、設定時は非同期送信 | 常時 |
| BR-40-02 | メッセージクローン | 非同期送信時、元のメッセージとクローンを分離して、リスナーの影響をオリジナルに及ぼさない | bus設定時 |
| BR-40-03 | Queued MessageEvent | 非同期送信前のMessageEventはqueued=trueで、リスナーが非同期送信時の動作を判断できる | bus設定時 |
| BR-40-04 | メッセージ拒否 | MessageEventリスナーがisRejected()をtrueに設定すると、メール送信が中止される | リスナーでの拒否時 |
| BR-40-05 | Stampの伝搬 | MessageEventで追加されたStampがMessengerのdispatchに渡される | bus設定時 |
| BR-40-06 | TransportException抽出 | HandlerFailedExceptionからTransportExceptionInterfaceをアンラップして再スローする | 非同期送信のエラー時 |
| BR-40-07 | DSNベーストランスポート | MAILER_DSN環境変数でトランスポートを設定。スキームでプロバイダを選択する | Transport::fromDsn使用時 |

### 計算ロジック

特になし。

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

本機能自体はデータベース操作を行わない。ただしDoctrineトランスポートでMessenger非同期送信を行う場合は間接的にDB操作が発生する。

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | 直接のデータベース操作なし |

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | TransportExceptionInterface | SMTP接続失敗、APIエラー等 | トランスポート設定を確認する |
| - | HandlerFailedException | Messenger経由でのハンドラー実行失敗 | ハンドラーの設定を確認する |
| - | InvalidArgumentException | 不正なDSN形式 | DSN文字列を修正する |
| - | UnsupportedSchemeException | 未対応のトランスポートスキーム | 対応するBridgeパッケージをインストールする |

### リトライ仕様

- Messenger経由の非同期送信の場合、Messengerのリトライ戦略が適用される
- 同期送信の場合は呼び出し元でリトライを実装する

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

トランザクション管理は行わない。

## パフォーマンス要件

- 非同期送信によりHTTPリクエスト処理中のメール送信遅延を回避
- FailoverTransportにより、プライマリトランスポート障害時にフォールバック
- RoundRobinTransportにより、複数トランスポートへの負荷分散

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

- MAILER_DSN（トランスポートの接続情報）は環境変数で管理し、ソースコードにハードコードしない
- メール本文にユーザー入力を含める場合はXSSやインジェクション対策が必要
- SPF/DKIM/DMARC設定により、なりすまし防止を行う

## 備考

- Mailerコンポーネントは多数のBridgeパッケージ（Amazon SES, Mailgun, Postmark, SendGrid, Gmail等）に対応している
- MailerAssertトレイトを使用して、テスト中のメール送信を検証できる
- DataCollectorが用意されており、WebProfilerのメーラーパネルで送信メールの詳細を確認できる

---

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

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

### 推奨読解順序

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

メール送信に関するデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Envelope.php | `src/Symfony/Component/Mailer/Envelope.php` | メール送信エンベロープ。sender, recipientsの管理 |
| 1-2 | SentMessage.php | `src/Symfony/Component/Mailer/SentMessage.php` | 送信結果情報 |

**読解のコツ**: MailerのEnvelopeはMessengerのEnvelopeとは別物。MailerのEnvelopeはメール送信の送信者・受信者を管理する。

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

Mailerクラスの送信処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | MailerInterface.php | `src/Symfony/Component/Mailer/MailerInterface.php` | メーラーの公開インターフェース |
| 2-2 | Mailer.php | `src/Symfony/Component/Mailer/Mailer.php` | メーラー実装。同期/非同期の分岐処理 |

**主要処理フロー**:
1. **28-33行目**: コンストラクタ。transport, bus(optional), dispatcher(optional)の注入
2. **35-41行目**: bus未設定時の同期送信パス（transport::send直接呼び出し）
3. **43-59行目**: 非同期送信のイベント処理（メッセージクローン、MessageEvent、Stamp取得、rejected判定）
4. **61-70行目**: MessageBus::dispatch(SendEmailMessage)とHandlerFailedExceptionの処理

#### Step 3: トランスポートを理解する

DSNベースのトランスポートファクトリとトランスポート実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Transport.php | `src/Symfony/Component/Mailer/Transport.php` | DSNパーサーとトランスポートファクトリ |
| 3-2 | TransportInterface.php | `src/Symfony/Component/Mailer/Transport/TransportInterface.php` | トランスポートインターフェース |

**主要処理フロー**:
- **55-75行目** (Transport.php): FACTORY_CLASSES - サポートされるトランスポートファクトリのリスト
- **77-80行目** (Transport.php): `fromDsn()` - DSN文字列からトランスポートを生成するスタティックメソッド

#### Step 4: イベントシステムを理解する

送信イベントの仕組みを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | MessageEvent.php | `src/Symfony/Component/Mailer/Event/MessageEvent.php` | メール送信イベント |
| 4-2 | EventListener/ | `src/Symfony/Component/Mailer/EventListener/` | イベントリスナー群 |

#### Step 5: Messenger連携を理解する

非同期送信のためのMessenger統合を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | SendEmailMessage.php | `src/Symfony/Component/Mailer/Messenger/SendEmailMessage.php` | Messengerメッセージクラス |
| 5-2 | MessageHandler.php | `src/Symfony/Component/Mailer/Messenger/MessageHandler.php` | Messengerメッセージハンドラー |

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

```
Mailer::send()
    |
    +-- [bus == null] TransportInterface::send() 直接送信
    |
    +-- [bus != null] 非同期送信パス
            |
            +-- EventDispatcher::dispatch(MessageEvent, queued=true)
            |
            +-- MessageBus::dispatch(SendEmailMessage)
                    |
                    +-- [Worker消費時]
                    +-- SendEmailMessageHandler::__invoke()
                            |
                            +-- TransportInterface::send()
                                    |
                                    +-- AbstractTransport::send()
                                    |       +-- EventDispatcher::dispatch(MessageEvent, queued=false)
                                    |
                                    +-- EsmtpTransport [SMTP送信]
                                    +-- HttpTransport [API送信]

Transport::fromDsn()
    |
    +-- TransportFactoryInterface::create()
            +-- EsmtpTransportFactory
            +-- NullTransportFactory
            +-- SendmailTransportFactory
            +-- NativeTransportFactory
            +-- [各Bridge] SesTransportFactory, MailgunTransportFactory, ...
```

### データフロー図

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

Email/RawMessage ──────> Mailer::send()
                              |
Envelope (optional) ────> ┌────┴────┐
                          |         |
                      同期送信    非同期送信
                          |         |
                     Transport   MessageBus
                     ::send()    ::dispatch()
                          |         |
                          |    SendEmailMessage
                          |         |
                          |    Worker消費
                          |         |
                          |    Transport::send()
                          |         |
                          v         v
                     SMTP/API ──────> メール配信 ──> 受信者
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Mailer.php | `src/Symfony/Component/Mailer/Mailer.php` | ソース | メーラー実装 |
| MailerInterface.php | `src/Symfony/Component/Mailer/MailerInterface.php` | ソース | メーラーインターフェース |
| Transport.php | `src/Symfony/Component/Mailer/Transport.php` | ソース | DSNトランスポートファクトリ |
| Envelope.php | `src/Symfony/Component/Mailer/Envelope.php` | ソース | 送信エンベロープ |
| SentMessage.php | `src/Symfony/Component/Mailer/SentMessage.php` | ソース | 送信結果情報 |
| Transport/ | `src/Symfony/Component/Mailer/Transport/` | ソース | トランスポート実装群 |
| Event/ | `src/Symfony/Component/Mailer/Event/` | ソース | イベント群 |
| EventListener/ | `src/Symfony/Component/Mailer/EventListener/` | ソース | イベントリスナー群 |
| Messenger/ | `src/Symfony/Component/Mailer/Messenger/` | ソース | Messenger統合 |
| DataCollector/ | `src/Symfony/Component/Mailer/DataCollector/` | ソース | WebProfiler用データコレクター |
| Header/ | `src/Symfony/Component/Mailer/Header/` | ソース | カスタムヘッダー |
| Command/ | `src/Symfony/Component/Mailer/Command/` | ソース | CLIコマンド |
