---
title: "リリース データ設計書"
---

最終更新: 2026-07-21

## 1. 概要

Neon（Vercel Postgres）`release` テーブルに格納される `Release`（リリース）エンティティのデータ設計。
`Release` は他の全機能（トラックリスト・プレイリスト・ユニット・タイムライン・曲対比等）から参照される最も基盤的なエンティティの一つだが、専用のデータ設計書がこれまで存在しなかった（#1379）。

---

## 2. 背景・目的

| 目的 | 詳細 |
| --- | --- |
| `Release` 本体のフィールド定義の一元化 | `types/release.ts` の `ReleaseSchema` と `prisma/schema.prisma` の `Release` モデルの対応関係をまとめる |
| Blob → Neon 移行の経緯の記録 | v4.1.0（#853）で `releases.json`（Vercel Blob）から Neon に移行した際の差分を明示する |
| `Member` との紐付けロジックの記録 | `MemberReleaseLink`（実行時算出、永続ファイルなし）の算出アルゴリズムを記録する |

---

## 3. 型定義

### `Release` インターフェース（`types/release.ts`）

```typescript
export const ReleaseSchema = z.object({
  id: z.string().min(1),
  title: z.string(),
  releaseDate: z.string(),
  format: z.string().min(1),
  source: z.string().min(1),
  sourceUrl: z.string(),
  links: z.array(ExternalLinkSchema).optional(),
  tracks: z.array(TrackSchema).optional(),
});
export type Release = Omit<z.infer<typeof ReleaseSchema>, 'format'> & { format: ReleaseFormat };
```

| フィールド | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| `id` | `string` | ✅ | リリースの識別子。MusicBrainz release-group MBID または手動登録リリースの一意な ID |
| `title` | `string` | ✅ | リリースタイトル |
| `releaseDate` | `string` | ✅ | 発売日。`YYYY-MM-DD`・`YYYY-MM`・`YYYY` のいずれかの精度を許容する（`MemberReleaseLink` 算出時の精度判定は §5 参照） |
| `format` | `ReleaseFormat` | ✅ | リリース形態。`single` / `album` / `ep` / `live` / `soundtrack` / `compilation` / `other` の列挙値。バリデーターは現行データとの互換性のため非空文字列のみを検証し、型レベルで `ReleaseFormat` に限定する |
| `source` | `string` | ✅ | データ取得元（`musicbrainz` または手動登録の識別子） |
| `sourceUrl` | `string` | ✅ | 取得元 URL。手動登録リリースで URL 不明な場合は空文字を許容する |
| `links` | `ExternalLink[]` | — | リリース単位の外部リンク（型定義上は存在するが、§4-2 の通り現行の DB 読み込み経路では未設定） |
| `tracks` | `Track[]` | — | トラックリスト。詳細 → [トラックリスト データ設計書](/design/discography/tracklist-data-design) |

---

## 4. Blob → Neon 移行（v4.1.0、#853）

### 4-1. Prisma モデル（`prisma/schema.prisma`）

```prisma
model Release {
  releaseId   String @id @db.VarChar(255)
  title       String @db.VarChar(255)
  releaseDate String @db.VarChar(20)
  format      String @db.VarChar(50)
  source      String @db.VarChar(255)
  sourceUrl   String @db.Text

  tracks Track[]

  @@map("releases")
}
```

- `Release.id`（Zod）と `Release.releaseId`（Prisma）は同一値。`lib/releases.ts` の `mapRelease` が変換時にリネームする
- `links` フィールドに対応する DB カラムは存在しない（§4-2）
- 読み取りは `lib/releases.ts` の `fetchReleasesFromDB` / `getReleases`（`unstable_cache`）経由で行う。旧 `getReleasesFromBlob`（`lib/blob.ts`）は移行前の実装として残るが、アプリの読み取り経路としては使われていない

### 4-2. `links` フィールドが未使用である理由

`ReleaseSchema.links` は型定義上は存在するが、`mapRelease`（`lib/releases.ts`）はこのフィールドを設定しない。リリース単位の YouTube MV URL 自動取得は「Neon に保存先がなくクォータを空振りするため」廃止済みである（#1123、詳細 → [バッチ設計書](/design/common/batch-design) W2「ディスコグラフィー同期バッチ」手順8）。リリース単位のリンクは廃止され、リンクは全てトラック単位の `Track.links[]` に一元化されている。

### 4-3. `sync-discography` バッチによる書き込み

MusicBrainz からの取得・手動登録リリースのマージ・Neon への保存フローの詳細は → [バッチ設計書](/design/common/batch-design) W2「ディスコグラフィー同期バッチ」を参照すること。

---

## 5. `Member` との紐付け（`MemberReleaseLink`）

`Release` と `Member` の関連は永続ファイルを持たず、`lib/member-release-linker.ts` により実行時に算出される。

### `MemberReleaseLink` インターフェース（`types/member-release-link.ts`）

```typescript
export interface MemberReleaseLink {
  memberId: string;
  releaseId: string;
  linkType: LinkType;
  confidence: Confidence;
  reason: string;
}
```

| フィールド | 型 | 説明 |
| --- | --- | --- |
| `memberId` | `string` | `Member.id` |
| `releaseId` | `string` | `Release.id` |
| `linkType` | `'primary_member' \| 'group_member' \| 'support_member' \| 'unknown'` | 紐付け種別。自動判定ロジックでは現状 `group_member`（在籍期間内一致）と `unknown`（override 由来でリンク種別未指定時）のみ使用 |
| `confidence` | `'high' \| 'medium' \| 'low'` | 紐付けの確度 |
| `reason` | `string` | 紐付け・除外理由（人間可読な説明文） |

### 算出アルゴリズム（`linkMemberToReleases`）

1. **実質基準日の算出**: `profile.joinDate` の翌月1日以降・`profile.gradDate` 以前に発売された最初のシングル（`format === 'single'` かつ完全日付）の発売日を「実質基準日」とする
   - 該当するシングルが存在しない場合、そのメンバーは（override 指定分を除き）全リリースが対象外となる
2. **在籍期間判定**: 各リリースについて、発売日が実質基準日以降・卒業日以前であれば紐付け対象とする
   - 発売日の精度（`YYYY-MM-DD`=full / `YYYY-MM`=month / `YYYY`=year）に応じて比較粒度を揃える
   - `precision === 'full'` の完全一致判定は `confidence: 'high'`、部分日付での推定判定は `confidence: 'medium'`
   - 発売日不明（`precision === 'none'`）のリリースは判定不能として除外
3. **override による補正**: `MemberReleaseLinkOverride`（`{ memberId, releaseId, action: 'include' | 'exclude', linkType?, reason }`）が指定されたペアは、上記アルゴリズムに優先して強制的に含める／除外する。ただし本書執筆時点では override を永続化するデータファイルは存在せず、呼び出し元が空配列を渡している

### 呼び出し箇所

`app/members/[id]/page.tsx`（メンバー詳細の関連リリース表示）・`app/releases/[id]/page.tsx`（リリース詳細の参加メンバー表示）・`lib/timeline-utils.ts` の `buildReleaseMemberLookup`（タイムライン画面・`/api/events` のイベント↔メンバー紐付け）から呼び出される。

---

## 6. ER 図

→ [ER 図](/design/common/er-diagram) を参照すること。`Release` はコア音楽データ・タイムライン統合イベントの各セクションに登場する。

---

## 7. 関連設計書

| 設計書 | 本書との関係 |
| --- | --- |
| [トラックリスト データ設計書](/design/discography/tracklist-data-design) | `Track`・`ExternalLink` 型定義、`releases.json`（移行前）のスキーマ変更経緯 |
| [バッチ設計書](/design/common/batch-design) | `sync-discography` バッチの処理フロー全体（MusicBrainz 取得・ユニット照合・YouTube リンク付与） |
| [ユニット一覧機能 データ設計書](/design/unit/unit-data-design) | `Track.unitId`・`Unit.releaseIds` によるユニットとの紐付け |
| [メンバー データ設計書](/design/common/member-data-design) | `profile.joinDate`/`profile.gradDate` の定義 |

---

## 改訂履歴

| 版 | 更新日 | 変更内容 |
| --- | --- | --- |
| 1.0 | 2026-07-21 | 初版作成（#1379） |
