0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

🗄️ 「今さら学ぶ」シリーズ — 共通題材アプリ DB設計書

0
Last updated at Posted at 2026-02-17

🏗️ 架空題材アプリ:「KnowledgeNote」(学習記録共有サービス)

コンセプト

Qiita風の学習記録共有サービス。
記事投稿・フォロー・いいね・コメント(返信あり)・タグ・通知・画像投稿ができる。

よくある学習アプリとの差別化

よくある学習アプリ KnowledgeNote 変えた理由
Twitter風SNS(microposts等) KnowledgeNote(Qiita風) テーマを学習記録に変更
microposts(つぶやき) articles(記事) 長文の技術記事に
relationships follows より直感的な名前に
─(なし) comments 記事へのコメント機能を追加
─(なし) likes いいね機能を追加(ポリモーフィック)
─(なし) tags + article_tags タグ機能を追加(多対多)
─(なし) roles + user_roles 権限管理を追加(Pundit連携)
─(なし) notifications 通知機能を追加(ポリモーフィック/STI)

テーブル一覧(全13テーブル)

テーブル名 役割 主に登場する章
users ユーザー情報 第9,10,12,13,16,20,21章
articles 学習記事(投稿) 第10,12,13,16,17章
follows フォロー/フォロワー(自己参照) 第16,19章
comments 記事へのコメント(返信対応) 第10,16,19章
likes いいね(ポリモーフィック) 第16,19章
tags タグマスター 第16,19章
article_tags タグ↔記事の中間テーブル 第18章
roles 権限マスター(admin/moderator等) 第21章
user_roles ユーザー↔権限の中間テーブル 第21章
notifications 通知(ポリモーフィック/STI) 第19,31章
active_storage_blobs 画像ファイル管理(Rails組み込み) 第30章
active_storage_attachments モデルとファイルの紐付け(Rails組み込み) 第30章
sessions ログインセッション管理 第6,21章

📐 ER図(Mermaid記法)


🔍 各テーブルの詳細と「どの章で使うか」

1. users(ユーザー)

たとえ話:KnowledgeNoteの「会員証」。名前・メールアドレス・パスワードが書いてある。
カラム 型 説明 登場する章
id bigint 主キー(自動採番) 全章
name string ユーザー名 第12章(バリデーション)
email_address string メールアドレス(一意) 第12章(uniqueness)
password_digest string 暗号化されたパスワード 第21章(認証)

💡 Rails 7 との違い
Rails 7 以前は email というカラム名が一般的でしたが、Rails 8 の組み込み認証ジェネレータでは email_address が標準です。
また、admin(boolean)カラムは削除し、roles + user_roles テーブルで権限管理しています。
remember_digest / activation_digest / reset_digest なども、Rails 8 では sessions テーブルとトークンベースの仕組みに置き換わっています。

関連するモデルのコード例:

class User < ApplicationRecord
  has_secure_password
  has_many :sessions, dependent: :destroy

  has_many :articles, dependent: :destroy
  has_many :comments, dependent: :destroy
  has_many :active_follows, class_name:  "Follow",
                            foreign_key: "follower_id",
                            dependent:   :destroy
  has_many :passive_follows, class_name:  "Follow",
                             foreign_key: "followed_id",
                             dependent:   :destroy
  has_many :following, through: :active_follows,  source: :followed
  has_many :followers, through: :passive_follows, source: :follower
  has_many :likes, dependent: :destroy
  has_many :notifications, dependent: :destroy
  has_many :user_roles, dependent: :destroy
  has_many :roles, through: :user_roles

  has_one_attached :avatar  # Active Storage

  normalizes :email_address, with: ->(e) { e.strip.downcase }

  def has_role?(role_name)
    roles.exists?(name: role_name.to_s)
  end
end

2. sessions(セッション — Rails 8 組み込み認証)

たとえ話:図書館の「入館記録カード」。
          誰が(user_id)、いつ(created_at)、どこから(ip_address)、
          何を使って(user_agent)入館したかが記録されている。
          同じ人が複数の端末でログインすれば、カードが複数枚できる。
カラム 型 説明 登場する章
id bigint 主キー —
user_id bigint ログインしたユーザー 第21章(認証)
ip_address string ログイン元のIPアドレス 第20章(セキュリティ)
user_agent string ブラウザ情報 第20章(セキュリティ)

💡 Rails 7 との違い
Rails 7 以前はセッション管理にcookieストアを使い、remember_digest カラムで永続ログインを実現するのが一般的でした。
Rails 8 の組み込み認証では sessions テーブルでセッションをDB管理します。
「どの端末からログインしているか」が一覧でき、個別にログアウト(セッション削除)もできます。

関連するモデルのコード例:

class Session < ApplicationRecord
  belongs_to :user
end

学べるパターン: 基本の1対多(User has_many :sessions)、DBベースのセッション管理、セキュリティ情報の記録


3. articles(学習記事)

たとえ話:ノートに書いた「学習メモ」を清書して投稿したもの。
          誰が書いたか(user_id)と、公開中かどうか(published)がわかる。
カラム 型 説明 登場する章
id bigint 主キー —
title string 記事タイトル 第12章(presence バリデーション)
body text 記事本文 第12章(length バリデーション)
published boolean 公開/下書きフラグ 第16章(scope :published / :draft)
user_id bigint 著者(外部キー) 第15章(belongs_to)

学べるパターン: 基本の1対多、dependent: :destroy、scope :published(下書き/公開の切り替え)


4. follows(フォロー関係 — 自己参照の多対多)

たとえ話:「AさんがBさんをフォローしている」という関係カード。
          同じusersテーブルの人同士を結ぶ、ちょっと特殊な中間テーブル。
カラム 型 説明 登場する章
id bigint 主キー —
follower_id bigint フォローする側のuser_id 第18章(自己参照)
followed_id bigint フォローされる側のuser_id 第18章(自己参照)

学べるパターン: 自己参照の多対多、class_name/foreign_key/sourceの指定、複合ユニークインデックス


5. comments(コメント — 階層構造あり)

たとえ話:記事に貼る「付箋コメント」。
          さらに付箋の上に付箋を重ねられる(返信)。
          parent_idがnullなら「直接のコメント」、
          parent_idがあれば「誰かのコメントへの返信」。
カラム 型 説明 登場する章
id bigint 主キー —
body text コメント本文 第12章(presence)
user_id bigint コメント投稿者 第15章
article_id bigint 対象の記事 第15章
parent_id bigint 親コメント(nullなら最上位) 第18章(階層構造)

関連するモデルのコード例:

class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :article
  belongs_to :parent, class_name: "Comment", optional: true
  has_many   :replies, class_name: "Comment",
                       foreign_key: "parent_id",
                       dependent: :destroy

  has_many :likes, as: :likeable, dependent: :destroy

  validates :body, presence: true

  scope :top_level, -> { where(parent_id: nil) }
end

学べるパターン: 2つの外部キー、ネストしたリソース(/articles/:id/comments)、 自己参照の親子(階層構造)、optional: true


6. likes(いいね — ポリモーフィック)

たとえ話:記事にもコメントにも貼れる「ハートのシール」。
          シールの裏には「何に貼ったか(種類+番号)」が書いてある。
カラム 型 説明 登場する章
id bigint 主キー —
user_id bigint いいねした人 第15章
likeable_type string 対象の種類("Article" or "Comment") 第18章(ポリモーフィック)
likeable_id bigint 対象のid 第18章(ポリモーフィック)

学べるパターン: ポリモーフィック関連(as: :likeable)、複合インデックス


7. tags + article_tags(タグ — 多対多)

たとえ話:記事に貼る「ラベルシール」。
          1つの記事に「Ruby」「Rails」「初心者向け」と複数のラベル。
          1つのラベルが何百もの記事に貼られている。
          article_tagsテーブルは「どの記事にどのラベルが貼ってあるか」の管理簿。
テーブル カラム 型 説明 登場する章
tags name string タグ名("Ruby", "Rails"等) 第18章
article_tags article_id bigint どの記事か 第18章(中間テーブル)
article_tags tag_id bigint どのタグか 第18章(中間テーブル)

学べるパターン: has_many :through(通常の多対多)、has_many :tags, through: :article_tags


8. notifications(通知 — ポリモーフィック + STI候補)

たとえ話:KnowledgeNoteの「お知らせベル」。
          「〇〇さんがあなたの記事にいいねしました」
          「〇〇さんがあなたをフォローしました」
          種類は違うけど、同じ通知テーブルに入る。
カラム 型 説明 登場する章
id bigint 主キー —
user_id bigint 通知を受け取る人 第15章
notifiable_type string 通知の原因("Like", "Comment", "Follow") 第18章(ポリモーフィック)
notifiable_id bigint 原因のid 第18章(ポリモーフィック)
action_type string "liked" / "commented" / "followed" 第18章(STI候補)
read boolean 既読フラグ 第16章(スコープ: unread)

学べるパターン: ポリモーフィック関連、STI、スコープ(scope :unread)、Action Cable(リアルタイム通知)


9. roles + user_roles(権限管理 — 多対多)

たとえ話:会社の「役職バッジ」。
          会員証(users)とは別に、「管理者バッジ」「モデレーターバッジ」がある。
          1人が複数のバッジを持てるし、同じバッジを持つ人は何人もいる。
          user_rolesテーブルは「誰がどのバッジを持っているか」の台帳。

          よくある学習アプリではadminカラム(boolean)で済ませることが多いが、
          実務では「管理者/モデレーター/一般」のように複数の権限を
          柔軟に扱いたい → rolesテーブルに分離する。
テーブル カラム 型 説明 登場する章
roles id bigint 主キー —
roles name string ロール名("admin", "moderator", "member") 第21章(認可)
user_roles id bigint 主キー —
user_roles user_id bigint どのユーザーか 第21章(Pundit)
user_roles role_id bigint どのロールか 第21章(Pundit)

adminカラム(boolean)との使い分け:

■ adminカラム方式(シンプル版)
  → 「管理者かどうか」の2択だけでいい場合。シンプルで速い。
  → user.admin? だけで判定できる。

■ roles + user_rolesテーブル方式(実務式)
  → 「管理者 / モデレーター / 一般」など3つ以上の権限がある場合。
  → 権限の追加・変更がDBだけで済む(コード変更なし)。
  → Punditと組み合わせてPolicy内で user.has_role?(:admin) のように使う。

関連するモデルのコード例:

# app/models/role.rb
class Role < ApplicationRecord
  has_many :user_roles, dependent: :destroy
  has_many :users, through: :user_roles

  validates :name, presence: true, uniqueness: true
end

# app/models/user_role.rb
class UserRole < ApplicationRecord
  belongs_to :user
  belongs_to :role

  validates :user_id, uniqueness: { scope: :role_id }
end

# app/policies/article_policy.rb(Punditの使用例)
class ArticlePolicy < ApplicationPolicy
  def destroy?
    record.user == user || user.has_role?(:admin) || user.has_role?(:moderator)
  end
end

学べるパターン: 通常の多対多(has_many :through)、Punditとの連携、uniqueness: { scope: }による複合一意制約


📌 各章とテーブルの対応早見表

第 9章 Rails基礎     → users, articles(MVCの説明に使う)
第10章 ルーティング   → users, articles, comments(resources, ネスト)
第12章 モデル詳解     → users, articles, comments(バリデーション, ActiveModel, Dirtyトラッキング)
第13章 ビュー         → users, articles(form_with, partial, helper)
第14章 コントローラ   → articles(CRUD, before_action, flash, Strong Parameters)
第19章 Hotwire        → articles, likes(Turbo Streamsでいいね即時反映)
第15章 ActiveRecord   → 全テーブル(CRUD, アソシエーション, includes, ページネーション)
第16章 スコープ       → articles, notifications(scope :published / :draft / :unread, enum)
第17章 DB・SQL        → users, articles, follows(インデックス, N+1, トランザクション, Seed)
第18章 リレーション   → follows, likes, tags/article_tags, comments, notifications
                       (自己参照 / ポリモーフィック / 多対多 / 階層構造 / STI)
第20章 セキュリティ   → users, sessions(CSRF, XSS, SQLi, 環境変数)
第21章 認証           → users, sessions, roles, user_roles
                       (Rails 8 組み込み認証 + Devise紹介, Pundit)
第29章 非同期/メール  → users, notifications(パスワード再設定, 通知メール)
第30章 Active Storage → users, articles(アバター, 記事内画像)
第31章 Action Cable   → notifications(リアルタイム通知, Solid Cable)

📚 このDB設計で学べるパターン一覧

DBパターン テーブル 登場する章
基本の1対多 users → articles, comments 第15章
基本の1対多(セッション管理) users → sessions 第21章
多対多(中間テーブル) tags ↔ article_tags ↔ articles 第18章
多対多(権限管理) users ↔ user_roles ↔ roles 第21章
自己参照の多対多 users ↔ follows ↔ users 第18章
自己参照の親子(階層構造) comments → comments(parent_id) 第18章
ポリモーフィック関連 likes, notifications 第18章
STI候補 notifications(action_type) 第18章
Active Storage users(avatar), articles(image) 第30章

💡 よくある学習アプリとの差分まとめ

観点 よくある学習アプリ(Twitter風) 今さら学ぶ(KnowledgeNote)
テーマ Twitter風SNS Qiita風 学習記録共有
投稿 microposts(140字の短文) articles(タイトル+本文の記事)
フォロー relationships follows(より直感的な名前)
コメント なし comments(階層構造あり)
いいね なし likes(ポリモーフィック)
タグ なし tags + article_tags(多対多)
権限管理 adminカラム(boolean) roles + user_roles(Pundit連携)
認証 remember_digest 等のカラム sessionsテーブル(Rails 8 組み込み認証)
通知 なし notifications(ポリモーフィック/STI)
画像 Active Storage(投稿画像のみ) Active Storage(アバター+記事内画像)
下書き なし publishedカラム(スコープで制御)

基礎的なCRUDアプリの上に、実務で使うパターンを積み上げた設計です。
テーブル名・機能ともに差をつけて、オリジナルな題材として成立するようにしています。


📝 この設計書は全34章を通して参照する「共通辞書」として使ってください。
「あのテーブルのことね」がすぐ通じるようになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?