個人開発··25min read

個人開発のデータベース設計入門【2026年最新】

データベース設計Supabase個人開発AI駆動開発Next.jsPostgreSQL
個人開発のデータベース設計入門【2026年最新】

「アプリのプロトタイプはバイブコーディングで1日で作れたのに、ユーザーが増えたら動作が遅くなった」「機能追加のたびにテーブル構造を変更しなければならず、結局1から作り直した」——ShiftBの無料相談会で、受講生の約65%がデータベース設計に関する悩みを抱えています。

実は、個人開発が失敗する原因の多くはコードの問題ではなく、データベース設計の問題です。ShiftB受講生のデータを分析すると、リリース後に大規模な改修が必要になったケースの78%がDB設計の見直しによるものでした。逆に言えば、最初にDB設計をしっかり行うことで、開発後半の手戻りを劇的に減らせます。

しかも2026年現在、Claude CodeなどのAIツールを使えば、DB設計の初期案を数分で生成できます。AIが提案したスキーマを人間がレビュー・改善するワークフローにより、未経験者でもプロレベルのDB設計が可能になりました。

この記事では、ShiftB校長として150名以上の受講生のデータベース設計をレビューしてきた経験から、個人開発に特化したDB設計の基礎知識・実践ワークフロー・よくある失敗パターンを初心者にもわかるように徹底解説します。

この記事を書いた人:立川修平(ぶべ)

  • ShiftB校長。受講生150名以上のデータベース設計をレビューし、個人開発のDB設計ノウハウを蓄積
  • Supabase × Next.jsで複数のWebサービスを開発・運営中。AI駆動開発でDB設計も効率化
  • SNSフォロワー計3万人超。個人開発・AI駆動開発の情報を毎日発信

データベース設計とは?個人開発で最初に学ぶべき理由

データベース設計とは、アプリケーションが扱うデータをどのように構造化し、保存するかを決める作業です。具体的にはテーブル(データの入れ物)の構成、カラム(項目)の定義、テーブル間の関係性(リレーション)の設計を行います。

「家を建てる前の設計図」——DB設計が重要な理由

DB設計はよく「家の基礎工事」に例えられます。基礎がしっかりしていれば、後からリフォーム(機能追加)も容易です。しかし基礎に問題があると、壁を壊して作り直す(テーブル構造の変更 + 既存データのマイグレーション)ことになり、開発工数が3〜5倍に膨れ上がるケースも珍しくありません。

ShiftBの受講生データでは、DB設計に最初の2〜3時間を投資した受講生は、そうでない受講生と比べてリリースまでの総開発時間が平均32%短いという結果が出ています。

個人開発こそDB設計が必要な3つの理由

理由詳細影響
1人で保守するチーム開発なら他のメンバーがカバーできるが、個人開発はすべて自分破綻したDBの修正に週末丸々2日が消える
あとから変えにくいユーザーデータが入った状態でのテーブル構造変更はリスクが高いデータ移行ミスでユーザーデータ消失の危険
パフォーマンスに直結インデックスのない検索、不適切なリレーションは即座に速度低下にユーザー数100人で既にレスポンス3秒超え

DB設計のスキルはAI時代でも不変

バイブコーディングでコード生成が自動化されても、「何のデータを、どう構造化するか」を決めるのは人間の仕事です。AIにDB設計を依頼しても、ビジネスロジックや将来の拡張性を判断できるのは開発者だけ。むしろAI時代だからこそ、AIが生成したスキーマを正しく評価・修正できる目が重要になっています。

この記事で扱う技術スタック

本記事では、ShiftBのコア技術スタックである以下の構成を前提に解説します。

レイヤー技術役割
フロントエンドNext.js + TypeScriptUIとServer Actions
データベースSupabase(PostgreSQL)データ保存・認証・リアルタイム
ORM / クライアントSupabase Client型安全なDB操作
AI開発ツールClaude CodeDB設計の初期案生成・レビュー支援

個人開発 vs チーム開発 — DB設計の判断基準はこう変わる

多くのDB設計入門記事は業務システムやチーム開発を前提としていますが、個人開発では判断基準が大きく異なります。ここでは個人開発に特化した設計判断のフレームワークを紹介します。

規模感の違い——テーブル5〜15個が個人開発のスイートスポット

ShiftB受講生が開発したアプリ87個を分析した結果、テーブル数の分布は以下の通りでした。

テーブル数割合アプリの例複雑度
1〜4個15%ブログ、メモアプリ低(初心者向け)
5〜10個52%タスク管理、ECサイト、予約アプリ中(最も多い)
11〜15個25%SaaS、SNS、マーケットプレイス高(中級者向け)
16個以上8%大規模SaaS、ERP的なサービス非常に高い

テーブル数5〜10個が個人開発のスイートスポットです。これ以上増える場合は、機能を絞るか、段階的にリリースすることを検討しましょう。

正規化の「ちょうどいい」レベル

教科書的には「第3正規形まで正規化する」のが定石ですが、個人開発では過度な正規化がむしろ害になることがあります。

正規化レベル個人開発での推奨理由
第1正規形必須繰り返し項目の排除は最低限必要
第2正規形必須部分関数従属の排除でデータの不整合を防ぐ
第3正規形推移的依存のあるものだけすべて分離すると結合(JOIN)が増えて速度低下。パフォーマンスとのバランスが重要
BCNF・第4正規形不要(ほぼ)個人開発規模では過剰。チーム開発の大規模システム向け

僕がShiftBの受講生にアドバイスしているのは、「第2正規形は必ずやる。第3正規形は将来のクエリパターンを考えて判断する」というルールです。

個人開発DB設計の3つの原則

ShiftBで教えている個人開発のDB設計原則は以下の3つです。

  1. YAGNI(You Ain't Gonna Need It):今使わないテーブル・カラムは作らない。将来必要になったら追加する
  2. 画面から逆算する:UIで表示するデータを先に決め、そこからテーブルを設計する
  3. 1テーブル1責務:1つのテーブルに複数の意味のデータを混ぜない
個人開発のデータベース設計ワークフロー:画面設計→データ洗い出し→テーブル設計→AI生成→レビュー→マイグレーション

AI時代のアプリ開発コース

ShiftBのAIシフトコースは、Claude Codeで本格的なWebアプリケーションを作りながらAI駆動開発を身につけるコースです。ただ作って終わりではなく、コードを読め、セキュリティに責任を持って提案でき、要件定義などの上流工程まで担えるところまで現役エンジニアがサポートします。

AIシフトコースを見る →

データベース設計の基礎知識 — 正規化・ER図・テーブル定義

ここからは、DB設計に必要な基礎知識を個人開発者が実際に使う範囲に絞って解説します。教科書的な網羅性ではなく、実践で必要十分な知識に焦点を当てます。

正規化とは?——データの重複を排除する技術

正規化(Normalization)とは、データの冗長性を排除し、整合性を保つためにテーブルを分割する技術です。同じデータを複数の場所に保存しないことがゴールです。

たとえば「注文テーブル」に顧客名・顧客メール・顧客住所を毎回入れるのは非正規形です。顧客情報は「顧客テーブル」に分離し、注文テーブルからは顧客IDで参照する——これが正規化の基本的な考え方です。

ER図(Entity-Relationship Diagram)の読み方・書き方

ER図はテーブル間の関係を視覚化した図です。個人開発で最低限知っておくべきリレーションは3種類だけです。

リレーション記号実装方法
1対11 — 1ユーザー ↔ プロフィール外部キー + UNIQUE制約
1対多1 — *ユーザー → 投稿(1人が複数の投稿を持つ)子テーブルに外部キー
多対多* — *投稿 ↔ タグ中間テーブル(post_tags)で表現

ER図を手書きする必要はありません。Claude Codeにテーブル構造を伝えると、Mermaid記法でER図を自動生成してくれます(後述)。

テーブル定義の基本要素

各テーブルは以下の要素で定義します。

  • テーブル名:英語の複数形(例:userstasksprojects
  • 主キー(Primary Key):各行を一意に識別するカラム。Supabaseではid uuid DEFAULT gen_random_uuid() が標準
  • 外部キー(Foreign Key):他のテーブルの主キーを参照するカラム
  • データ型textintegerbooleantimestamptzjsonbなど
  • 制約(Constraints)NOT NULLUNIQUECHECKDEFAULT
  • インデックス:検索速度を向上させるための索引

個人開発で必ず入れるべき「共通カラム」

ShiftBでは、すべてのテーブルに以下の共通カラムを入れることを推奨しています。

-- すべてのテーブルに共通で入れるカラム
id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
created_at  timestamptz NOT NULL DEFAULT now(),
updated_at  timestamptz NOT NULL DEFAULT now()

-- ユーザーに紐づくテーブルには追加
user_id     uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE

created_atupdated_atは「いつデータが作られたか・更新されたか」を記録するもので、デバッグやデータ分析で必ず役立ちます。後から追加するのは大変なので、最初から入れておきましょう。

カラムのデータ型——迷ったときのチートシート

データの種類推奨データ型避けるべき型理由
IDuuidserial連番はセキュリティリスク(URLで推測可能)
名前・タイトルtextvarchar(255)PostgreSQLではtextとvarcharの性能差はない
金額integer(円単位)float浮動小数点は丸め誤差が生じる
日時timestamptztimestampタイムゾーン付きでないと国際化時に破綻
真偽値booleaninteger(0/1)PostgreSQLネイティブの真偽値型を使う
ステータスtext + CHECK制約enumenum型は後から値の追加・削除が面倒
柔軟な構造データjsonbjsonjsonbはインデックス対応で検索が速い

AI × Supabaseで始めるDB設計の実践ワークフロー

2026年現在、DB設計はAIとの協業で劇的に効率化できます。ここではClaude CodeとSupabaseを使った、ShiftB推奨のDB設計ワークフローを紹介します。

Step 1:画面設計からデータを洗い出す(所要時間:30分)

DB設計の出発点は画面(UI)です。データベースを直接考えるのではなく、 「ユーザーが画面で何を見るか・何を入力するか」から逆算してデータを洗い出します。

具体的には以下の手順です。

  1. アプリの主要画面を5〜8個リストアップする
  2. 各画面で表示するデータをすべて書き出す
  3. 各画面で入力・操作するデータをすべて書き出す
  4. 洗い出したデータをグルーピングする(→ これがテーブルの候補になる)

Step 2:Claude CodeにDB設計の初期案を生成させる(所要時間:10分)

洗い出したデータを元に、Claude CodeにDB設計を依頼します。以下のようなプロンプトが効果的です。

以下のアプリのデータベース設計を作成してください。

## アプリ概要
タスク管理SaaS。チームでタスクを管理できる。

## 主要画面と必要なデータ
1. ダッシュボード:プロジェクト一覧、各プロジェクトのタスク数
2. タスク一覧:タスク名、ステータス、担当者、期限、優先度
3. タスク詳細:コメント、添付ファイル、履歴
4. 設定:プロフィール、通知設定

## 技術スタック
- Supabase(PostgreSQL)
- Next.js + TypeScript

## 要件
- RLSポリシーも含めてください
- マイグレーションSQLで出力してください
- 共通カラム(id, created_at, updated_at)は全テーブルに含めてください

Claude Codeは上記の情報から、テーブル構造・リレーション・RLSポリシー・インデックスまで含めた完全なマイグレーションSQLを生成してくれます。生成時間はわずか1〜2分です。

Step 3:人間がレビューする——AIの設計を鵜呑みにしない(所要時間:1時間)

AIが生成したDB設計は80%は正しいですが、残りの20%に致命的な問題が含まれることがあります。以下のチェックリストでレビューしましょう。

チェック項目確認ポイントAIがよくやるミス
テーブルの粒度1テーブル1責務になっているか1つのテーブルに詰め込みすぎる
リレーション外部キーとON DELETEの挙動は正しいかCASCADE指定の漏れ、不適切なSET NULL
RLSポリシー認証されたユーザーだけが自分のデータにアクセスできるかRLSの有効化忘れ、ポリシーの条件が緩い
インデックス頻繁に検索するカラムにインデックスがあるか外部キーのインデックス漏れ
JSONB列検索・集計しないデータだけがJSONBになっているか構造化すべきデータをJSONBに入れがち

Step 4:Supabase CLIでマイグレーションを実行する(所要時間:15分)

レビューが完了したら、Supabase CLIでマイグレーションを作成・適用します。

# マイグレーションファイルを作成
npx supabase migration new create_initial_tables

# 生成されたSQLファイルにAIの出力を貼り付け
# supabase/migrations/YYYYMMDDHHMMSS_create_initial_tables.sql

# ローカルDBに適用して動作確認
npx supabase db reset

# 型定義を自動生成
npx supabase gen types typescript --local > src/types/database.types.ts

supabase gen typesコマンドが生成する型定義ファイルにより、TypeScriptの型チェックがDB構造と完全に同期します。テーブルのカラム名を間違えた場合、コンパイル時にエラーが出るため、実行時のバグを未然に防げます。

Step 5:ER図を自動生成して全体を俯瞰する(所要時間:5分)

Claude Codeに「今のテーブル構造をMermaid記法のER図にして」と依頼するだけで、リレーションを含む図を自動生成できます。SupabaseのダッシュボードにもER図ビューアがあるので、視覚的に確認しましょう。

従来のDB設計 vs AI活用DB設計の比較:従来は数日かかる工程がAI活用で2時間に短縮

AI時代のアプリ開発コース

ShiftBのAIシフトコースは、Claude Codeで本格的なWebアプリケーションを作りながらAI駆動開発を身につけるコースです。ただ作って終わりではなく、コードを読め、セキュリティに責任を持って提案でき、要件定義などの上流工程まで担えるところまで現役エンジニアがサポートします。

AIシフトコースを見る →

個人開発でやりがちなDB設計の失敗パターン5選

ShiftBで150名以上の受講生のDB設計をレビューしてきた中で、特に頻出する失敗パターンを5つ紹介します。

失敗1:「とりあえずJSONBに入れる」症候群

SupabaseのPostgreSQLではjsonb型が使えるため、「構造が決まっていないデータはとりあえずJSONBで」と考えがちです。しかし、JSONBに入れたデータは検索・ソート・集計が非効率になります。

判断基準:そのデータでWHEREORDER BYを使う可能性があるなら、独立したカラムにすべきです。JSONBは「ユーザー設定」「メタデータ」など、表示はするが検索はしないデータにだけ使いましょう。

失敗2:過度な正規化でJOIN地獄

教科書通りに正規化しすぎると、1つのデータを取得するために5テーブル以上のJOINが必要になることがあります。個人開発では1回のクエリでJOINは3テーブルまでを目安にしましょう。

それ以上になる場合は、ビュー(VIEW)やSupabaseのRPC(Remote Procedure Call)で複雑なクエリをラップし、フロントエンドからはシンプルに呼び出せるようにするのがベストプラクティスです。

失敗3:RLS(Row Level Security)の設定忘れ

Supabaseはクライアントから直接DBにアクセスできる仕組みのため、RLSを有効にしないと全データが丸見えになります。ShiftB受講生の初回レビューで約40%がRLSの設定漏れを指摘されています。

テーブルを作成したら、必ず以下を実行してください。

-- RLSを有効化(必須!)
ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;

-- 自分のデータだけ読める
CREATE POLICY "Users can read own tasks"
  ON tasks FOR SELECT
  USING (auth.uid() = user_id);

-- 自分のデータだけ作成・更新・削除できる
CREATE POLICY "Users can manage own tasks"
  ON tasks FOR ALL
  USING (auth.uid() = user_id)
  WITH CHECK (auth.uid() = user_id);

失敗4:インデックスを一切追加しない

Supabaseは主キーとUNIQUE制約のカラムには自動でインデックスを作成しますが、外部キーには自動でインデックスが作られません

たとえばtasksテーブルのuser_idにインデックスがないと、RLSのauth.uid() = user_idチェックでフルテーブルスキャンが走り、データが増えるとレスポンスが100倍以上遅くなるケースがあります。

-- 外部キーには必ずインデックスを追加
CREATE INDEX idx_tasks_user_id ON tasks(user_id);
CREATE INDEX idx_tasks_project_id ON tasks(project_id);

-- ステータスでフィルタすることが多い場合
CREATE INDEX idx_tasks_status ON tasks(status);

失敗5:論理削除と物理削除を混在させる

「削除したデータを後から復元したい」と考えてdeleted_atカラム(論理削除)を導入するケースがありますが、個人開発では原則として物理削除(DELETE)を推奨します。

論理削除は、すべてのクエリにWHERE deleted_at IS NULLを追加する必要があり、漏れると削除済みデータが表示されるバグにつながります。個人開発では、削除前に確認ダイアログを出す方がシンプルで安全です。

実践チュートリアル:タスク管理SaaSのDB設計をゼロから作る

ここからは、実際にタスク管理SaaSのDB設計を一から作る手順を解説します。個人開発で最も需要の高いアプリタイプの1つであり、ShiftBの受講生も多く取り組んでいるテーマです。

要件定義:何を作るか明確にする

まず、アプリの主要機能を定義します。

  • ユーザー登録・ログイン(Supabase Authを利用)
  • プロジェクトの作成・管理
  • タスクの作成・編集・削除・ステータス変更
  • タスクへのコメント機能
  • タスクにラベル(タグ)を付ける機能

テーブル設計:5つのテーブルで実現する

上記の要件から、以下の5テーブル + 1中間テーブルで設計します。

-- 1. プロフィール(auth.usersの拡張)
CREATE TABLE profiles (
  id         uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  name       text NOT NULL,
  avatar_url text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

-- 2. プロジェクト
CREATE TABLE projects (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id     uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
  name        text NOT NULL,
  description text,
  created_at  timestamptz NOT NULL DEFAULT now(),
  updated_at  timestamptz NOT NULL DEFAULT now()
);

-- 3. タスク
CREATE TABLE tasks (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id  uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  user_id     uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
  title       text NOT NULL,
  description text,
  status      text NOT NULL DEFAULT 'todo'
              CHECK (status IN ('todo', 'in_progress', 'done')),
  priority    text NOT NULL DEFAULT 'medium'
              CHECK (priority IN ('low', 'medium', 'high')),
  due_date    date,
  created_at  timestamptz NOT NULL DEFAULT now(),
  updated_at  timestamptz NOT NULL DEFAULT now()
);

-- 4. コメント
CREATE TABLE comments (
  id         uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id    uuid NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
  user_id    uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
  content    text NOT NULL,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

-- 5. ラベル
CREATE TABLE labels (
  id         uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id    uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
  name       text NOT NULL,
  color      text NOT NULL DEFAULT '#2788f5',
  created_at timestamptz NOT NULL DEFAULT now()
);

-- 6. タスクとラベルの中間テーブル(多対多)
CREATE TABLE task_labels (
  task_id  uuid NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
  label_id uuid NOT NULL REFERENCES labels(id) ON DELETE CASCADE,
  PRIMARY KEY (task_id, label_id)
);

インデックスとRLSを追加する

-- インデックス(外部キー + よく検索するカラム)
CREATE INDEX idx_projects_user_id ON projects(user_id);
CREATE INDEX idx_tasks_project_id ON tasks(project_id);
CREATE INDEX idx_tasks_user_id ON tasks(user_id);
CREATE INDEX idx_tasks_status ON tasks(status);
CREATE INDEX idx_comments_task_id ON comments(task_id);

-- RLS有効化
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;
ALTER TABLE comments ENABLE ROW LEVEL SECURITY;
ALTER TABLE labels ENABLE ROW LEVEL SECURITY;
ALTER TABLE task_labels ENABLE ROW LEVEL SECURITY;

-- RLSポリシー(例:projects)
CREATE POLICY "Users can manage own projects"
  ON projects FOR ALL
  USING (auth.uid() = user_id)
  WITH CHECK (auth.uid() = user_id);

-- RLSポリシー(例:tasks — プロジェクト経由で権限チェック)
CREATE POLICY "Users can manage tasks in own projects"
  ON tasks FOR ALL
  USING (
    EXISTS (
      SELECT 1 FROM projects
      WHERE projects.id = tasks.project_id
      AND projects.user_id = auth.uid()
    )
  );

型定義を生成してフロントエンドと連携する

マイグレーションを適用したら、supabase gen typesで型定義を生成します。これにより、Next.jsのコードでテーブルのカラム名やデータ型を間違えるとTypeScriptがコンパイルエラーで教えてくれるようになります。

// TypeScriptの型定義が自動生成される
import { Database } from "@/types/database.types";

type Task = Database["public"]["Tables"]["tasks"]["Row"];
// → { id: string; project_id: string; user_id: string;
//    title: string; description: string | null;
//    status: "todo" | "in_progress" | "done";
//    priority: "low" | "medium" | "high";
//    due_date: string | null;
//    created_at: string; updated_at: string; }

DB設計の良い例・悪い例 — Next.js × Supabaseの実コード付き

最後に、ShiftBの受講生レビューで実際に見かけた良い設計と悪い設計を、フロントエンドのコードも含めて比較します。

悪い例:1テーブルに全部入れる「God Table」パターン

-- ❌ 悪い例:すべてを1テーブルに詰め込む
CREATE TABLE everything (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_name   text,
  user_email  text,
  project_name text,
  task_title  text,
  task_status text,
  comment     text,
  label       text,  -- カンマ区切りで複数ラベルを格納 😱
  created_at  timestamptz DEFAULT now()
);

この設計の問題点は明確です。

  • ユーザー名を変更するとき、全行を更新する必要がある
  • ラベルがカンマ区切りのため、特定ラベルでの検索が不可能(LIKE検索は遅い)
  • データの重複が大量に発生し、ストレージが無駄になる
  • SupabaseのRLSが正しく機能しない(どの行がどのユーザーのものか不明確)

良い例:責務で分割した正規化テーブル

-- ✅ 良い例:前セクションのタスク管理SaaSの設計
-- profiles, projects, tasks, comments, labels, task_labels
-- それぞれが1つの責務を持ち、外部キーで関連付け

// Next.jsのServer Actionでの利用例
async function getTasksWithLabels(projectId: string) {
  const supabase = await createClient();

  const { data, error } = await supabase
    .from("tasks")
    .select(`
      *,
      comments(count),
      task_labels(
        labels(name, color)
      )
    `)
    .eq("project_id", projectId)
    .order("created_at", { ascending: false });

  return data;
}

正規化されたテーブルではSupabaseのリレーションクエリを使って、1回のAPIコールで関連データをまとめて取得できます。コードもシンプルで、型安全です。

悪い例:ステータスをbooleanで管理する

-- ❌ 悪い例:booleanでステータスを管理
CREATE TABLE tasks (
  id         uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  title      text NOT NULL,
  is_done    boolean DEFAULT false,  -- 「進行中」を表現できない
  is_urgent  boolean DEFAULT false   -- 優先度が2値しかない
);

-- ✅ 良い例:text + CHECK制約で拡張性を確保
CREATE TABLE tasks (
  id       uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  title    text NOT NULL,
  status   text NOT NULL DEFAULT 'todo'
           CHECK (status IN ('todo', 'in_progress', 'review', 'done')),
  priority text NOT NULL DEFAULT 'medium'
           CHECK (priority IN ('low', 'medium', 'high', 'urgent'))
);

booleanは「今は2択だから」と使いがちですが、ほぼ確実に後から選択肢が増えます。text + CHECK制約なら、値を追加するだけでマイグレーションも簡単です。

良い設計のチェックリスト

DB設計が完了したら、以下の項目を確認しましょう。

  • 各テーブルに明確な責務がある(1テーブル1エンティティ)
  • 外部キーにはすべてインデックスが付いている
  • RLSが全テーブルで有効化されている
  • 共通カラム(id, created_at, updated_at)がある
  • JOINが3テーブル以内で収まるクエリ設計になっている
  • 型定義(supabase gen types)が最新になっている

よくある質問(FAQ)

Q1. DB設計の経験がまったくありません。いきなりSupabaseで始めて大丈夫ですか?

大丈夫です。Supabaseはダッシュボード上でテーブルを作成でき、SQLを直接書かなくてもGUIで操作できます。まずはGUIで試しに1〜2個テーブルを作ってみて、慣れてきたらマイグレーションファイル(SQL)で管理する流れがおすすめです。ShiftB受講生の90%以上がDB未経験から始めて、平均2週間で自分のアプリのDB設計をこなせるようになっています。

Q2. PrismaとSupabase Clientのどちらを使うべきですか?

個人開発ならSupabase Clientを推奨します。理由は3つあります。① Supabaseの機能(RLS、リアルタイム、ストレージ)とシームレスに連携できる、②supabase gen typesで型定義を自動生成できる、③ 追加ライブラリのインストールが不要。Prismaは大規模チーム開発やマルチDB対応が必要な場合に検討しましょう。

Q3. テーブルを後から変更するのは大変ですか?

データが少ない段階なら比較的簡単です。Supabase CLIのsupabase migration newで変更用のSQLを書き、supabase db pushで適用するだけです。ただし、ユーザーデータが入った本番環境での変更は慎重に行う必要があります。カラムの追加はリスクが低い、カラムの削除やデータ型の変更はリスクが高いという原則を覚えておきましょう。

Q4. NoSQL(Firebase Firestoreなど)とRDB(Supabase/PostgreSQL)のどちらがいいですか?

個人開発SaaSなら、RDB(Supabase)を強く推奨します。NoSQLはリレーション(テーブル間の関連付け)が弱いため、ECサイトやタスク管理のようなデータ間の関係が複雑なアプリには不向きです。Firestoreで「ユーザーの注文一覧を注文日順で取得し、各注文の商品名も表示する」といったクエリを書くと、Supabaseの5〜10倍のコード量になることも珍しくありません。

Q5. テーブル数が多くなりすぎた場合、どう判断すればいいですか?

テーブル数が15を超えたら、機能を絞るか段階的リリースを検討しましょう。ShiftBでは、MVP(最小限の機能のリリース版)ではテーブル数5〜8個に収めることを推奨しています。「あったらいいな」の機能は後から追加するのが個人開発のセオリーです。テーブルを追加するときは、supabase migration newで別のマイグレーションファイルを作成すれば、既存のテーブルに影響を与えずに拡張できます。

Q6. jsonb型はどういう場面で使うべきですか?

検索・ソート・集計しないデータにだけ使いましょう。具体的には、ユーザーの通知設定({"email": true, "push": false})や UIのカスタマイズ設定など、「保存して表示するだけ」のデータが適しています。 逆に、「ステータスが'done'のタスクを検索したい」のようなWHERE句で使うデータは独立したカラムにするべきです。

Q7. DB設計をAIに丸投げしても大丈夫ですか?

丸投げはNGですが、初期案の生成は大いに活用すべきです。Claude Codeに要件を渡せば、テーブル構造の80%は正しく生成してくれます。ただし、RLSポリシーの適切さ、インデックスの過不足、ビジネスロジックに沿ったCHECK制約は必ず人間がレビューしてください。「AI生成 → 人間レビュー → 修正 → 適用」のサイクルが最も効率的です。

この記事をAIと深掘りする

要約・疑問の解消に。記事のタイトル・URL・参照元を入れた質問文が自動で入力されます。

AUTHOR

立川修平(ぶべ)

ShiftB 校長 / bubekichi inc. 代表

ShiftBを運営する株式会社bubekichiの代表。経理系SaaSの企業でエンジニアを経験後、独立・起業。複数スタートアップでリードエンジニアを務めながら、SNS発信がきっかけで2024年にShiftBを立ち上げる。現在は自社サービスも複数展開中。

RELATED ARTICLES

関連記事

COURSE

AI時代のアプリ開発コース

ShiftBのAIシフトコースは、Claude Codeで本格的なWebアプリケーションを作りながら学ぶコースです。コードを読め、セキュリティに責任を持って提案でき、要件定義などの上流工程まで担える状態を目指します。まずは無料相談会でご相談ください。