🏗️ 架空題材アプリ:「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_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章を通して参照する「共通辞書」として使ってください。
「あのテーブルのことね」がすぐ通じるようになります。