Fat Model, Skinny Controller
It was my first coding interview. To say I felt “nervous” is a huge understatement. Inside the small conference room, it was just a bit too hot and stuffy with the door closed. I like to think that’s where the light sweat was coming from.
When the coding challenge started, I was thrown for a bit of a curve when they gave me three sheets of blank printer paper and a sharpie. Then they asked me to write something about Fibonacci numbers?
We danced all over the coding spectrum, from regular expressions to the Big O, (this means something completely different in code land…). One that sticks out is:
You’re making an email app using an MVC framework. It’s time to set up the inbox view. Where would you put the logic so the user only sees their inbox messages?
They were testing me on “fat model, skinny controller”. On the surface, this seems like it’s something we need to work at, but once you understand the distinct jobs of the Model, View, and Controller, this happens pretty organically.
To see why the Model ends up fat, we’ll work our way from the inside (data) out.
The Model is our interface with the database. The database lives outside of our app. It doesn’t even speak Ruby, or Rails, or anything close. Databases generally speak SQL.
The Model is our translator, it speaks database. Its job is to move data in to and out of the database. If the logic you’re writing moves data to or from the database, it should go in The Model.
The Controller is the middle man. It directs the flow of data from the models to the view. The Controller knows what data is needed and asks the models for it.
As the data flows in, the controller catches it in nice big buckets, seals them up, and @names them. This makes it easy to pass off to the view.
The View is a template for how things should look. It’s filled with HTML, and fancy styles.
There’s logic about what elements we should see in different situations. For example, if you’re signed in, you should see a log out button, not a sign in button.
The Inbox Question
A knee jerk response to the inbox question is that the logic goes in the view. Logic about what we should see goes in the view, right? Not really.
Let’s follow the request through the app.
I’d like to see all the messages in my inbox, please.
The request gets routed into the controller. The method for the inbox view runs. The messages in your inbox are requested. Should we get all of the messages in the database, or just the ones that are in the inbox?
Databases exist to sort through and return specific data. They’re REALLY fast! We should take advantage of that. It’s much faster and cleaner to have the database give us only the messages in the inbox.
Which part of our app speaks database again?
The model speaks database. Logic that gets specific data from the database goes in the model. If we had buckets for things like inbox, starred, and archive for our email messages, the model would look something like this.
def only_the_inbox_messages Message.where(bucket: 'inbox') end def only_the_starred_messages Message.where(bucket: 'starred') end def only_the_archived_messages Message.where(bucket: 'archive') end
This is going to fill up fast! This model is in charge of getting specific messages for every view of our email app. The logic to specify which messages are returned in different circumstances goes here in the model.
The answer to the inbox question is The Model.
Return Of The Controller
Now that the model has gotten us all of the inbox messages, the controller can package them in a bucket and @name them. The controller method would look something like this:
def inbox @messages = only_the_inbox_messages end
The beautiful and simple @messages variable will be passed on to the…
Now that everything is all set up, the only thing we need in the view is to render the messages that were returned to us.
<% @messages.each do |message| %> <%= ... %> <% end %>
No logic, just a quick iteration through the messages to put each one on the screen and we’re done.
Why Does The Model Get Fat???
When we let each part of the MVC framework do what it was born to do, the controller calls model methods and saves what’s returned to @instance_variables.
The view is there to format and put make up on the data that’s passed to it. The only data that should go to the make up room is data that’s about to go on stage.
Methods that get data should be defined in the model. Logic to get specific data can get complex. Because of this alone, the model tends to grow MUCH faster than the controller. When the different parts of our MVC framework are used the way they’re designed, the model usually gets fat all on it’s own.