はじめに
前回の記事では、Web技術の歴史とRackの正体について学びました。
今回は、Rails内部のMVCアーキテクチャについて、データがどのように流れるのかを徹底的に解説します。
「なんとなく動いている」状態から「完全に理解している」状態へステップアップしましょう!
対象読者
- Railsを使い始めたばかりの方
- Model、View、Controllerの役割が曖昧な方
- クライアントからレスポンスまでの完全な流れを知りたい方
MVCアーキテクチャとは
MVCは、アプリケーションを3つの役割に分ける設計パターンです。
- Model:データ管理
- View:画面表示
- Controller:処理の制御
なぜMVCで分けるのか?
分けない場合の問題:
# 全部一緒くた(カオス!)
def show_users
# データベース接続
conn = Database.connect
# SQL実行
users = conn.execute("SELECT * FROM users")
# HTML生成
html = "<h1>ユーザー一覧</h1>"
users.each do |user|
html += "<p>#{user['name']}</p>"
end
# レスポンス返却
return html
end
問題点:
- 変更が大変(HTMLを変えるだけでもSQL周辺のコードを触る)
- テストしにくい
- チームで分業できない
MVCで分けた場合:
# Controller(司令塔)
def index
@users = User.all # Modelに依頼
# Viewに渡す
end
# Model(データ管理)
class User < ApplicationRecord
# データベース操作
end
# View(画面表示)
# app/views/users/index.html.erb
<h1>ユーザー一覧</h1>
<% @users.each do |user| %>
<p><%= user.name %></p>
<% end %>
メリット:
- 役割が明確
- 変更しやすい
- テストしやすい
- チームで分業できる
完全なデータフロー
クライアントからクライアントまでの旅
ユーザーがブラウザで「ユーザー一覧」ページを開くときの完全な流れを見ていきましょう。
クライアント(ブラウザ)
↓ HTTPリクエスト
Webサーバー(Nginx/Apache)
↓
Rackインターフェース
↓
【Railsの世界に入る】
↓
Controller(司令塔)
↓ データ取得依頼
Model(データ管理者)
↓ SQL実行
Database(MySQL/PostgreSQL)
↓ データ返却
Model
↓ データを返す
Controller
↓ データを渡してHTML生成依頼
View(デザイナー)
↓ 生成したHTMLを返す
Controller
↓
【Railsの世界から出る】
↓
Rackインターフェース
↓
Webサーバー
↓ HTTPレスポンス
クライアント(ブラウザに表示)
ポイント
- Controllerがハブ(中心) になっている
- 行きも帰りも同じ道を通る
- ViewもModelもControllerを経由する
- Controllerは常に処理の流れを制御している
MVCの各役割を詳しく解説
Controller(司令塔)
役割:
- リクエストを最初に受け取る
- 「何をすべきか」を判断
- ModelとViewに指示を出す
具体例:
# app/controllers/users_controller.rb
class UsersController < ApplicationController
# ユーザー一覧を表示
def index
@users = User.all # Modelに「全ユーザー取得して」と依頼
# 自動的にapp/views/users/index.html.erbを呼ぶ
end
# 特定のユーザーを表示
def show
@user = User.find(params[:id]) # IDで1人取得
end
# 新規ユーザー作成
def create
@user = User.new(user_params)
if @user.save # Modelに保存を依頼
redirect_to @user # 成功したらユーザーページへ
else
render :new # 失敗したら入力画面に戻る
end
end
private
def user_params
params.require(:user).permit(:name, :email)
end
end
Controllerの責務:
- ✅ リクエストパラメータの取得
- ✅ Modelへのデータ操作依頼
- ✅ Viewへのデータ受け渡し
- ✅ リダイレクトやレンダリングの制御
- ❌ SQL を直接書く(Modelの仕事)
- ❌ HTMLを直接生成(Viewの仕事)
Model(データ管理者)
役割:
- データベースとのやり取り
- ビジネスロジックの実装
- データの検証(バリデーション)
具体例:
# app/models/user.rb
class User < ApplicationRecord
# バリデーション
validates :name, presence: true
validates :email, presence: true, uniqueness: true
# 関連付け
has_many :posts
has_many :comments
# ビジネスロジック
def full_name
"#{first_name} #{last_name}"
end
# スコープ(よく使う検索条件)
scope :active, -> { where(active: true) }
scope :recent, -> { order(created_at: :desc) }
end
よく使うModelの操作:
# 全件取得
User.all
# 条件検索
User.where(age: 20)
User.where("age > ?", 20)
# 1件取得
User.find(1) # IDで検索
User.find_by(email: "test@example.com") # 条件で検索
# 新規作成
user = User.new(name: "太郎", email: "taro@example.com")
user.save
# または
User.create(name: "太郎", email: "taro@example.com")
# 更新
user = User.find(1)
user.update(name: "新しい名前")
# 削除
user = User.find(1)
user.destroy
Modelの責務:
- ✅ データベース操作
- ✅ データの検証
- ✅ ビジネスロジック
- ✅ 関連付け(has_many等)
- ❌ HTTPリクエストの処理(Controllerの仕事)
- ❌ HTMLの生成(Viewの仕事)
View(デザイナー)
役割:
- HTMLの生成
- ユーザーに見せる画面を作る
- Controllerから渡されたデータを表示
具体例:
<!-- app/views/users/index.html.erb -->
<h1>ユーザー一覧</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>名前</th>
<th>メール</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.id %></td>
<td><%= user.name %></td>
<td><%= user.email %></td>
<td>
<%= link_to '詳細', user_path(user) %>
<%= link_to '編集', edit_user_path(user) %>
<%= link_to '削除', user_path(user), method: :delete,
data: { confirm: '本当に削除しますか?' } %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= link_to '新規作成', new_user_path, class: 'btn btn-primary' %>
ERBの書き方:
<%# コメント(画面に表示されない) %>
<% # Rubyコードを実行(画面に表示しない) %>
<% @users.each do |user| %>
...
<% end %>
<%= # Rubyコードを実行して結果を表示 %>
<%= user.name %>
<%== # HTMLエスケープせずに表示(XSS注意!) %>
<%== user.bio %>
Viewの責務:
- ✅ HTMLの生成
- ✅ データの表示
- ✅ フォームの作成
- ✅ リンクの生成
- ❌ データベース操作(Modelの仕事)
- ❌ 複雑なロジック(Controllerの仕事)
実例で理解する:ユーザー一覧の表示
ステップ1:ルーティング設定
# config/routes.rb
Rails.application.routes.draw do
resources :users # RESTfulなルートを自動生成
end
これで以下のルートが自動生成されます:
| HTTPメソッド | パス | Controller#Action | 用途 |
|---|---|---|---|
| GET | /users | users#index | 一覧 |
| GET | /users/:id | users#show | 詳細 |
| GET | /users/new | users#new | 新規作成画面 |
| POST | /users | users#create | 作成実行 |
| GET | /users/:id/edit | users#edit | 編集画面 |
| PATCH/PUT | /users/:id | users#update | 更新実行 |
| DELETE | /users/:id | users#destroy | 削除 |
ステップ2:Controllerで処理
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def index
# Step 1: Modelに全ユーザー取得を依頼
@users = User.all
# Step 2: 自動的にapp/views/users/index.html.erbが呼ばれる
end
end
ステップ3:Modelがデータ取得
# app/models/user.rb
class User < ApplicationRecord
# User.allが呼ばれると、ActiveRecordが自動的にSQLを生成
# SELECT * FROM users;
end
ステップ4:Viewで表示
<!-- app/views/users/index.html.erb -->
<h1>ユーザー一覧</h1>
<% @users.each do |user| %>
<div class="user-card">
<h2><%= user.name %></h2>
<p><%= user.email %></p>
</div>
<% end %>
完全な流れの再確認
1. ブラウザ:http://localhost:3000/users にアクセス
↓
2. ルーティング:「GET /users は UsersController#index だな」
↓
3. Controller:「User.allでデータ取得しよう」
↓
4. Model:「SELECT * FROM users; を実行」
↓
5. Database:データを返す
↓
6. Model:Rubyオブジェクトに変換してControllerに返す
↓
7. Controller:「@users = [データ] をViewに渡す」
↓
8. View:「@usersを使ってHTMLを生成」
↓
9. Controller:生成されたHTMLを受け取る
↓
10. Rack → Webサーバー → ブラウザへレスポンス
インスタンス変数(@users)の役割
Controllerから Viewへデータを渡すときに使うのがインスタンス変数です。
# Controller
def index
@users = User.all # @をつける
end
<!-- View -->
<% @users.each do |user| %> <!-- @usersにアクセスできる -->
<%= user.name %>
<% end %>
なぜ@をつけるのか?
- Controllerのインスタンス変数がViewで自動的に使える仕組み
- Railsの「魔法」の一つ
ローカル変数だとどうなる?
# Controller
def index
users = User.all # @なし
end
<!-- View -->
<% users.each do |user| %> <!-- エラー!usersが見つからない -->
<%= user.name %>
<% end %>
よくある質問
Q1: ViewからModelに直接アクセスできる?
技術的には可能ですが、やるべきではありません。
<!-- 悪い例 -->
<% User.all.each do |user| %> <!-- ViewがModelを直接呼んでいる -->
<%= user.name %>
<% end %>
問題点:
- Viewに処理が漏れる
- テストしにくい
- 複雑なクエリをViewに書くことになる
正しい方法:
# Controller
def index
@users = User.all
end
<!-- View -->
<% @users.each do |user| %>
<%= user.name %>
<% end %>
Q2: ControllerでHTMLを生成できる?
技術的には可能ですが、やるべきではありません。
# 悪い例
def index
@users = User.all
html = "<h1>ユーザー一覧</h1>"
@users.each do |user|
html += "<p>#{user.name}</p>"
end
render html: html.html_safe
end
問題点:
- Controllerが肥大化
- HTMLの変更が困難
- デザイナーが作業できない
正しい方法:
ControllerはViewに任せる。
Q3: 複雑な処理はどこに書く?
シンプルな判断基準:
| 処理内容 | 場所 |
|---|---|
| データベース操作 | Model |
| リクエスト処理、フロー制御 | Controller |
| HTML生成、表示ロジック | View |
| 複雑なビジネスロジック | Model(またはService Object) |
| 複雑な表示ロジック | Helper(または View Object) |
まとめ
MVCの3つの役割
-
Controller(司令塔)
- リクエストを受け取る
- ModelとViewに指示を出す
- 処理の流れを制御
-
Model(データ管理者)
- データベース操作
- ビジネスロジック
- データの検証
-
View(デザイナー)
- HTMLの生成
- データの表示
- ユーザーインターフェース
データの流れ(完全版)
クライアント
↓
Webサーバー
↓
Rackインターフェース
↓
Controller
↓
Model
↓
Controller
↓
View
↓
Controller
↓
Rackインターフェース
↓
Webサーバー
↓
クライアント
重要ポイント:
- Controllerが常にハブになる
- 行きも帰りも同じ道を通る
- 各層が明確に役割分担している
なぜMVCを理解すべきか
- エラーが出たときに「どこで起きているか」が分かる
- コードをどこに書くべきか迷わなくなる
- Railsの規約に沿った開発ができる
- チームで開発しやすくなる
次のステップ
次の記事では、HTTPリクエスト/レスポンスの実際の中身とActiveRecordの詳細を学びます!
- 【記事1】Web技術の歴史とRackの正体を理解する
- 【記事3】HTTPリクエスト/レスポンスとActiveRecordを理解する(準備中)
参考資料
この記事が役に立ったら、ぜひいいねやストックをお願いします!
質問やフィードバックもお気軽にコメントください 😊