Let's talk about design patterns. The term sounds so cool and computer-sciencey and can be used to impress your friends and colleagues at parties. But all it really means to implement a design pattern is to write some code in a way that lots of other software developers have done before, in order to solve a common problem. Do a Google search for "software design pattern", and we'll find dozens of patterns that pro developers use every day.

Shockingly! – we are not the first software developers on Earth to be writing a dynamic web application backed by a database. In fact, it's one of the more common types of applications that web developers want to build. And there is a design pattern that's been used time and time again, by millions of developers, in order to do so – the Model-View-Controller (MVC) design pattern.

The MVC pattern, in its most basic form, says that we should separate our application into three layers, with the code for each layer in their own files and folders. These layers are:

  • Models – code that talks directly to our database
  • Views – code that creates what the user sees in the browser, like HTML and its cousin ERB
  • Controllers – code that connects and controls traffic between the models and views

Of course, we've already been introduced to models and views in previous lessons. And we've also seen that Ruby on Rails implements the MVC design pattern right out of the box, with the separate app/models, app/views, and app/controllers directories that come with Rails by default.

So what is the controller and how does it fit into the equation? After all, we've created working applications already, without the controller – what is it and why do we need it? Let's just dive right in and implement one, then we'll discuss why the controller is important – it's actually quite straightforward. Let's begin with our dice resource from last time:

<%
  die1 = rand(1..6)
  die2 = rand(1..6)
  total = die1 + die2
%>
<h1>Roll the dice</h1>
<p>
  <img src="/images/dice/<%= die1 %>.svg">
  <img src="/images/dice/<%= die2 %>.svg">
</p>
<p>
  The total is <%= total %>
</p>
<p>
  <a href="/dice">Roll Again</a>
</p>
app/views/dice/index.html.erb

Let's make just a few subtle changes, including moving those first three lines of Ruby out of the view and into the DiceController, which was automatically created when we did rails generate controller dice earlier.

class DiceController < ApplicationController

  def index
    @die1 = rand(1..6)
    @die2 = rand(1..6)
    @total = @die1 + @die2
  end

end
app/controllers/dice_controller.rb
<h1>Roll the dice</h1>
<p>
  <img src="/images/dice/<%= @die1 %>.svg">
  <img src="/images/dice/<%= @die2 %>.svg">
</p>
<p>
  The total is <%= @total %>
</p>
<p>
  <a href="/dice">Roll Again</a>
</p>
app/views/dice/index.html.erb

There are quite a few new concepts happening here – let's just take it one at a time!

  • We've opened up the source code for our DiceController, which is a Ruby class. Classes in Ruby are used to encapsulate functionality, as we've previously seen with model classes. Controller classes do a different job than model class though – while model classes are used to communicate with the database, controller classes are used as the glue between views and the rest of our code.
  • Each method of the controller class, i.e. the code that begins with def and concludes with end, is known as an action. We'll notice that the name of each action corresponds to the name of each view. In this example, we have a view called index.html.erb and an action method called index.
  • We've moved the code that previously lived at the top of our index.html.erb view into this new controller/action – but we need to use variables like die1 and die2 that are shared between the two files. In order to share variables between the controller and the view, the variable must be prefixed with the @ symbol. So we've renamed die1 and die2 to @die1 and @die2.

But why do this? Why not leave well enough alone? It seems like we're adding more complexity to do the same thing we did before! Seems like MV was just fine; no need for MVC. These are valid points – but there are benefits to fully implementing the MVC pattern, even in this tiny example:

  • Our view is now free and clean of extraneous Ruby code. All of the application logic needed to supply the view with data is now in the controller, and there are only tiny sprinkles of ERB – only where absolutely needed to dynamically embed data in our HTML.
  • Coupling our Ruby-based logic with the view just gets plain ugly as our applications become more complex. It's only three lines today, but imagine 100 lines of code at the top of our ERB file.
  • Web applications may need to produce other types of views. Imagine a time when our application might produce other types of output, like a JSON-based API, a PDF, or an Excel spreadsheet. Or perhaps we want different HTML views for mobile and desktop, each sharing the same logic and data. Moving the code that generates this data to the controller layer allows for this flexibility.