So What, Exactly, Is the Purpose of a Rails Controller?

a jumble of wires

We've all seen a Rails controller that looked like this.

Have you ever hesitated when trying to refactor a controller for simplicity? Sure, you know how to write a controller so that it “works”. You even know how to organize your controllers in a resource-oriented, RESTful way. But when it comes to understanding the purpose of controllers, they’ve always seemed a bit fuzzy. And they’ve always seemed a bit vulnerable to becoming a soupy mixture of different ingredients.

Is a Rails controller a motley crew of leftover code that needs a home? Is it a glob of glue sticking other parts of the app together? Is it a tangled mess hiding inside a wiring closet?

What, exactly, is the purpose of a Rails controller?

The Controller Is a Translator

It turns out that a Rails controller has not one, but two main responsibilities. The first is to act as a translator between the language of HTTP and the language of your application.

The Model Layer Is the Application

To fully understand this, it helps to have a good grasp on what the model layer in a Rails app actually is. It’s not just a collection of classes that interface with the database. It’s the entire brains of your application. All of the domain concepts, logic and behavior live in the model layer.

In fact, I find it useful to sometimes refer to the model layer as “the application”. This emphasizes the fact that everything that your application does happens there. Controllers and views simply serve as an interface to “the application”.

Incoming HTTP

So the controller doesn’t actually do the work; it delegates. It receives an HTTP request (in a convenient Ruby form, of course) and translates it into a command the application can understand.

For example, if a POST request comes in with some parameters, a controller will translate this into the language of the application by creating a new ActiveRecord object and calling save on it.

Often, controllers will try to do too much. But a controller should pretty much only be calling one command on the model layer. It shouldn’t be instantiating two or three different ActiveRecord objects and orchestrating them.

Outgoing HTTP

The controller also translates the results of your application to the language of HTTP. Often this is simply a 200 OK with HTML. Notice that it’s not the responsibility of the controller to construct the HTML, just the HTTP.

Also notice that it’s not the responsibility of a controller to prepare data for a view to consume. That’s the responsibility of a presenter, a missing–but necessary–piece of Rails.

The Controller Is a Security Guard

Mr. T

401, fool.

The second responsibility of a controller is authorization. Based on the incoming HTTP request, it decides whether to pass the action on to the model layer or deny the request.

That’s It

And that’s it. Just two things. Your controller should only be (1) translating between the language of HTTP and the language of your app, or (2) making authorization decisions. Anything else does not belong in a controller.

You can skip to the end and leave a response. Pinging is currently not allowed.

4 Responses to “So What, Exactly, Is the Purpose of a Rails Controller?”

  1. Randall says:

    I say the security guard thing is just a special case. Controllers turn HTTP requests into HTTP responses (period). If the application says that your request is invalid then the appropriate response is something like 401 or 403. It’s the application’s job to say you can’t do that. It’s the controller’s to turn “you can’t do that” into 403.

    –R

    P.S. I don’t buy the model == application concept simply because I’m coming to think that application rules should be divorced from data access. E.g. your application logic should operate on object instances that quack like foo.bar not rows in a database table that have the column bar that happen to be wrapped in an AR class.

  2. techiferous says:

    @Randall,

    I totally get where you’re coming from; authorization can be regarded as a special case of HTTP since it’s baked into the protocol.

    I would still say that it’s the controller’s job to authorize, because the model layer doesn’t know who you are (current_user).

    Also, I agree with your thoughts about divorcing application logic from data access, but I lack good language to clearly describe my thoughts. In my world, the model layer includes not just ActiveRecord classes, but also classes that would encapsulate business logic.

  3. Chris says:

    By definition, the model is where the business logic is, so it seems fine to me to call the model your application.

    The argument on whether to integrate application logic with data access logic is a separate one. If your data access objects also contain application logic then they are your Model too, but if your data access objects are just ‘dumb’ data carrying objects with no logic, then they are not your Model, even if they are ActiveRecord objects.

  4. Peter Jaros says:

    I buy that it’s often appropriate to leave security to the controller, but I think that’s a pragmatic simplification. There’s no reason the controller *needs* that extra responsibility.

    In an app with particularly interesting authentication logic, it makes more sense for other objects to be concerned with authentication. In those cases, the controller asks this authenticated application layer to perform an operation as a particular user. The authenticated layer can either perform the operation or reject it. In either case, it’s then the controller’s job to translate that back into HTTP for the client.

    Regarding the word “model”…

    This word has been stretched and abused by Rails and MVC before it. What part of an application is the “model”? All of it! Everything in an application models *something*.

    What we’re usually referring to is the *domain model*, which models the real-world domain concepts our application is about. You might separate that from a *data model*, which doesn’t have business logic rules, but does impose a model of the information itself. In Rails, that’s usually encoded solely in the database, but you could extend the data model into simple data access objects (perhaps implemented as ActiveRecord “models”) which controllers access through a domain model layer.

Leave a Reply