---
title: "ER 図"
---

最終更新: 2026-07-20

## 概要

MorningStatusApp のデータモデル全体を示す ER 図。
フェーズ・バージョンごとに追加されるエンティティを管理する。

---

## エンティティ一覧

| エンティティ | 対応ファイル | 追加バージョン | 対応 Issue |
| --- | --- | --- | --- |
| メンバー（`Member`） | Vercel Blob（`members.json`） / `data/work/members.json`（作業用一時コピー） | v0.1.0〜 | - |
| リリース（`Release`） | Neon（Vercel Postgres）`release` テーブル | v0.2.0〜 | #244 |
| メンバー・リリース紐付け（`MemberReleaseLink`） | Neon `release` + Blob `members.json` から実行時に算出（永続ファイルなし） | v0.2.0〜 | #244 |
| 収録曲（`Track`） | Neon（Vercel Postgres）`track` テーブル | **v0.5.0** | #338・#339 |
| 外部リンク（`ExternalLink`） | `PlaylistEntry.links[]` 内の値型（独立ファイルなし） | **v0.5.0** | #347 |
| プレイリスト（`Playlist`） | `data/inputs/playlists.json`（Vercel Blob 凍結入力） | **v0.6.0** | #328・#330 |
| プレイリスト楽曲（`PlaylistEntry`） | `data/inputs/playlists.json` 内 `entries[]` | **v0.6.0** | #328・#330 |
| 対比ペア（`ComparisonPair`） | `comparison-index.json` 内 `pairs[]` | **v1.1.0** | #474 |
| 対比曲（`ComparisonTrack`） | `comparison-index.json` 内 `pairs[].tracks[]` | **v1.1.0** | #474 |
| ユニット（`Unit`） | Vercel Blob（`UNITS_BLOB_URL`） | **v1.5.0** | #532 |
| 会場（`Venue`） | Neon（Vercel Postgres） | **v3.0.0** | #528 |
| ツアー（`Tour`） | Neon（Vercel Postgres） | **v3.0.0** | #528 |
| 公演（`Live`） | Neon（Vercel Postgres） | **v3.0.0** | #528 |
| メンバー・公演紐付け（`MemberLive`） | Neon（Vercel Postgres） | **v3.0.0** | #528 |
| 都道府県コード変換テーブル（`PrefectureCode`） | Neon（Vercel Postgres） | **v3.0.0** | #528 |
| 出身地照合用メンバー（`Member`、DB） | Neon（Vercel Postgres）※出身地照合用 | **v3.0.0** | #528 |
| セットリスト（`Setlist`） | Neon（Vercel Postgres）※将来拡張 | — | #528 |
| フェス（`Festival`） | Neon（Vercel Postgres） | **v3.9.0** | #776 |
| リリース（DB、`Release`） | Neon（Vercel Postgres）※ Blob releases.json の移行先 | **v4.1.0** | #853 |
| 収録曲（DB、`Track`） | Neon（Vercel Postgres）※ releases.json 内 tracks[] の移行先 | **v4.1.0** | #853 |
| 収録曲外部リンク（DB、`TrackLink`） | Neon（Vercel Postgres）※ Track.links[] の正規化テーブル | **v4.1.0** | #853 |
| Instagram投稿（`InstagramPost`） | Vercel Blob（`instagram/{memberId}/posts.json`） | **v3.5.0** | #715 |
| Instagram同期状態（`InstagramSyncStatus`） | Vercel Blob（`instagram/sync-status.json`） | **v3.5.0** | #715 |
| 当日新着投稿（`DailyNewPost`） | Vercel Blob（`instagram/daily-new-posts.json`） | **v3.5.0** | #715 |
| TikTok投稿（`TikTokPost`） | Neon（Vercel Postgres）※ v4.2.3までVercel Blob | **v4.2.0** | #805・#888 |
| TikTok同期状態（`TikTokSyncStatus`） | Neon（Vercel Postgres）※ v4.2.3までVercel Blob | **v4.2.0** | #805・#888 |
| ラジオ番組（`RadioShow`） | Neon（Vercel Postgres） | **v4.3.0** | #806 |
| ラジオ放送回（`RadioEpisode`） | Neon（Vercel Postgres） | **v4.3.0** | #806 |
| ラジオ出演メンバー（`RadioMember`） | Neon（Vercel Postgres） | **v4.3.0** | #806 |
| ラジオオンエア楽曲（`RadioOnairSong`） | Neon（Vercel Postgres） | **v4.3.0** | #806 |
| 統合イベント（`Event`） | Neon（Vercel Postgres）※ Releases/Members/Lives/Radio/手動登録イベントの統合派生データ | **v5.0.0** | #925〜#928 |
| YouTube投稿（`YoutubePost`） | Neon（Vercel Postgres） | **v6.5.0** | #1097 |
| メンバー関係性エッジ（`MemberRelationship`） | Neon（Vercel Postgres） | **v8.0.0** | #1209 |
| メンバー関係性マップ座標（`MemberMapLayout`） | Neon（Vercel Postgres） | **v8.2.0** | #1210 |

---

## ER 図（コア音楽データ）

> メンバー・リリース・収録曲・プレイリスト・ユニット・曲対比の関連。`Member` は Vercel Blob（`members.json`）、`Release` / `Track` / `TrackLink` は Neon（Vercel Postgres）。Blob と Neon をまたぐ関連（`Member`-`MemberReleaseLink`-`Release` 等）は ID の値一致で対応し、DB レベルの FK 制約はない。

```mermaid
erDiagram
  Member {
    string id PK
    string name
    string status
    int generation
  }

  Release {
    string releaseId PK
    string title
    string releaseDate
    string format
    string source
    string sourceUrl
  }

  Track {
    string trackId PK "recording UUID (MusicBrainz)"
    string releaseId FK
    string mbTrackId "track UUID (MusicBrainz、optional)"
    int trackNumber
    int discNumber "optional"
    string title
    int length "ms、optional"
    string artist "optional"
    string unitId "optional"
    string artistMbid "artist UUID (MusicBrainz、optional)"
  }

  TrackLink {
    int id PK
    string trackId FK
    string type "youtube|youtube-music|spotify|apple-music|... (ExternalLinkType)"
    string url
  }

  MemberReleaseLink {
    string memberId FK
    string releaseId FK
    string linkType
    string confidence
    string reason
  }

  Playlist {
    string id PK
    string name
    string description "optional"
    string publishedAt
  }

  PlaylistEntry {
    string playlistId FK
    string memberId FK "optional"
    string releaseId FK "optional"
    string trackId FK "optional"
    string songTitle
  }

  Unit {
    string id PK
    string name
    string memberIds "string[]"
    string releaseIds "string[]、optional"
    string activeFrom "YYYY"
    string activeTo "YYYY、optional"
    string parentId "上位ユニットID、optional"
    string mbid "artist UUID (MusicBrainz、optional)"
  }

  ComparisonPair {
    string id PK
    string baseTitle
  }

  ComparisonTrack {
    string trackId FK
    string title
    string youtubeVideoId "optional"
    string releaseId FK "optional"
    string releaseTitle "optional"
    string releaseFormat "single|ep|album|other、optional"
  }

  Release ||--o{ Track : "収録"
  Track ||--o{ TrackLink : "外部リンク"
  Member ||--o{ MemberReleaseLink : "参加"
  Release ||--o{ MemberReleaseLink : "参加メンバー"
  Playlist ||--o{ PlaylistEntry : "エントリ"
  Member ||--o{ PlaylistEntry : "選曲者 (optional)"
  Release }o--o{ PlaylistEntry : "収録リリース (optional)"
  Track ||--o{ PlaylistEntry : "楽曲参照 (optional)"
  Unit }o--o{ Member : "所属メンバー"
  Unit }o--o{ Release : "関連リリース (optional)"
  ComparisonPair ||--o{ ComparisonTrack : "収録曲"
  Track ||--o{ ComparisonTrack : "参照 (optional)"
```

---

## ER 図（ライブ・フェス機能）

> `Venue` / `Tour` / `Live` / `MemberLive` / `Setlist` / `Festival` は Neon（Vercel Postgres）。`MemberLive.memberId` は Blob `members.json` の `id` と値一致で対応し、DB レベルの FK 制約はない。

```mermaid
erDiagram
  Venue {
    string venueId PK
    string name
    string prefecture "都道府県コード"
    float latitude "optional"
    float longitude "optional"
  }

  "Tour" {
    string tourId PK
    string title
    string releaseId "対応リリースID、optional"
  }

  Live {
    string liveId PK
    string tourId FK "optional"
    string venueId FK
    string date
    string time "optional"
    string title "optional"
  }

  MemberLive {
    string memberId "Blob Memberのid参照、FK制約なし"
    string liveId FK
  }

  Setlist {
    string setlistId PK
    string type "tour|festival"
    string tourId FK "optional"
    string festivalId FK "optional"
    int seq
    string trackId "Track.id参照、FK制約なし"
  }

  Festival {
    string festivalId PK
    string name
    string date
    string venueId FK
  }

  PrefectureCode {
    string prefectureCode PK
    string name
    string aliases "string[]"
  }

  Member {
    string memberId PK "出身地照合用（Blob Memberのidと同値をNeon側に複製）"
    string birthplace "optional"
  }

  Venue ||--o{ Live : "開催"
  Venue ||--o{ Festival : "開催"
  "Tour" ||--o{ Live : "公演"
  "Tour" ||--o{ Setlist : "セットリスト"
  Festival ||--o{ Setlist : "セットリスト"
  Live ||--o{ MemberLive : "出演メンバー"
```

> `PrefectureCode`・`Member`（出身地照合用）は「ご当地ライブ」判定でアプリケーション側から参照される（`Venue.prefecture` ↔ `PrefectureCode.prefectureCode`、`MemberLive.memberId` ↔ 本テーブルの `memberId`）。Prisma の `@relation` は定義されておらず、DB レベルの FK 制約はない。
>
> `"Tour"` はダブルクォートで囲んでいる（構文上は不要）。クォートなしの `Tour` はエディタによって Mermaid ER 図のレンダリングに失敗することが確認されているため、クォートを外さないこと。

---

## ER 図（SNS投稿収集：Instagram・TikTok・YouTube）

> `InstagramPost` / `InstagramSyncStatus` / `DailyNewPost` は Vercel Blob、`TikTokPost` / `TikTokSyncStatus` / `YoutubePost` は Neon（Vercel Postgres）。いずれもメンバーとの対応は `memberId` の値一致によるもので、FK 制約はない。

```mermaid
erDiagram
  InstagramPost {
    string postUrl PK
    string memberId "Blob Memberのid参照"
    string imageBlobUrl "optional"
    string thumbnailUrl "optional"
    boolean isLive "optional"
    boolean isReel "optional"
    string caption
    string capturedAt
    string instagramUrl "optional"
  }

  InstagramSyncStatus {
    string lastSyncedAt
    string sessionStatus "valid|expired"
    int capturedMembersCount
    int totalTargetMembersCount
  }

  DailyNewPost {
    string postUrl PK
    string memberId "Blob Memberのid参照"
    string memberName
    string capturedAt
  }

  TikTokPost {
    string postId PK
    string postUrl
    string thumbnailUrl "optional"
    string imageBlobUrl "optional"
    boolean isSlideshow
    string caption
    datetime capturedAt
    string mentionedMemberIds "string[]、Blob Memberのid参照"
  }

  TikTokSyncStatus {
    int id PK "常に1固定（upsert）"
    datetime lastSyncedAt
    string syncStatus
    int capturedMembersCount
    int totalTargetMembersCount
    string retroCursor "optional"
  }

  YoutubePost {
    string videoId PK
    string channelType "official|og"
    string memberId "Blob Memberのid参照、optional"
    string title
    datetime publishedAt
    datetime capturedAt
  }

  DailyNewPost ||--|| InstagramPost : "InstagramPostを拡張（memberName付与）"
```

---

## ER 図（ラジオオンエア収集）

> `RadioShow` / `RadioEpisode` / `RadioMember` / `RadioOnairSong` は全て Neon（Vercel Postgres）。`RadioMember.memberId` は Blob `members.json` の `id` と値一致で対応し、DB レベルの FK 制約はない。

```mermaid
erDiagram
  RadioShow {
    string showId PK
    string name
    string station "optional"
  }

  RadioEpisode {
    string episodeId PK
    string showId FK
    string date
    string title "optional"
  }

  RadioMember {
    string episodeId FK
    string memberId "Blob Memberのid参照、FK制約なし"
    boolean isDayDuty
  }

  RadioOnairSong {
    int onairId PK
    string episodeId FK
    int trackOrder
    string songTitle
    string trackId "Track.id参照、optional"
    string artistName "optional"
  }

  RadioShow ||--o{ RadioEpisode : "放送回"
  RadioEpisode ||--o{ RadioMember : "出演メンバー"
  RadioEpisode ||--o{ RadioOnairSong : "オンエア楽曲"
```

---

## ER 図（タイムライン統合イベント）

> `Event` は Neon（Vercel Postgres）の統合派生データ。`source`/`sourceId` は Release/Live/RadioEpisode/Festival/Member（`source='manual'` の場合は元データなし）への多態的な参照であり、Prisma の `@relation` は定義されていない。

```mermaid
erDiagram
  Event {
    string id PK "UUID"
    string date
    string time "optional、HH:MM相当（秒精度なし）"
    string type "release|live|join|graduate|media|topic|festival"
    string title
    string description "optional"
    string members "string[]、Blob Memberのid参照"
    string tags "string[]"
    string imageUrl "optional"
    string link "optional"
    string source "release|member|live|radio|manual|festival"
    string sourceId "元データのID"
  }

  Release {
    string releaseId PK
  }

  Live {
    string liveId PK
  }

  RadioEpisode {
    string episodeId PK
  }

  Festival {
    string festivalId PK
  }

  Member {
    string id PK
  }

  Release ||--o{ Event : "source=release（sourceId経由、FK制約なし）"
  Live ||--o{ Event : "source=live（sourceId経由、FK制約なし）"
  RadioEpisode ||--o{ Event : "source=radio（sourceId経由、FK制約なし）"
  Festival ||--o{ Event : "source=festival（sourceId経由、FK制約なし）"
  Member ||--o{ Event : "source=member（sourceId経由、FK制約なし）"
```

---

## ER 図（メンバー関係性マップ）

> `MemberRelationship` / `MemberMapLayout` は Neon（Vercel Postgres）。`fromMemberId` / `toMemberId` / `memberId` は Blob `members.json` の `id` と値一致で対応し、DB レベルの FK 制約はない。

```mermaid
erDiagram
  Member {
    string id PK
  }

  MemberRelationship {
    int id PK
    string fromMemberId "Blob Memberのid参照"
    string toMemberId "Blob Memberのid参照"
    string type "same-gen|same-hometown|same-unit|overlapping|leader-succession|mentor-student|live-coappearance|radio-coappearance|event-coappearance"
    int weight
    string label "optional"
  }

  MemberMapLayout {
    string memberId PK "Blob Memberのid参照"
    float x
    float y
  }

  Member ||--o{ MemberRelationship : "起点 (fromMemberId)"
  Member ||--o{ MemberRelationship : "終点 (toMemberId)"
  Member ||--|| MemberMapLayout : "座標 (memberId)"
```

---

## 更新履歴

| 版 | バージョン | 更新日 | 変更内容 | 関連 Issue |
| --- | --- | --- | --- | --- |
| 2.2 | - | 2026-07-20 | `Event` エンティティに `time` フィールドを追加 | #1381 |
| 2.1 | - | 2026-07-19 | v0.5.0/v0.6.0の古いMermaid ER図スナップショットを破棄し、現行実装の全エンティティを反映した機能ドメイン別のER図（コア音楽データ・ライブ/フェス・SNS投稿収集・ラジオオンエア収集・タイムライン統合イベント・メンバー関係性マップ）に置き換え | #1382 |
| 2.0 | - | 2026-07-19 | v3.5.0〜v8.2.0で追加された未反映エンティティ（Instagram・TikTok・ラジオ・年表・YouTube投稿・メンバー関係性マップ）をエンティティ一覧に追加。既存行の物理テーブル名（`venues`等）をPrismaモデル名に是正 | #1374 |
| 1.9 | v4.1.0 | 2026-05-10 | アプリのリリースデータ読み取りを Blob `releases.json` → Neon `getReleases()` に移行 | #855 |
| 1.8 | v4.1.0 | 2026-05-10 | `releases`・`tracks`・`track_links` Neon テーブル追加（Blob releases.json の移行先スキーマ） | #853 |
| 1.7 | v3.9.0 | 2026-05-08 | `festivals` テーブル追加 | #776 |
| 1.6 | v3.0.0 | 2026-04-11 | ライブ機能 Neon テーブル（`venues`・`tours`・`lives`・`member_lives`・`prefecture_codes`・`members`・`setlists`）追加 | #528 |
| 1.5 | v1.5.0 | 2026-03-30 | `Unit` エンティティ追加 | #532 |
| 1.4 | v0.7.6 | 2026-03-23 | `Release.youtubeUrl` を廃止し `Release.links[]`（`ExternalLink[]`）に統合。`PlaylistEntry.links` を廃止しリンクは `Track.links[]` に一元化 | #424・#425 |
| 1.3 | v0.6.0 | 2026-03-21 | `Playlist.description` フィールド追加、`ExternalLink.type` にストリーミングプラットフォームを追加 | #328・#329 |
| 1.2 | v0.6.0 | 2026-03-19 | `Playlist`・`PlaylistEntry` エンティティ追加、`PlaylistEntry.links` に `ExternalLink[]` 追加 | #328・#330・#349・#351 |
| 1.1 | v0.5.0 | 2026-03-19 | `Track` エンティティ追加、`Release` に `tracks[]`・`youtubeUrl`（現在は廃止済）追加、`ExternalLink` 型追加 | #338・#339・#347・#348・#350 |
| 1.0 | v0.2.0 | — | `Release`・`MemberReleaseLink` エンティティ追加、`Member` エンティティ追加 | #244 |
