Skip
Arish's avatar

16. Understanding MVC Architecture


The MVC Architecture

MVC (Model-View-Controller) is the architectural pattern that Rails is built upon. Understanding MVC is essential for building well-organized Rails applications.

What is MVC?

MVC separates your application into three interconnected components:

  • Model: Handles data and business logic
  • View: Displays information to the user
  • Controller: Coordinates between Model and View

How MVC Works in Rails

┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ Browser │────▶│ Controller │────▶│ Model │ │ Request │ │ │ │ │ └─────────────┘ └──────────────┘ └─────────────┘ │ │ │ │ ▼ │ ┌─────────────┐ │ │ View │◀────────────┘ │ │ └─────────────┘ │ ▼ ┌─────────────┐ │ Response │ │ (HTML) │ └─────────────┘

The Request Cycle

Let's trace a request through a Rails app:

1. User Makes a Request

GET /articles/1

2. Router Matches the Request

ruby
1# config/routes.rb
2Rails.application.routes.draw do
3  resources :articles
4end
5
6# This routes GET /articles/1 to ArticlesController#show

3. Controller Receives the Request

ruby
1# app/controllers/articles_controller.rb
2class ArticlesController < ApplicationController
3  def show
4    @article = Article.find(params[:id])
5    # params[:id] is "1" from the URL
6  end
7end

4. Model Fetches Data

ruby
1# app/models/article.rb
2class Article < ApplicationRecord
3  # Active Record handles database queries
4  # Article.find(1) generates:
5  # SELECT * FROM articles WHERE id = 1
6end

5. View Renders the Response

erb
1<!-- app/views/articles/show.html.erb -->
2<article>
3  <h1><%= @article.title %></h1>
4  <p><%= @article.body %></p>
5  <small>Published: <%= @article.created_at.strftime("%B %d, %Y") %></small>
6</article>

6. HTML Response Sent to Browser

html
1<article>
2  <h1>My First Article</h1>
3  <p>This is the content of my article...</p>
4  <small>Published: January 15, 2024</small>
5</article>

Models in Detail

Models handle:

  • Database interactions
  • Data validation
  • Business logic
  • Relationships between data
ruby
1# app/models/article.rb
2class Article < ApplicationRecord
3  # Relationships
4  belongs_to :author
5  has_many :comments
6  
7  # Validations
8  validates :title, presence: true, length: { minimum: 5 }
9  validates :body, presence: true
10  
11  # Scopes (query shortcuts)
12  scope :published, -> { where(published: true) }
13  scope :recent, -> { order(created_at: :desc) }
14  
15  # Business logic
16  def publish!
17    update(published: true, published_at: Time.current)
18  end
19  
20  def reading_time
21    (body.split.size / 200.0).ceil
22  end
23end

Views in Detail

Views handle:

  • Displaying data to users
  • HTML structure
  • Presenting formatted content
erb
1<!-- app/views/articles/index.html.erb -->
2<h1>All Articles</h1>
3
4<% @articles.each do |article| %>
5  <div class="article-card">
6    <h2><%= link_to article.title, article %></h2>
7    <p><%= truncate(article.body, length: 150) %></p>
8    <div class="meta">
9      By <%= article.author.name %>10      <%= article.reading_time %> min read
11    </div>
12  </div>
13<% end %>

Controllers in Detail

Controllers handle:

  • Receiving HTTP requests
  • Processing user input
  • Calling models for data
  • Selecting which view to render
ruby
1# app/controllers/articles_controller.rb
2class ArticlesController < ApplicationController
3  before_action :set_article, only: [:show, :edit, :update, :destroy]
4  before_action :authenticate_user!, except: [:index, :show]
5  
6  # GET /articles
7  def index
8    @articles = Article.published.recent.page(params[:page])
9  end
10  
11  # GET /articles/1
12  def show
13  end
14  
15  # GET /articles/new
16  def new
17    @article = Article.new
18  end
19  
20  # POST /articles
21  def create
22    @article = current_user.articles.build(article_params)
23    
24    if @article.save
25      redirect_to @article, notice: 'Article was created!'
26    else
27      render :new, status: :unprocessable_entity
28    end
29  end
30  
31  # GET /articles/1/edit
32  def edit
33  end
34  
35  # PATCH/PUT /articles/1
36  def update
37    if @article.update(article_params)
38      redirect_to @article, notice: 'Article was updated!'
39    else
40      render :edit, status: :unprocessable_entity
41    end
42  end
43  
44  # DELETE /articles/1
45  def destroy
46    @article.destroy
47    redirect_to articles_url, notice: 'Article was deleted!'
48  end
49  
50  private
51  
52  def set_article
53    @article = Article.find(params[:id])
54  end
55  
56  def article_params
57    params.require(:article).permit(:title, :body, :published)
58  end
59end

The Golden Rules of MVC

1. Fat Models, Skinny Controllers

Put business logic in models, not controllers:

ruby
1# Bad - logic in controller
2class ArticlesController < ApplicationController
3  def publish
4    @article = Article.find(params[:id])
5    @article.published = true
6    @article.published_at = Time.current
7    @article.save
8    UserMailer.article_published(@article).deliver_later
9  end
10end
11
12# Good - logic in model
13class Article < ApplicationRecord
14  def publish!
15    update(published: true, published_at: Time.current)
16    notify_subscribers
17  end
18  
19  private
20  
21  def notify_subscribers
22    UserMailer.article_published(self).deliver_later
23  end
24end
25
26class ArticlesController < ApplicationController
27  def publish
28    @article = Article.find(params[:id])
29    @article.publish!
30  end
31end

2. Don't Put Logic in Views

Views should only display data:

erb
1<!-- Bad - logic in view -->
2<% if article.published && article.published_at < 7.days.ago %>
3  <span class="badge">New!</span>
4<% end %>
5
6<!-- Good - use a helper or model method -->
7<% if article.recent? %>
8  <span class="badge">New!</span>
9<% end %>
ruby
1# In the model
2class Article < ApplicationRecord
3  def recent?
4    published && published_at > 7.days.ago
5  end
6end

3. Controllers Should Be RESTful

Stick to the standard CRUD actions:

ruby
1# Standard RESTful actions
2def index   # GET /articles
3def show    # GET /articles/1
4def new     # GET /articles/new
5def create  # POST /articles
6def edit    # GET /articles/1/edit
7def update  # PATCH /articles/1
8def destroy # DELETE /articles/1

Why MVC Matters

  1. Separation of Concerns: Each component has a single responsibility
  2. Testability: Easy to test each component in isolation
  3. Maintainability: Changes in one area don't affect others
  4. Reusability: Models and views can be reused across controllers
  5. Team Collaboration: Developers can work on different components simultaneously

Understanding MVC is the foundation for everything else in Rails!