4
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?

第9章|今さら学ぶ「Rails基礎(MVC / 規約)」

4
Last updated at Posted at 2026-02-19

第9章|今さら学ぶ「Rails基礎(MVC / 規約)」

📚 シリーズ目次はこちら → 「今さら学ぶ」シリーズ — はじめに
🗺️ KnowledgeNoteの設計を確認 → 設計マップ

この章でわかること

  • Railsの最大の特徴「レールが敷いてある」とはどういうことか
  • MVCの責務分担を「レストラン」で理解する
  • CoC(設定より規約)— 暗黙のルールで手間を省く仕組み
  • DRY(Don't Repeat Yourself)— 同じことを繰り返さない原則
  • KnowledgeNoteのディレクトリ構造で見る「Railsの規約」
  • Rackとリクエストライフサイクル — HTTPリクエストがコントローラに届くまでの全体フロー

🏠 たとえ話で掴む「Railsの世界」

Ruby on Rails の最大の魅力を一言でいうと、 「レールが敷いてある安心感」 です。

鉄道旅行を想像してください。自分で道を切り開いて目的地に行くこともできますが、線路に乗ってしまえば、迷わずに到着できます。途中の分岐も、駅員さん(Railsの規約)が「こっちです」と案内してくれます。

鉄道旅行 Rails
線路が敷いてある ファイルの置き場所・名前の付け方が決まっている
駅員が案内してくれる エラーメッセージが「こうすればいいよ」と教えてくれる
切符を買えば乗れる rails new で必要なものが揃う
指定席に座る MVC(役割分担)が決まっている
時刻表通りに走る リクエスト→レスポンスの流れが決まっている

レールから外れた独自のやり方もできますが、 レールに乗っている限り、最小限の設定でアプリが動く。これがRailsの思想です。


Rails / MVC とは何か — 技術的な定義

Railsというフレームワーク

Ruby on Rails は、Rubyで書かれたWebアプリケーションフレームワークです。2004年にDavid Heinemeier Hansson(DHH)がBasecampの開発から抽出して公開しました。

フレームワークとは、アプリケーション開発に必要な基盤(ルーティング、DB接続、テンプレートエンジン、セキュリティ対策など)をあらかじめ用意したものです。開発者はフレームワークの規約に従ってコードを書くことで、共通の基盤部分を自分で作る必要がなくなります。

Railsが重視する設計思想は2つあります。 CoC(Convention over Configuration / 設定より規約)DRY(Don't Repeat Yourself / 同じことを繰り返さない) です。この2つがRailsの生産性を支えています。

MVCアーキテクチャ

MVC(Model-View-Controller) は、アプリケーションの責務を3つの層に分離するアーキテクチャパターンです。1979年にSmalltalkerのTrygve Reenskaugが考案し、現在ではWebフレームワークの標準的な設計になっています。

Model はアプリケーションのビジネスロジックとデータの永続化を担います。RailsではActiveRecordがこの役割を果たし、DBとのやり取り、バリデーション、データ間の関連付けを行います。

View はユーザーに見せる画面の生成を担います。RailsではERBテンプレートがHTMLを組み立てます。

Controller はリクエストの受付とレスポンスの決定を担います。ユーザーからのリクエストを受け取り、Modelに処理を依頼し、結果をViewに渡して画面を返します。

MVCの核心は 責務の分離 です。各層が自分の仕事だけに集中することで、コードの見通しが良くなり、変更時の影響範囲を限定できます。


🍽️ MVC — レストランの役割分担

第0章で「宅配ピザ」のたとえを使いましたが、MVCの理解をもう一段深めるために、今度は レストラン にたとえます。

3人の役割

役割 MVC 仕事内容
ホールスタッフ Controller お客さん(リクエスト)の注文を聞いて、キッチンに伝える。料理が出来たら運ぶ
シェフ Model 材料(DB)を使って料理(データ加工)する。レシピ(ビジネスロジック)を知っている
メニュー表・食器 View 料理の見せ方(HTML)を担当。盛り付けや器の選択

大事なルール:役割を侵さない

レストランがうまく回るのは、 各自が自分の仕事に集中している からです。

✅ 正しい分担
  ホール「注文を受けて、キッチンに伝えます」
  シェフ「材料を取り出して、調理します」
  メニュー表「料理の見た目を整えます」

❌ ダメな分担(Fat Controller)
  ホール「注文を受けて、自分で冷蔵庫を開けて、
          料理も自分で作って、盛り付けも自分でやって、
          ついでにお会計もやります」
  → ホールスタッフが過労で倒れる

ホールスタッフ(Controller)が料理(ビジネスロジック)まで全部やってしまう状態を Fat Controller と呼びます。これはRailsでよくあるアンチパターンです(→ 第25章で詳しく扱います)。

MVCの流れを具体的に

KnowledgeNoteで「記事一覧を表示する」場合の流れです。

# 1. ルーティング — 注文を受け付ける窓口
# config/routes.rb
resources :articles   # GET /articles → articles#index

# 2. コントローラ — ホールスタッフ(注文を受けてキッチンに伝える)
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def index
    @articles = Article.published.recent.page(params[:page])
    # ↑ Modelに「公開済みの記事を新しい順で持ってきて」と頼む
    # ↑ 自分(Controller)は料理しない!
  end
end

# 3. モデル — シェフ(データを取得・加工する)
# app/models/article.rb
class Article < ApplicationRecord
  scope :published, -> { where(published: true) }
  scope :recent,    -> { order(created_at: :desc) }

  def author_name
    user.display_name
  end
end

# 4. ビュー — メニュー表(見た目を整える)
# app/views/articles/index.html.erb
# <% @articles.each do |article| %>
#   <h2><%= article.title %></h2>
#   <p>by <%= article.author_name %></p>
# <% end %>

この流れが身についてくると、「次のコードはどこに書けばいいか」の判断が自然とできるようになります。


📏 CoC(Convention over Configuration)— 設定より規約

CoC(設定より規約) は、Railsの根幹にある哲学です。「みんなが同じルール(規約)に従えば、設定ファイルをいちいち書かなくて済む」という考え方です。

具体的な規約の例

# ① テーブル名とモデル名の規約
#    モデル名(単数形・キャメルケース)→ テーブル名(複数形・スネークケース)
class Article < ApplicationRecord   # → テーブル名は「articles」
end
class UserRole < ApplicationRecord  # → テーブル名は「user_roles」
end

# ② コントローラ名とビューの規約
#    ArticlesController#show → app/views/articles/show.html.erb を自動で探す

# ③ 主キーの規約
#    全テーブルの主キーは「id」(自動採番)

# ④ 外部キーの規約
#    「テーブル名の単数形_id」(例:user_id, article_id)

これらの規約に従っている限り、設定ファイルに「このモデルはこのテーブルに対応する」と書く必要がありません。 Railsが自動で判断してくれます

規約に従わないとどうなるか

規約から外れることもできますが、その分だけ設定が必要になります。

# 規約に従う場合 — 設定不要
class Article < ApplicationRecord
  # テーブル名「articles」、主キー「id」、外部キー「user_id」
  # → 全て自動で認識される。何も書かなくていい!
end

# 規約から外れる場合 — 設定が必要
class Article < ApplicationRecord
  self.table_name = "blog_posts"      # テーブル名が規約と違う
  self.primary_key = "article_id"     # 主キーが規約と違う
  belongs_to :author, class_name: "User", foreign_key: "writer_id"
  # 外部キーが規約と違うので明示的に指定が必要
end

上のコードを見れば、規約に従うことでどれだけコードが減るかが一目瞭然です。

Railsの命名規約一覧

種類 規約
モデル 単数形・キャメルケース Article, UserRole
テーブル 複数形・スネークケース articles, user_roles
コントローラ 複数形・キャメルケース + Controller ArticlesController
コントローラファイル 複数形・スネークケース articles_controller.rb
ビュー app/views/コントローラ名/アクション名.html.erb app/views/articles/show.html.erb
主キー id articles.id
外部キー テーブル名の単数形_id articles.user_id
マイグレーション 日時_説明 20250215120000_create_articles.rb

🔄 DRY(Don't Repeat Yourself)— 同じことを繰り返さない

DRY は「同じコードを2箇所以上に書かない」という原則です。Railsはこの原則を強く推奨しています。

DRY違反の例

# ❌ DRY違反 — 同じコードが2箇所にある
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    if @article.user != current_user
      redirect_to root_path, alert: "権限がありません"
    end
  end

  def edit
    @article = Article.find(params[:id])
    if @article.user != current_user
      redirect_to root_path, alert: "権限がありません"    # ← 同じコード!
    end
  end

  def update
    @article = Article.find(params[:id])
    if @article.user != current_user
      redirect_to root_path, alert: "権限がありません"    # ← また同じ!
    end
    # ...
  end
end

DRYに書き直す

# ✅ DRY — before_action で共通処理をまとめる
class ArticlesController < ApplicationController
  before_action :set_article, only: [:show, :edit, :update, :destroy]
  before_action :authorize_user!, only: [:edit, :update, :destroy]

  def show
    # @articleはbefore_actionでセット済み
  end

  def edit
    # @articleもauthorizeもbefore_actionで済んでいる
  end

  def update
    if @article.update(article_params)
      redirect_to @article, notice: "記事を更新しました"
    else
      render :edit, status: :unprocessable_entity
    end
  end

  private

  def set_article
    @article = Article.find(params[:id])
  end

  def authorize_user!
    unless @article.user == current_user
      redirect_to root_path, alert: "権限がありません"
    end
  end

  def article_params
    params.expect(article: [:title, :body, :published])
  end
end

before_action で共通処理をまとめることで、同じコードの繰り返しがなくなりました。修正が必要になっても1箇所直すだけで済みます(→ 第14章で詳しく扱います)。


📁 Railsのディレクトリ構造

規約の話をしたところで、Railsプロジェクトのディレクトリ構造を確認します。「どこに何を置くか」も規約で決まっています。

knowledgenote/
├── app/                        ← アプリの本体(99%ここを触る)
│   ├── controllers/            ← コントローラ(ホールスタッフ)
│   │   ├── application_controller.rb
│   │   ├── articles_controller.rb
│   │   └── users_controller.rb
│   ├── models/                 ← モデル(シェフ)
│   │   ├── application_record.rb
│   │   ├── article.rb
│   │   └── user.rb
│   ├── views/                  ← ビュー(メニュー表)
│   │   ├── layouts/
│   │   │   └── application.html.erb
│   │   ├── articles/
│   │   │   ├── index.html.erb
│   │   │   └── show.html.erb
│   │   └── users/
│   ├── helpers/                ← ビュー用のヘルパーメソッド
│   ├── javascript/             ← JavaScript(Stimulus等)
│   └── assets/                 ← CSS、画像
│       └── stylesheets/
│
├── config/                     ← 設定ファイル
│   ├── routes.rb               ← ルーティング(重要!)
│   ├── database.yml            ← DB接続設定
│   └── environments/           ← 環境別設定
│
├── db/                         ← データベース関連
│   ├── migrate/                ← マイグレーションファイル
│   ├── schema.rb               ← 現在のDB構造
│   └── seeds.rb                ← 初期データ
│
├── spec/                       ← テスト(RSpec)
│   ├── models/
│   ├── controllers/
│   └── factories/
│
├── Gemfile                     ← Gem一覧(→ [第8章](https://qiita.com/harapeco-mgn/items/8d3f1959402ee69665ee))
├── Gemfile.lock                ← Gemバージョンの記録
└── README.md

ファイルの置き場所で迷ったら、この構造に従えば間違いありません。Railsの規約に乗っている限り、場所を間違えることはほとんどありません。


🌐 Rackとリクエストライフサイクル — HTTPリクエストが届くまで

MVCの「Controller → Model → View」の流れを学びましたが、 ブラウザからのリクエストがControllerに届くまでにも、いくつかの層を通っています

Rackとは

Rack は、RubyのWebサーバとWebフレームワークをつなぐインターフェースの仕様です。「どんなWebサーバ(Puma等)でも、どんなフレームワーク(Rails、Sinatra等)でも、Rackの仕様に従って通信できる」ようにする共通規約です。

リクエストの全体フロー

ブラウザ(GET /articles/1)
  ↓
Puma(Webサーバ)
  │ TCPソケットでHTTP通信を受け付ける
  ↓
Rack
  │ HTTPリクエストを Rails が読める env ハッシュに変換して渡す
  ↓
ミドルウェアスタック(複数のフィルター層)
  │ ・RequestLogger   → リクエストをログに記録
  │ ・Session         → Cookieからセッション情報を復元
  │ ・CSRF Protection → トークンを検証
  │ ・SSL Enforcement → HTTP → HTTPS リダイレクト
  ↓
Rails Router(routes.rb)
  │ URLとHTTPメソッドからコントローラ・アクションを決定
  │ GET /articles/1 → ArticlesController#show
  ↓
ApplicationController → ArticlesController#show
  │ ① before_action を実行(認証チェック等)
  │ ② Article.find(params[:id]) でModelに問い合わせ
  │      └ Model(ActiveRecord): SELECT * FROM articles WHERE id = 1
  │      └ クエリ結果を @article として Controller が受け取る
  │ ③ render :show を実行(@article をインスタンス変数でViewに渡す)
  ↓
View(ERBテンプレート)
  │ @article を使ってHTMLを組み立てる
  ↓
レスポンス(HTML)をブラウザへ返す

ミドルウェアスタックを確認する

rails middleware コマンドで、現在のミドルウェアの一覧が確認できます。

$ rails middleware
use ActionDispatch::HostAuthorization
use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ContentSecurityPolicy::Middleware
run KnowledgeNote::Application.routes

上から順にリクエストが通過します。最後の routes に到達するころには、セッション復元・CSRF検証・ロギングが全て完了しています。

before_action とミドルウェアの違い

どちらも「アクションの前に処理を実行する」機能ですが、役割が異なります。

ミドルウェア before_action
適用範囲 アプリ全体(全リクエスト) コントローラ単位
書く場所 config/application.rb コントローラ内
典型的な用途 セッション管理、ロギング、SSL強制 認証チェック、リソース取得
Railsの変数(@article等) 使えない 使える

コントローラで使う before_action :authenticate_user! は、Rackミドルウェアスタックを通過した後のアプリ層で実行されます。


🛠️ KnowledgeNoteでの具体例

KnowledgeNoteの「記事の作成→保存→表示」の流れで、MVCの連携を確認します。

# config/routes.rb — ルーティング(どの窓口に案内するか)
resources :articles
# POST /articles → ArticlesController#create
# app/controllers/articles_controller.rb — コントローラ(ホールスタッフ)
class ArticlesController < ApplicationController
  def new
    @article = Article.new   # 空のフォーム用オブジェクト
  end

  def create
    @article = current_user.articles.build(article_params)

    if @article.save          # モデルに「保存して」と頼む
      redirect_to @article, notice: "記事を投稿しました"
    else
      render :new, status: :unprocessable_entity
      # バリデーションエラー → 作成フォームを再表示
    end
  end

  private

  def article_params
    params.expect(article: [:title, :body, :published])
  end
end
# app/models/article.rb — モデル(シェフ)
class Article < ApplicationRecord
  belongs_to :user                # 著者との関連付け
  validates :title, presence: true, length: { maximum: 100 }
  validates :body, presence: true

  scope :published, -> { where(published: true) }
end
<!-- app/views/articles/new.html.erb — ビュー(見た目) -->
<h1 class="text-2xl font-bold mb-6">記事を書く</h1>

<%= form_with(model: @article, class: "space-y-4") do |f| %>
  <% if @article.errors.any? %>
    <div class="bg-red-50 text-red-700 p-4 rounded">
      <ul>
        <% @article.errors.full_messages.each do |message| %>
          <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div>
    <%= f.label :title, "タイトル", class: "block font-bold mb-1" %>
    <%= f.text_field :title, class: "w-full border rounded p-2" %>
  </div>

  <div>
    <%= f.label :body, "本文", class: "block font-bold mb-1" %>
    <%= f.text_area :body, rows: 10, class: "w-full border rounded p-2" %>
  </div>

  <%= f.submit "投稿する", class: "bg-blue-600 text-white px-6 py-2 rounded" %>
<% end %>

ポイントは、 各ファイルが自分の仕事だけをしている ことです。コントローラは「保存を頼む→結果で分岐」だけ。モデルは「バリデーション+データ管理」だけ。ビューは「画面の見た目」だけ。バリデーションの詳細は(→ 第12章で詳しく扱います)。


💼 面接で聞かれたら?

Q:RailsのCoCとDRYについて説明してください。

「CoCは『設定より規約』という原則で、命名規則やファイルの配置を規約に従って書くことで、設定ファイルを最小限にできる仕組みです。たとえばArticleモデルはarticlesテーブルと自動で紐づきます。DRYは『同じことを繰り返さない』原則で、共通の処理をbefore_actionやヘルパーメソッドにまとめることで、修正が1箇所で済むようにする考え方です。」

深掘りされたら:

  • 「MVCの各層の責務は?」→ Modelはデータの取得・加工・バリデーション。Viewは画面の組み立て(HTML生成)。Controllerはリクエストの受付とModelへの指示、レスポンスの決定。
  • 「規約に従わないとどうなる?」→ 動かなくなるわけではないが、明示的な設定が必要になる。self.table_nameclass_name の指定など、コード量が増える。

🔗 もっと深く知りたい人へ(1次情報リンク)


まとめ

  • ✅ Railsは「レールが敷いてある」フレームワーク。規約に従えば最小限のコードでアプリが動く
  • ✅ MVCはレストランの役割分担。Controller=ホール、Model=シェフ、View=メニュー表
  • ✅ CoC(設定より規約)により、命名やファイル配置を規約に沿わせるだけで設定不要
  • ✅ DRY(同じことを繰り返さない)で、共通処理をまとめてメンテナンスしやすくする
  • ✅ 「どこに何を書くか」で迷ったら、Railsのディレクトリ規約に従えばOK
  • ✅ HTTPリクエストはPuma→Rack→ミドルウェアスタック→Router→Controller→Viewの順で処理される
  • ✅ ミドルウェアはアプリ全体に適用、before_actionはコントローラ単位。役割が違う

📚 シリーズ目次:「今さら学ぶ」シリーズ — はじめに

4
0
1

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
4
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?