Skinny Everything (Part 1 of 3)
At Revelry, we build web apps using Rails. Working with any MVC framework, you will come across the saying “Fat model, Skinny Controller”. The idea is to slim the the controller down to basic CRUD actions, and put the rest of the more complex functionality in the model. Now, while it is definitely good to have a slim controller for sake of code reusability, readability, and testing, it is also good to have a skinny model (for the exact same reasons). In fact, it’s just good to have a skinny everything.
What tends to happen when you move all logic into models is that you get God like Models which become impossible to understand and maintain. Jon Cairns also wrote a great article about why Fat model, Skinny Controller is a load of rubbish. Unfortunately, Cairns doesn’t list any of the ways to keep everything skinny. So, I have decided to write a three-part article that will explain some of the tactics we use at Revelry to maintain “Skinny Everything” in our Rails applications.
The rest of this article will cover the first of these tacticts:
Sometimes you need a way to implement display logic on an object. For example, you have an Event object. In your view you want to call
@event.start_date, and have it be a correctly formatted date (because Rails doesn’t store dates the way humans like to look at them). You might think it would make sense to just add a method to the model:
That seems like a good idea, because then you will be able to call
@event.start_date everywhere that you have an Event object. However, according to MVC, the model isn’t supposed to deal with presentation; that’s meant to happen primarily in the controller. Not to mention, display methods like this can add up, leading to fat models. You might consider writing a helper method that formats the date for you. That is probably a bad idea.
Instead, the best option is to use a decorator. At Revelry, we use Draper for decorators. Here’s an example of solving the above issue with a with Draper decorator:
Then in the controller, you decorate the
@event object with
EventDecorator.decorate(@event), which returns the decorated
@event object. So now you have the start_date method accessible via the decorated object.
It’s important to remember that you need to decorate the event object in order to get the decorator methods. It might seem obvious, but consider this situation:
Before you knew it was a bad idea, you put a display logic based method in a model. Now that you know better, you decide you want to move it into a decorator. Smart. Thing is, if that method was already being called all over the application, you’re gonna need to make sure that all instances of that model are getting decorated before the new decorator method is called. When it was just a model method, you could call it on any instances of the at model, but with a decorator, you can only call it on a decorated instance object.
Decorators are great because they keep display logic out of the models, and they keep your models skinnier (without resorting to using helpers).