データベース設計(Database Design)
本書をスキーマ仕様のリポジトリ上の正 とする(実装は supabase/migrations 等と整合させる)。docs/TRD.md §4 から参照する。
DBMS : PostgreSQL(Supabase 内蔵)。物理名は snake_case ・複数形 を原則とする(下記 ER 図の大文字エンティティ名は概念的対応のみ)。
Supabase ダッシュボード(Table Editor / SQL Editor 等)でのスキーマ変更は原則禁止。 テーブル・列・インデックス・RLS・ポリシーはすべて supabase/migrations/ に SQL として追加し、PR でレビューする。例外・緊急時の扱いは docs/Rule.md 「データベーススキーマ(Supabase)」に従う。
erDiagram
PROFILES {
uuid id PK "auth.users の id と一致"
varchar email "メールアドレス (NOT NULL)"
varchar display_currency "表示希望通貨 (例: JPY)"
boolean onboarding_completed "初回オンボーディング完了フラグ"
timestamp created_at "default: now()"
timestamp updated_at
}
CATEGORIES {
int id PK "自動採番"
varchar name "ジャンル名 (NOT NULL)"
varchar color_code "テーマカラー (NOT NULL)"
varchar icon_name "UI アイコン名"
}
CYCLES {
int id PK "自動採番"
varchar name "monthly, yearly, weekly 等 (NOT NULL)"
}
SERVICES {
uuid id PK
varchar name "サービス名 (NOT NULL)"
int category_id FK "categories 参照"
uuid user_id FK "profiles 参照。NULL=プリセットマスタ"
varchar logo_key "同梱ロゴ識別子 assets/services/{logo_key}.jpeg"
varchar logo_uri "ロゴ画像 URL(外部URL用・任意)"
varchar icon_name "画像がない場合の UI アイコン名"
}
PLANS {
uuid id PK
uuid service_id FK "services 参照"
varchar name "プラン名 (NOT NULL)"
numeric price "価格 (NOT NULL)"
varchar currency "決済通貨"
int cycle_id FK "cycles 参照 → 支払周期"
}
SUBSCRIPTIONS {
uuid id PK
uuid user_id FK "profiles 参照"
uuid plan_id FK "plans 参照"
date start_date "契約の最初の請求日 (NOT NULL)"
date next_billing_date "次回更新日 (NOT NULL)"
numeric custom_price "ユーザー編集料金 numeric(14,4) NULL可 / NULL時は plans.price"
varchar status "active / paused / cancelled"
timestamp cancelled_at "解約日時(cancelled 時必須)"
text memo "解約リンクなど"
timestamp created_at "default: now()"
timestamp updated_at
}
NOTIFICATION_SETTINGS {
uuid id PK
uuid subscription_id FK "subscriptions 参照"
int notify_days_before "何日前に通知するか"
}
USAGE_LOGS {
uuid id PK
uuid user_id FK "profiles 参照"
uuid subscription_id FK "subscriptions 参照"
date used_date "利用日 (NOT NULL)"
timestamp created_at "default: now()"
}
PROFILES ||--o{ SUBSCRIPTIONS : "契約"
PROFILES ||--o{ USAGE_LOGS : "記録"
CATEGORIES ||--o{ SERVICES : "分類"
CYCLES ||--o{ PLANS : "周期"
SERVICES ||--o{ PLANS : "複数プラン"
PLANS ||--o{ SUBSCRIPTIONS : "契約のベース"
SUBSCRIPTIONS ||--o{ NOTIFICATION_SETTINGS : "通知タイミング複数"
SUBSCRIPTIONS ||--o{ USAGE_LOGS : "利用履歴"
Loading
補足 : 旧メモにあった notify_timing は notification_settings へ分離。プリセットサービスのロゴは services.logo_key で assets/services/ の同梱画像と紐づける(logo_uri は外部 URL 用)。ユーザー固有アイコンがマスタに要る場合は別途検討。
プリセットマスタ : categories / cycles / services / plans の初期データは supabase/migrations/ で管理する(seed.sql はローカル開発用ユーザー・サンプル契約のみ)。出典は Notion「主要サブスク一覧」を正とし(services 53 件 / plans 204 件 ※無料プラン6件は 20260609120000 で削除)、内容は docs/preset-services-template.csv に出力(各値は要レビュー)。cycle は monthly / yearly / weekly のみで、3ヶ月・期間保証などはプラン名に期間を併記し近い cycle へ丸める。ロゴは 53 件中 49 件が同梱画像(logo_key)にマッチ、未同梱の 4 件(LINE MUSIC / AppleCare+ / dマガジン / コミックシーモア)は logo_key=NULL、外部 URL があるものは logo_uri にフォールバック。生成は scripts/generate-preset-seed.js 。
項目
方針
主キー
uuid(gen_random_uuid() 等)または int + GENERATED BY DEFAULT AS IDENTITY(マスタ)
日時
timestamptz(created_at / updated_at)
金額
numeric(通貨とセットで解釈)
論理削除
現状なし(削除は物理削除 or アーカイブは要件次第)
物理名
概要
備考
profiles
ユーザー設定(表示通貨など)
旧 Users 想定。id = auth.users.id
categories
ジャンルマスタ
cycles
支払周期マスタ(月額/年額/週額等)
新規
services
サービスマスタ
旧 master_services 想定。改修
plans
サービス別プラン(価格・通貨・周期)
新規 。1 サービス複数プラン
subscriptions
ユーザーの契約台帳
改修 。plan_id 参照でスリム化
notification_settings
サブスクごとの通知タイミング(複数可)
新規 (notify_timing の切り出し)
usage_logs
利用実績(使用チェック)
line_links
ユーザーと LINE userId の紐付け
新規 (20260606130000)
line_link_codes
LINE 連携用ワンタイムコード(短命)
新規 (20260606130000)
マイグレーションファイル名は追加時に本表へ追記する。
型は PostgreSQL。NULL 欄は NO = NOT NULL。
カラム名
型
NULL
デフォルト
説明
id
uuid
NO
—
PK。auth.users.id と一致
email
varchar(255)
NO
—
ログインアドレス
display_currency
varchar(8)
NO
'JPY'
F-13 : 表示ベース通貨
onboarding_completed
boolean
NO
false
F-01 : 初回オンボーディング(サブスク一括登録)を完了したか。false の間は初回ログイン時にオンボーディング画面を表示する
created_at
timestamptz
NO
now()
updated_at
timestamptz
YES
—
最終更新
インデックス
インデックス名
カラム
種別
目的
profiles_pkey
id
PRIMARY
—
自動作成 : auth.users への INSERT を契機に public.handle_new_user() トリガが profiles 行(id / email)を作成する。メール認証・Google OAuth いずれの登録でも適用される(マイグレーション: 20260605160000_create_profile_on_signup.sql)。
カラム名
型
NULL
デフォルト
説明
id
int
NO
identity
PK
name
varchar(64)
NO
—
例: 動画・音楽・AI
color_code
varchar(16)
NO
—
グラフ用 Hex 等
icon_name
varchar(64)
YES
—
UI アイコン名
インデックス
インデックス名
カラム
種別
目的
categories_pkey
id
PRIMARY
—
ジャンル対応 : id 1〜17 は src/domain/genre.ts の GENRE_IDS 順(ai, video, music, … other)と対応。name は日本語ラベル(画面表示用)。
カラム名
型
NULL
デフォルト
説明
id
int
NO
identity
PK
name
varchar(32)
NO
—
例: monthly, yearly, weekly
インデックス
インデックス名
カラム
種別
目的
cycles_pkey
id
PRIMARY
—
cycles_name_key
name
UNIQUE
コードとして一意想定(要否は実装で確定)
services(旧 Master_Services)
カラム名
型
NULL
デフォルト
説明
id
uuid
NO
gen_random_uuid()
PK
name
varchar(255)
NO
—
例: Netflix, Spotify
category_id
int
NO
—
FK → categories.id
user_id
uuid
YES
—
FK → profiles.id。NULL=プリセット、NOT NULL=ユーザーカスタム(F-02)
logo_key
varchar(100)
YES
—
同梱ロゴ assets/services/{logo_key}.jpeg の識別子
logo_uri
varchar(2048)
YES
—
外部ロゴ URL(任意)
icon_name
varchar(128)
YES
—
画像がない場合の UI アイコン名
インデックス
インデックス名
カラム
種別
目的
services_pkey
id
PRIMARY
—
services_logo_key_key
logo_key
UNIQUE
プリセット slug の一意(NULL 除く)
(任意)
category_id
INDEX
ジャンルフィルタ
カラム名
型
NULL
デフォルト
説明
id
uuid
NO
gen_random_uuid()
PK
service_id
uuid
NO
—
FK → services.id
name
varchar(255)
NO
—
例: Premium, 学割
price
numeric(14,4)
NO
—
決済金額
currency
varchar(8)
NO
'JPY'
このプランの決済通貨
cycle_id
int
NO
—
FK → cycles.id
default_trial_days
integer
YES
—
052 : プリセット登録時のデフォルトお試し日数(NULL 可)
インデックス
インデックス名
カラム
種別
目的
plans_pkey
id
PRIMARY
—
(任意)
service_id
INDEX
サービス単位のプラン一覧
カラム名
型
NULL
デフォルト
説明
id
uuid
NO
gen_random_uuid()
PK
user_id
uuid
NO
—
FK → profiles.id(RLS 対象)
plan_id
uuid
NO
—
FK → plans.id
start_date
date
NO
—
契約の最初の請求日(不変)。F-09 利用頻度計算など
next_billing_date
date
NO
—
F-10 通知の基準日。請求ごとに更新
custom_price
numeric(14,4)
YES
—
F-01 : ユーザー編集料金。NULL 時は plans.price を使用
status
varchar(50)
NO
'active'
F-03 : active / paused / cancelled のいずれか(CHECK 制約で限定)
cancelled_at
timestamptz
YES
—
F-04/044 : 解約日時。cancelled のとき必須・それ以外は NULL(CHECK 制約で整合)
trial_ends_on
date
YES
—
052 : お試し終了日。NULL のとき通常課金。終了後は自動クリア
memo
text
YES
—
解約 URL・メモ
created_at
timestamptz
NO
now()
updated_at
timestamptz
YES
—
インデックス
インデックス名
カラム
種別
目的
subscriptions_pkey
id
PRIMARY
—
(任意)
user_id
INDEX
ユーザーの契約一覧
(任意)
next_billing_date
INDEX
通知バッチ
notification_settings(新規)
カラム名
型
NULL
デフォルト
説明
id
uuid
NO
gen_random_uuid()
PK
subscription_id
uuid
NO
—
FK → subscriptions.id
notify_days_before
int
NO
—
更新の何日前に通知するか(F-12 )
インデックス
インデックス名
カラム
種別
目的
notification_settings_pkey
id
PRIMARY
—
(任意)
subscription_id
INDEX
サブスク単位の設定取得
カラム名
型
NULL
デフォルト
説明
id
uuid
NO
gen_random_uuid()
PK
user_id
uuid
NO
—
FK → profiles.id
subscription_id
uuid
NO
—
FK → subscriptions.id
used_date
date
NO
—
利用日(F-08 )。同一サブスク × 日で一意推奨
created_at
timestamptz
NO
now()
記録時刻
インデックス
インデックス名
カラム
種別
目的
usage_logs_pkey
id
PRIMARY
—
usage_logs_subscription_id_used_date_key
subscription_id, used_date
UNIQUE
同日二重記録防止
LINE 公式アカウントのトークから利用実績を記録するための、ユーザーと LINE userId の 1:1 紐付け。書き込み(連携確定)は Edge Function が service_role で行う。
カラム名
型
NULL
デフォルト
説明
user_id
uuid
NO
—
PK。FK → profiles.id
line_user_id
text
NO
—
LINE のユーザーID(UNIQUE)
linked_at
timestamptz
NO
now()
連携した時刻
RLS : 自分の行のみ select / delete(auth.uid() = user_id)。insert/update は service_role のみ。
連携用のワンタイムコード。アプリが RPC create_line_link_code() で発行し、ユーザーが LINE に送ると Edge Function が照合して line_links を作成する。
カラム名
型
NULL
デフォルト
説明
code
text
NO
—
PK。6 桁の数字コード
user_id
uuid
NO
—
FK → profiles.id
expires_at
timestamptz
NO
—
有効期限(発行から 10 分)
created_at
timestamptz
NO
now()
発行時刻
RLS : ポリシーなし(一般ユーザーは直接アクセス不可)。発行は SECURITY DEFINER の RPC、照合は service_role。
スキーマの変更はマイグレーションファイルのみ (UI 上のエディタで本番・Staging を直接いじらない。理由・例外は docs/Rule.md )
ローカル: Supabase CLI(docs/TRD.md §1 参照)
スキーマ変更後、本書の「テーブル一覧」のマイグレーション列を更新する
profiles: 原則として「自分の行のみ select / update」(auth.uid() = id)
subscriptions: select / insert に加え、update(一時停止・再開・解約)と delete(F-04 物理削除)も auth.uid() = user_id に限定して許可(20260617000000)
subscriptions / usage_logs / notification_settings: user_id または親 subscription 経由でユーザーに紐づく行のみ
usage_logs: select に加え、insert / delete も auth.uid() = user_id に限定して許可(F-08 利用チェック/取り消し)
マスタ(categories, cycles, services, plans): anon / authenticated に SELECT 可 (プリセット参照用)。services は user_id IS NULL のプリセットのみ全員が読める。カスタム(user_id = auth.uid())は本人のみ SELECT(F-02)
next_billing_date の自動繰り上げ(DB 関数・cron)
calc_next_billing_date(start_date, cycle, today): 契約開始日とサイクルから「今日以降の最初の請求日」を算出する純粋関数
refresh_due_billing_dates(): status = active かつ next_billing_date < 今日(JST) の契約を一括更新。戻り値は更新件数
pg_cron ジョブ refresh-due-billing-dates: 毎日 JST 0:10(UTC 15:10)に refresh_due_billing_dates() を実行
開始日を起点に周期を加算するため、毎月の請求日(例: 15 日)を維持し、月末 clamp によるドリフトを防ぐ
日付
変更内容
2026-06-26
subscription_price_history 追加(F-03 料金変更の「今月から」対応、20260626180000)
2026-06-26
DAZN「W杯(月額更新)」プラン追加、Gemini の Google AI Plus 料金を ¥725 に更新(20260626120000)
2026-06-25
subscriptions.trial_ends_on(date・NULL 可)、plans.default_trial_days(integer・NULL 可)追加。お試し期間(052、20260625120000)
2026-06-18
services.user_id 追加、create_custom_subscription RPC、カスタムサービス用 RLS(F-02 / 038、20260618100000)
2026-06-17
subscriptions.cancelled_at(timestamptz・NULL 可・status との整合 CHECK 制約)追加、update / delete の RLS ポリシー追加(044 一時停止・解約・削除、20260617000000)
2026-06-16
profiles.onboarding_completed(boolean・NOT NULL・default false)追加、profiles の UPDATE RLS(自分の行のみ)追加、既存契約ありユーザーを完了扱いに backfill(初回オンボーディングのサブスク一括登録、F-01、20260616120000)
2026-06-12
subscriptions.custom_price(numeric(14,4)・NULL 可・非負 CHECK 制約)追加と INSERT RLS ポリシー追加(F-01 プリセット登録、20260612170000)
2026-06-09
重複サービスを参照する test@test.com の契約(55555555-…)を正プランへ付け替え、参照の消えた誤plan(44444444-…)・誤service(33333333-…)を削除して重複を解消(20260609130000)
2026-06-09
プリセット整理: 重複サービス(手動投入の 33333333-… Netflix/Amazon Prime Video/DAZN)と配下プランを削除、無料プラン(price=0、金額不定は除外)を削除、Figma/Gemini を同梱ロゴ(logo_key)方式へ統一(20260609120000)
2026-06-06
Dropbox のプリセット価格を税込に統一(20260606160000)
2026-06-06
AppleCare+「iPhone(月払い)」プランを追加(20260606150000)
2026-06-06
既存ユーザーの profiles backfill と create_line_link_code の profile 自動作成(20260606140000)
2026-06-06
line_links / line_link_codes と連携コード発行 RPC を追加(LINE 連携)
2026-06-06
プリセットマスタ投入: services.logo_key 追加、categories 17 ジャンル、RLS(マスタ SELECT 可)、Notion 由来の services 53 件 / plans 210 件を投入、migration 20260606120000 / 20260606120100
2026-06-05
subscriptions.status に CHECK 制約を追加(active / paused / cancelled に限定)
2026-06-05
マスタテーブルの select RLS ポリシー追加。next_billing_date 自動繰り上げ関数・pg_cron を追加
2026-06-04
usage_logs に INSERT / DELETE の RLS ポリシーを追加(F-08 利用チェック)
2026-06-02
subscriptions.start_date(契約の最初の請求日)を追加
2026-05-15
「スキーマ変更のルール」: Supabase UI での変更は原則禁止、マイグレーション運用を明記
2026-05-15
初版: ER・テーブル定義ドラフトを反映