You are following the “Fat Models, Skinny Controller” pattern in Rails. Your controllers being the HTTP endpoints are nice and clean. Short action methods and brief assignments. However, your models keep growing and growing, you are losing track of its responsibilities and you are trying to figure out why.

That is a problem. Rails makes you push all your domain code into the persistance layer. Avoiding the oh so dreaded “over-abstraction”, bloating a handful of ActiveRecord classes seems to be a better strategy. Also, the idea of three – and only three – possible places for your business logic makes it easy to justify the growing model: Controllers must be “skinny” and views shouldn’t contain logic, so… if not pushing into the “model”, where else should I put it?

To make it short: Stop thinking in database tables and relations and start modeling your domain with dedicated classes and instances – that is what object-orientation was made for!

What Is My Domain?

If your concern is “I want to export a series of chat messages to a JSON document” then write a new class MessagesDocument. That is your domain. That is the problem you as the programmer are supposed to solve. So do it.

class MessagesDocument
  def to_json(messages)
    messages.collect do |msg|
      msg.to_json
    end.to_json
  end
end

We all know that this simple example will soon get bloated with code lines since we want a little message filter here and a special formatting method for dates there. So, why would you put all this logic into the Message class? It has nothing to do with your message domain at all. And, no, this ain’t no over-engineering, this reflects your concern of compiling a document.

— UPDATE: There was some discussion whether “compiling JSON” is part of “the” domain or not. Well, let’s presume we’re designing a document management tool for librarians (very interesting people, BTW!). In order to send archived files to other systems, one feature is an exporter to serialize documents to JSON – then this component is surely part of your domain.

And, The Persistance Layer?

Strictly speaking, your Message class should be responsible only for synchronizing the model instance with the corresponding row in the database. It is a persistance layer. And this layer should be free of business code. I know, it is handy to simply push in a formatter method for the created_at field – at the cost of an ever-growing class, thou. Think about it. How many methods could you move to dedicated new classes since those behaviours don’t need persistance?

But Isn’t That Over-Engineered?

If Rails and its legendary “model” layer would be good practice, I wouldn’t see dozens of projects all over the world with model files having 600 LOC each, including programmers complaining about too many responsibilites in their classes, starring at me, eyes wide open, blearily, asking where to put all that code. Rails does allow you additional assets, but the framework itself doesn’t answer those questions.

Your fat models are killing your software design. Do you know how hard it is to figure out the concern of a set of methods in a fat class? Of course you do! So why don’t you just generously push a set of logically associated methods into its own class? If that hurts too much you can still move it back into your all-mighty “model”.

What Do We Get From That?

Please don’t get me wrong – I don’t tell you to perfectly abstract your code “Java-like” (hate that comparison) into twohundredseventyfive classes. Relax, and refactor your code step by step. Parts that do not necessarily need the persistance should be separated. You will keep track of what happens where more easily with your refactorings and have faster tests that might encourage you to assert more edge cases.

In addition, that process makes you think about interfaces – the interaction of your new classes with your existing models. Interfaces are a good thing. Don’t fear the class. Hopefully, the next posts in this series can help. I want to talk about mass-assignment problems, lifecycle callbacks in ActiveRecord, form objects, and a lot more including ways to do that practically.

Several speakers were talking about the same problem: Unbloat your models in Rails and focus on your domain instead. “There’s something in the water.” – that was the conclusion of the LoneStar RubyConf 2012 last weekend, a fantastic conference in beautiful Austin, TX with even more fantastic people.

Tags:

12 Responses to “Fear Of The Class: Fat Model Kills Your Software Design.”


  1. Chris

    So now we have animals that don’t walk and poo themselves, but instead are composed of several modules that do the walking and crapping for them.

    It’s not really the type of object orientation one learns at school is it? although maybe it’s the only way to do it.

    This is why I think DCI has so much potential as it still allows us to think about objects that do things for themselves.


  2. Mathieu

    I think that having a too huge, too fat, too complex Model class is not a good thing.

    And I don’t poo by myself alone. I have a “module” called digestive system that handles it for me.
    But I still poo! ;-)


  3. nick

    I was about to make a similar comparison, Mathieu, haha, I also shit but I usually delegate it to my Digestion instance!


  4. Brian

    Nice post! If you think about the general, mammalian anatomy, it is comprised of one or more entities that have a single responsibility. These entities tend to aggregate up and collaborate to accomplish one or more goals. But the example of walking and pooing only reinforces Nick’s post that separation of concerns – at least refactoring your models – is a good thing.

    I think what Nick is getting at is creating a service layer between your controller and your models. ASP.NET MVC uses a similar model where you tie an interface between your controller and your model, so you get skinny controllers and skinny models.


  5. nick

    Danil: Thanks for those great links, really helpful, I will come back to that! Oh, and congratulations for being the 500th comment :-D

    Ruby5, thanks for featuring this article! http://ruby5.envylabs.com/episodes/305-episode-301-august-28-2012 Love and hugs!

  6. Great read. Tangentially related, an article I wrote not too long ago called The Greenfield App Continuum (http://erniemiller.org/2012/08/15/the-greenfield-app-continuum/). basically writing about how working in terms of domain modeling instead of persistence modeling can help you think more clearly about the problem you’re trying to solve.

  7. Hi,

    Very intresting blog,

    I was wondering how exacly will you persist MessagesDocument, make a Factory to map MessageDocument on Message perhaps?

    Regards,


  8. Troy

    Just curious, where do you put MessagesDocument? lib? Seems if you want to go all out on the koolaide, move AR’s from app/model to app/records or app/tables and use app/models to model your domain ;)


  9. nick

    Calin: In this example, MessagesDocument doesn’t need persistance as it compiles a document on the fly. I will give some examples in the next post about persisting objects.

    Troy: app/persistance for AR and app/models for MessagesDocument, that sounds just right!

    Thanks!


  10. Stuart

    great post as usual, got a bunch of code primed for this kind of refactoring!

Leave a Reply