# 機能設計書 20-並列ルート（Parallel Routes）

## 概要

本ドキュメントは、Next.js App Routerにおける並列ルート（Parallel Routes）の設計を記述する。@スロットによる同一レイアウト内での複数ページの同時レンダリングを実現する機能である。

### 本機能の処理概要

並列ルートは、同一のレイアウト内で複数の独立したルートセグメント（スロット）を同時にレンダリングする機能である。

**業務上の目的・背景**：複雑なWebアプリケーションでは、ダッシュボード画面に複数の独立したコンテンツ領域（アナリティクス、チーム情報、通知等）を同時に表示したり、モーダルとメインコンテンツを同時に管理する必要がある。並列ルートにより、各コンテンツ領域を独立したルートとして定義し、個別にローディング状態やエラー状態を管理できる。

**機能の利用シーン**：ダッシュボードの複数パネル表示、モーダル表示とバックグラウンドコンテンツの同時管理、条件付きルートの表示切り替え（認証状態に応じたUI切り替え等）で利用される。

**主要な処理内容**：
1. `@folder` 規約によるスロット（並列ルート）の定義
2. LoaderTree内のparallelRoutesオブジェクトとしてのスロット管理
3. コンポーネントツリー構築時のスロット別再帰処理
4. デフォルトページ（default.tsx）によるスロット未マッチ時のフォールバック
5. クライアントサイドでのparallelRouterKeyによるスロット別レンダリング
6. ソフトナビゲーション時の非アクティブスロットの状態保持

**関連システム・外部連携**：App Routerのルーティングシステム、LoaderTree構築。

**権限による制御**：スロット別に認証状態に応じた表示切り替えが可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 11 | パラレルルートデフォルト (Parallel Route Default) | 主機能 | パラレルルートで明示的なdefault.tsxが未定義の場合のフォールバック処理（notFound呼び出し） |

## 機能種別

ルーティング / UI構造定義

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| @folder名 | ディレクトリ規約 | Yes | @で始まるディレクトリ名がスロット名となる | @で始まるディレクトリ |
| parallelRouterKey | string | Yes | スロットを識別するキー | "children"はデフォルトの子ルート |

### 入力データソース

- ファイルシステム（`app/**/@slotName/` ディレクトリ構造）
- LoaderTreeのparallelRoutesオブジェクト
- FlightRouterState（クライアントサイドのスロット別ステート）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| スロット別CacheNodeSeedData | Record<string, CacheNodeSeedData> | 各スロットのキャッシュノードデータ |
| スロット別ReactNode | ReactNode | 各スロットのレンダリング結果 |

### 出力先

- HTMLストリーム内のスロット別コンテンツ
- クライアントサイドDOMの各スロット領域

## 処理フロー

### 処理シーケンス

```
1. LoaderTree解析
   └─ parseLoaderTreeでparallelRoutesオブジェクトを取得
2. スロット列挙
   └─ Object.keys(parallelRoutes)で定義済みスロットを取得
3. スロット別コンポーネントツリー構築
   └─ 各parallelRouteKeyに対してcreateComponentTreeInternalを再帰呼び出し
4. デフォルトページ処理
   └─ スロットに対応するページが存在しない場合、default.tsx/ビルトインデフォルトを使用
5. レイアウトでの統合
   └─ レイアウトコンポーネントがスロット別propsを受け取り統合表示
6. クライアントサイドルーティング
   └─ OuterLayoutRouterがparallelRouterKeyに基づいてスロット別レンダリング
```

### フローチャート

```mermaid
flowchart TD
    A[LoaderTree解析] --> B[parallelRoutes取得]
    B --> C{複数スロット存在?}
    C -->|No| D[childrenのみ処理]
    C -->|Yes| E[各スロットを列挙]
    E --> F[スロット別にcreateComponentTreeInternal]
    F --> G{スロットにページ存在?}
    G -->|Yes| H[通常のコンポーネントツリー構築]
    G -->|No| I{default.tsx存在?}
    I -->|Yes| J[default.tsxをフォールバック表示]
    I -->|No| K[ビルトインデフォルト: notFound()]
    H --> L[レイアウトで統合]
    J --> L
    K --> L
    L --> M[HTML/RSCペイロード出力]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | @規約 | @で始まるディレクトリが並列ルートスロットとして認識される | ファイルシステム規約 |
| BR-02 | childrenスロット | 暗黙の"children"スロットが常に存在し、@なしのpage.tsxに対応する | 常時 |
| BR-03 | デフォルトフォールバック | スロットに一致するページがない場合、default.tsxが使用される | ルート不一致時 |
| BR-04 | ビルトインデフォルト | default.tsxが存在しない場合、Next.jsビルトインのParallelRouteDefaultが使用される（notFound()呼び出し） | default.tsx未定義時 |
| BR-05 | 独立ローディング | 各スロットは独立したloading.tsx/error.tsxを持つことができる | スロット別定義時 |
| BR-06 | ソフトナビゲーション保持 | ソフトナビゲーション時、非アクティブなスロットの状態が保持される | ナビゲーション時 |

### 計算ロジック

該当なし。

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | Not Found | スロットに対応するページが存在せずdefault.tsxもない | ビルトインParallelRouteDefault(notFound())を使用 |
| - | 不整合エラー | スロットのルートステートが不整合 | ハードナビゲーションにフォールバック |

### リトライ仕様

該当なし。

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

該当なし。

## パフォーマンス要件

- 各スロットは独立してストリーミングされるため、個別のローディング状態を持つことが可能
- 非アクティブなスロットはReact Activityでオフスクリーン管理される

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

- スロット別に認証状態チェックを実装可能
- 認証済みユーザーと未認証ユーザーで異なるスロットコンテンツを表示するパターンが利用可能

## 備考

- "children"は特殊なparallelRouterKeyで、@フォルダなしのデフォルトの子ルートに対応
- 並列ルートは1つのレイアウト内でObject.keys(parallelRoutes).length > 1の場合に検出される（869-870行目）
- PARALLEL_ROUTE_DEFAULT_PATHはビルトインのデフォルトコンポーネントのパス

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | app-dir-module.ts | `packages/next/src/server/lib/app-dir-module.ts` | LoaderTree内のparallelRoutesの構造を理解。LoaderTreeは `[segment, parallelRoutes, modules]` |
| 1-2 | default.tsx | `packages/next/src/client/components/builtin/default.tsx` | ビルトインのParallelRouteDefaultの実装（notFound()呼び出し）、PARALLEL_ROUTE_DEFAULT_PATH定数 |

**読解のコツ**: parallelRoutesは `Record<string, LoaderTree>` 型で、キーがスロット名（"children"、"@team"等）、値が子ツリー。"children"はデフォルトで常に存在するスロット。

#### Step 2: サーバーサイドの並列ルート処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | create-component-tree.tsx | `packages/next/src/server/app-render/create-component-tree.tsx` | parallelRoutesの処理（458行目以降、869-870行目） |

**主要処理フロー**:
1. **136行目**: `const { page, conventionPath, segment, modules, parallelRoutes } = parseLoaderTree(tree)` で並列ルート情報を取得
2. **458行目以降**: `Object.keys(parallelRoutes).map(...)` で各スロットを列挙し再帰処理
3. **463行目**: `const parallelRoute = parallelRoutes[parallelRouteKey]` で個別スロットのLoaderTreeを取得
4. **525行目**: `parsedTree.conventionPath?.endsWith(PARALLEL_ROUTE_DEFAULT_PATH)` でデフォルトページ判定
5. **869-870行目**: `'children' in parallelRoutes && Object.keys(parallelRoutes).length > 1` で並列ルート存在判定

#### Step 3: ビルトインデフォルトページ

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | default.tsx | `packages/next/src/client/components/builtin/default.tsx` | ParallelRouteDefault関数（6-8行目）でnotFound()呼び出し |

**主要処理フロー**:
- **3行目**: PARALLEL_ROUTE_DEFAULT_PATHの定義
- **6-8行目**: ParallelRouteDefault関数がnotFound()を呼び出してデフォルト動作

#### Step 4: クライアントサイドの並列ルートレンダリング

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | layout-router.tsx | `packages/next/src/client/components/layout-router.tsx` | OuterLayoutRouter（492行目）のparallelRouterKey prop |

**主要処理フロー**:
- **492行目**: OuterLayoutRouterがparallelRouterKeyを受け取り、該当スロットのキャッシュノードを参照
- **402-445行目**: LoadingBoundaryProviderで各スロットにローディングデータを提供

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

```
createComponentTreeInternal (create-component-tree.tsx)
    |
    +-- parseLoaderTree
    |      └─ parallelRoutes オブジェクト取得
    |
    +-- Object.keys(parallelRoutes).map(parallelRouteKey => ...)
           |
           +-- [スロット "children"]
           |      └─ createComponentTreeInternal (再帰)
           |
           +-- [スロット "@team"]
           |      └─ createComponentTreeInternal (再帰)
           |             └─ ページ存在? → 通常処理
           |             └─ default.tsx? → デフォルトフォールバック
           |             └─ 未定義? → ParallelRouteDefault (notFound)
           |
           +-- [スロット "@analytics"]
                  └─ createComponentTreeInternal (再帰)

クライアントサイド:
Layout Component
    |
    +-- OuterLayoutRouter (parallelRouterKey="children")
    |      +-- LoadingBoundary
    |      +-- ErrorBoundary
    |      +-- children page
    |
    +-- OuterLayoutRouter (parallelRouterKey="@team")
    |      +-- LoadingBoundary
    |      +-- ErrorBoundary
    |      +-- @team page
    |
    +-- OuterLayoutRouter (parallelRouterKey="@analytics")
           +-- LoadingBoundary
           +-- ErrorBoundary
           +-- @analytics page
```

### データフロー図

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

app/dashboard/
  ├── layout.tsx ---------> レイアウトコンポーネント
  ├── page.tsx ----------> children スロット ---+
  ├── @team/                                    |
  │   └── page.tsx -----> @team スロット -------+---> 統合HTML/RSCペイロード
  └── @analytics/                               |
      └── page.tsx -----> @analytics スロット --+

FlightRouterState ------> スロット別ステート管理 -----> クライアントDOM更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| create-component-tree.tsx | `packages/next/src/server/app-render/create-component-tree.tsx` | ソース | parallelRoutes再帰処理 |
| default.tsx | `packages/next/src/client/components/builtin/default.tsx` | ソース | ビルトインデフォルトページ |
| layout-router.tsx | `packages/next/src/client/components/layout-router.tsx` | ソース | parallelRouterKeyベースのルーティング |
| parse-loader-tree.ts | `packages/next/src/shared/lib/router/utils/parse-loader-tree.ts` | ソース | LoaderTree解析（parallelRoutes抽出） |
| app-dir-module.ts | `packages/next/src/server/lib/app-dir-module.ts` | ソース | LoaderTree型定義 |
| create-flight-router-state-from-loader-tree.tsx | `packages/next/src/server/app-render/create-flight-router-state-from-loader-tree.tsx` | ソース | FlightRouterStateのスロット別生成 |
