# データベース設計書

## 概要

本ドキュメントは、Next.js フレームワーク（next.js-canary リポジトリ）におけるデータベース関連の設計を記述するものである。

Next.js はフレームワークそのものであり、アプリケーション固有の永続データベース（RDB や NoSQL）を内部に持たない。ただし、以下の2つの観点でデータベース的なデータ構造が存在する。

1. **内部キャッシュストレージ（Incremental Cache）**: Next.js のビルド・ランタイムで使用されるファイルシステムベース及びインメモリのキャッシュ機構。ISR（Incremental Static Regeneration）やフェッチキャッシュを支えるデータ保存構造として機能する。
2. **サンプルアプリケーション（examples）内のデータベーススキーマ**: Prisma, Knex, MongoDB Mongoose, Turso（SQLite）, Convex などの各種 ORM/DB クライアントを利用したサンプルが `examples/` ディレクトリに含まれている。

本書では、両方の観点からテーブル/データ構造を整理する。

---

## テーブル一覧

### A. Next.js 内部キャッシュデータ構造（IncrementalCache）

| データ構造名 | 対応エンティティ | 説明 |
| --- | --- | --- |
| CachedAppPageValue | App Router ページキャッシュ | App Router で生成されたページの HTML, RSC データ, メタデータを保持 |
| CachedPageValue | Pages Router ページキャッシュ | Pages Router で生成されたページの HTML とページデータを保持 |
| CachedRouteValue | App Route キャッシュ | App Router の Route Handler レスポンスボディを保持 |
| CachedFetchValue | フェッチキャッシュ | `fetch()` のレスポンスデータをキャッシュとして保持 |
| CachedRedirectValue | リダイレクトキャッシュ | リダイレクト先情報を保持 |
| CachedImageValue | 画像キャッシュ | 最適化された画像データとメタ情報を保持 |
| TagManifestEntry | タグマニフェスト | キャッシュ無効化用のタグと有効期限情報を保持 |
| SharedCacheControls | キャッシュ制御 | ルートごとの revalidate/expire 設定をインメモリで保持 |

### B. サンプルアプリケーション（examples）内のデータベーステーブル

| テーブル名 | 対応エンティティ | 説明 | サンプル |
| --- | --- | --- | --- |
| Product | 商品 | ECサイトの商品情報 | examples/with-mysql |
| Category | カテゴリ | 商品カテゴリ | examples/with-mysql |
| User | ユーザー | ブログユーザー | examples/prisma-postgres |
| Post | 投稿 | ブログ投稿記事 | examples/prisma-postgres |
| todos | TODO項目 | TODO管理アプリのタスク | examples/with-knex, examples/next-forms, examples/with-turso |
| items | アイテム | ユーザー所有アイテム | examples/with-nhost-auth-realtime-graphql |
| messages | メッセージ | チャットメッセージ | examples/convex |
| Pet | ペット | ペット情報管理 | examples/with-mongodb-mongoose |

---

## 各テーブル定義

### A. Next.js 内部キャッシュデータ構造

#### A-1. CachedAppPageValue

App Router で生成されたページのキャッシュエントリ。ファイルシステム上では `.html`, `.rsc`, `.meta` ファイルとして保存される。

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| kind | CachedRouteKind.APP_PAGE (string) | NO | キャッシュ種別識別子。常に `'APP_PAGE'` |
| html | string | NO | レンダリング済み HTML コンテンツ |
| rscData | Buffer | YES | React Server Components のペイロードデータ |
| headers | OutgoingHttpHeaders | YES | レスポンスHTTPヘッダー |
| postponed | string | YES | PPR（Partial Prerendering）の延期データ |
| status | number | YES | HTTPステータスコード |
| segmentData | Map<string, Buffer> | YES | セグメントごとのプリフェッチデータ |

#### A-2. CachedPageValue（Pages Router）

Pages Router で生成されたページのキャッシュエントリ。ファイルシステム上では `.html` と `.json` ファイルとして保存される。

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| kind | CachedRouteKind.PAGES (string) | NO | キャッシュ種別識別子。常に `'PAGES'` |
| html | string | NO | レンダリング済み HTML コンテンツ |
| pageData | Object | NO | `getStaticProps` / `getServerSideProps` が返すページデータ |
| headers | OutgoingHttpHeaders | YES | レスポンスHTTPヘッダー |
| status | number | YES | HTTPステータスコード |

#### A-3. CachedRouteValue（App Route Handler）

App Router の Route Handler から返されるレスポンスのキャッシュエントリ。ファイルシステム上では `.body` と `.meta` ファイルとして保存される。

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| kind | CachedRouteKind.APP_ROUTE (string) | NO | キャッシュ種別識別子。常に `'APP_ROUTE'` |
| body | Buffer | NO | レスポンスボディ |
| status | number | NO | HTTPステータスコード |
| headers | OutgoingHttpHeaders | NO | レスポンスHTTPヘッダー |

#### A-4. CachedFetchValue

`fetch()` API のレスポンスキャッシュ。ファイルシステム上では `.next/cache/fetch-cache/` 配下に JSON ファイルとして保存される。

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| kind | CachedRouteKind.FETCH (string) | NO | キャッシュ種別識別子。常に `'FETCH'` |
| data | CachedFetchData | NO | フェッチレスポンスデータ（headers, body, url, status） |
| tags | string[] | YES | キャッシュ無効化用タグ配列（ファイルシステムキャッシュ時のみ） |
| revalidate | number | NO | 再検証間隔（秒） |

**CachedFetchData 内部構造：**

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| headers | Record<string, string> | NO | レスポンスヘッダー |
| body | string | NO | レスポンスボディ文字列 |
| url | string | NO | フェッチ先URL |
| status | number | YES | HTTPステータスコード |

#### A-5. CachedRedirectValue

リダイレクトのキャッシュエントリ。

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| kind | CachedRouteKind.REDIRECT (string) | NO | キャッシュ種別識別子。常に `'REDIRECT'` |
| props | Object | NO | リダイレクト先情報を含むプロパティ |

#### A-6. CachedImageValue

画像最適化のキャッシュエントリ。

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| kind | CachedRouteKind.IMAGE (string) | NO | キャッシュ種別識別子。常に `'IMAGE'` |
| etag | string | NO | 生成されたETag |
| upstreamEtag | string | NO | オリジナル画像のETag |
| buffer | Buffer | NO | 最適化済み画像バイナリ |
| extension | string | NO | 画像ファイル拡張子 |
| revalidate | number | YES | 再検証間隔（秒） |
| isMiss | boolean | YES | キャッシュミスフラグ |
| isStale | boolean | YES | キャッシュ失効フラグ |

#### A-7. TagManifestEntry

キャッシュ無効化に使用されるタグ管理構造。インメモリの `Map<string, TagManifestEntry>` として管理される。

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| (key) | string | NO | タグ名（Mapのキー） |
| stale | number | YES | stale（古くなった）と判定するタイムスタンプ（ミリ秒） |
| expired | number | YES | 有効期限切れと判定するタイムスタンプ（ミリ秒） |

#### A-8. CacheHandlerValue

キャッシュハンドラーが返す値のラッパー構造。LRU キャッシュやファイルシステムキャッシュのエントリ単位として使用される。

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| lastModified | number | NO | 最終更新タイムスタンプ（ミリ秒） |
| age | number | YES | キャッシュエントリの経過時間 |
| cacheState | string | YES | キャッシュ状態の文字列表現 |
| value | IncrementalCacheValue | YES | キャッシュされた値（上記 A-1 から A-6 のいずれかの型） |

---

### B. サンプルアプリケーションのデータベーステーブル

#### B-1. Product（examples/with-mysql）

ECサイトにおける商品情報を管理するテーブル。Prisma ORM + MySQL で定義。

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| id | Int (AUTO_INCREMENT) | NO | 主キー |
| name | String | NO | 商品名 |
| description | String | NO | 商品説明 |
| price | Decimal | NO | 価格 |
| image | String | NO | 商品画像URL |
| categoryId | Int | NO | カテゴリID（外部キー: Category.id） |

- **主キー**: id
- **インデックス**: categoryId
- **リレーション**: Category (多対一)

#### B-2. Category（examples/with-mysql）

商品カテゴリを管理するテーブル。Prisma ORM + MySQL で定義。

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| id | Int (AUTO_INCREMENT) | NO | 主キー |
| name | String | NO | カテゴリ名 |
| description | String | NO | カテゴリ説明 |

- **主キー**: id
- **リレーション**: Product[] (一対多)

#### B-3. User（examples/prisma-postgres）

ブログアプリケーションのユーザー情報を管理するテーブル。Prisma ORM + PostgreSQL で定義。

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| id | Int (AUTO_INCREMENT) | NO | 主キー |
| email | String | NO | メールアドレス（ユニーク制約） |
| name | String | YES | 表示名 |

- **主キー**: id
- **ユニーク制約**: email
- **リレーション**: Post[] (一対多)

#### B-4. Post（examples/prisma-postgres）

ブログ投稿記事を管理するテーブル。Prisma ORM + PostgreSQL で定義。

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| id | Int (AUTO_INCREMENT) | NO | 主キー |
| createdAt | DateTime | NO | 作成日時（デフォルト: now()） |
| updatedAt | DateTime | NO | 更新日時（自動更新） |
| title | String | NO | 記事タイトル |
| content | String | YES | 記事本文 |
| published | Boolean | NO | 公開フラグ（デフォルト: false） |
| authorId | Int | NO | 著者ID（外部キー: User.id） |

- **主キー**: id
- **リレーション**: User (多対一, authorId -> User.id)

#### B-5. todos（examples/with-knex）

TODO管理アプリケーションのタスクテーブル。Knex.js + PostgreSQL で定義。

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| id | Integer (AUTO_INCREMENT) | NO | 主キー |
| text | String | NO | TODOテキスト |
| done | Boolean | NO | 完了フラグ |

- **主キー**: id

#### B-6. todos（examples/next-forms）

Server Actions のフォームサンプル用テーブル。PostgreSQL で直接 SQL を使用。

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| id | SERIAL | NO | 主キー |
| text | TEXT | NO | TODOテキスト |

- **主キー**: id

#### B-7. todos（examples/with-turso）

Turso（libSQL/SQLite）を使用したTODO管理テーブル。

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| id | INTEGER (AUTO_INCREMENT) | NO | 主キー |
| description | TEXT | NO | TODO説明文 |

- **主キー**: id

#### B-8. items（examples/with-nhost-auth-realtime-graphql）

Nhost 認証・リアルタイム GraphQL サンプルにおけるアイテムテーブル。PostgreSQL で SQL 定義。

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| id | UUID | NO | 主キー（デフォルト: gen_random_uuid()） |
| created_at | Timestamp with time zone | NO | 作成日時（デフォルト: now()） |
| name | TEXT | NO | アイテム名 |
| user_id | UUID | NO | ユーザーID（外部キー: users.id） |

- **主キー**: id (items_pkey)
- **外部キー**: user_id -> public.users(id), ON UPDATE CASCADE ON DELETE CASCADE

#### B-9. messages（examples/convex）

Convex を使用したチャットアプリケーションのメッセージテーブル。

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| author | string (v.string()) | NO | メッセージ著者名 |
| body | string (v.string()) | NO | メッセージ本文 |

- **注意**: Convex はスキーマバリデーションが無効化されている（schemaValidation: false）

#### B-10. Pet（examples/with-mongodb-mongoose）

MongoDB + Mongoose を使用したペット情報管理コレクション。

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| name | String (max 60) | NO | ペット名 |
| owner_name | String (max 60) | NO | 飼い主名 |
| species | String (max 40) | NO | 種類 |
| age | Number | YES | 年齢 |
| poddy_trained | Boolean | YES | トイレトレーニング済みフラグ |
| diet | String[] | YES | 食事制限リスト |
| image_url | String | NO | ペット画像URL |
| likes | String[] | YES | 好きなことリスト |
| dislikes | String[] | YES | 嫌いなことリスト |

- **注意**: MongoDB コレクションのため、`_id` フィールドが自動付与される

---

## 備考

### Next.js 内部キャッシュの保存場所

Next.js の IncrementalCache は以下のディレクトリ構造でファイルシステムにデータを保存する。

```
.next/
├── server/
│   ├── app/          # APP_PAGE, APP_ROUTE キャッシュ
│   │   ├── *.html    # HTMLコンテンツ
│   │   ├── *.rsc     # RSCペイロード
│   │   ├── *.meta    # メタデータ（JSON）
│   │   └── *.body    # Route Handler ボディ
│   └── pages/        # PAGES キャッシュ
│       ├── *.html    # HTMLコンテンツ
│       └── *.json    # ページデータ
└── cache/
    └── fetch-cache/  # FETCHキャッシュ（JSON）
```

### キャッシュ階層

1. **インメモリ LRU キャッシュ**: `maxMemoryCacheSize` で制御。サイズ計算は CachedRouteKind ごとに異なるロジックで推定される。
2. **ファイルシステムキャッシュ**: `.next/` ディレクトリに永続化。デプロイ間で `fetch-cache` は維持可能。
3. **カスタムキャッシュハンドラー**: `CacheHandler` インターフェースを実装することで Redis, DynamoDB 等の外部ストアに差し替え可能。

### サンプルアプリケーションについて

`examples/` ディレクトリ内のデータベーススキーマは Next.js フレームワークの機能ではなく、Next.js と各種データベースを組み合わせて使用する際の参考実装である。本番アプリケーションのデータベース設計はプロジェクトの要件に応じて個別に設計する必要がある。
