
Ways to Decompose Fat ActiveRecord Models - lest
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
======
programminggeek
Ok, let me throw out a different perspective, why are you even using an
ActiveRecord Model as your entities in the first place. That in itself
violates the Single Responsibility Principle by attaching the persistance
mechanism to the entity itself.

What if your entities were just objects that held data and did validation, but
you let use case objects determine the behavior of your system beyond that?
Data persistance at that point is literally persisting your entities to the
DB.

Then you use the DB much more like you would a filesystem - to retrieve and
save data. It doesn't determine your model, it just stores and retrieves your
data.

So, you end up with 3 types of things in this system... entities, use cases,
and data gateways.

Your data gateways can still use AR if you want, or something else, it doesn't
matter.

This isn't my idea, Uncle Bob lays it out better than I can here:
[http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-
arch...](http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-
architecture.html)

------
mattmanser
Ok, time to embarrass myself due to lack of Ruby/programming knowledge. A few
questions that perhaps someone can clear up as from my OO perspective this, to
me, is all over the place. I must admit I'm only just getting started on Ruby.

Firstly, that doesn't look like the strategy pattern to me, isn't it back to
front? And even if it were, what the hell are you doing? You do not pass the
user to the authenticator, you'd pass the authenticator to the user
constructor.

The way you've chosen is very brittle, it's a sure fire way to accidentally
shoot yourself in the foot later on when someone accidentally deletes the
authenticator or adds a new code path that doesn't contain one.

I also don't understand why you're creating a new class per object query?
Ditto for the policy stuff. Why not just use a repository object if you don't
want to clutter your main class. Like OrderRepository.GetByCompany(Id).

As for point 7, I don't understand why you're not completely extracting the
facebook integration from the comment class. Does Ruby not have events? Why
aren't you firing an event that the facebook integrator that initialized on
the user object subscribed to? i.e. make a facebook integrator that registers
itself on the user object creation.

Also, "View Model" not "View Object", that's what they're called, a lot of
other frameworks already use them.

~~~
brynary
Good points/questions. I'll try to respond to each...

* RE: Strategy pattern. My understanding of the strategy pattern is it simply refers to "algorithms are encapsulate and can be selected at runtime". (<http://en.wikipedia.org/wiki/Strategy_pattern>)

* RE: New class per object query. Agreed that grouping these can make sense. Had to keep the example brief.

* RE: Events. That's another approach -- thanks for the suggestion.

* RE: "View Model" vs. "View". I had it as "View Model" in the original draft and got feedback from reviewers that they are usually called just "Views". :-) I've heard it both ways .

Thanks for the questions!

-Bryan

------
ljoshua
This is more of a theoretical question, but at what point does a project
become large enough that concerns like this begin to truly matter?

When doing a small, limited-use project, it seems often that trying to follow
"best practices" like avoiding fat models would be more trouble than it's
worth. And yet I've worked on larger enterprise-scale projects that have most
certainly benefited from following this and other practices.

Anyone know of any research or work on where the tipping point is for
following increasingly complicated patterns and practices?

~~~
ollysb
I try to follow a gradual approach to decomposition. When I start to get a lot
of related methods I wrap them in a module, but still within the model
definition(I just include the module immediately after definition). From there
I might just move it to a separate file as is or perhaps I turn into a
decorator and add an accessor for it on the model e.g.

    
    
      class Retailer
        ...
        def metrics
          RetailerMatrics.new(self)
        end
      end
    
      class RetailerMetrics < Struct.new(:retailer)
        def profit
          # devious stat twisting
        end
      end
    

The biggest danger with any of the patterns from the article is that you start
using them before you actually need them. Then you just end up with a lot of
complexity for no benefit.

~~~
brynary
Yes! Gradual is they key. Your architecture should scale up gradually to
handle the app complexity.

------
taude
I don't know anything about Ruby, but it's interesting to see typical
"enterprisey" patterns appear for people using these scripting frameworks for
web-app development. One thing I've always been curious about when evaluating
Python frameworks, or looking at Ruby on Rails sample code in Github was how
apps look when they start getting large. I usually see a mess, and it sort of
turned me off. It's nice to know that some of the same patterns are being
though of and applied in these worlds, too.

Also some good comments below on not just jumping into using all these
patterns on smaller projects. Take the benefit of being light and nimble
starting, then start breaking things apart as the app and teams grow. Best of
both worlds.

------
LaSombra
I like Ruby on Rails, but "coming" from a Java background I find it odd to
have lots of stuff in the models instead of using utility classes or services.

~~~
ollysb
Coming from a java background to rails I found it entirely refreshing that
everything wasn't in a utility class or service. Decomposition of a system
using utility classes and services(in the n-tier style) is to make your system
more procedural rather than more OO. This article is encouraging more OO
patterns, closer in spirit to DDD. The article doesn't actually go into this
but all of the techniques described can be used to compose a class so that
there is still a single API for each model in your system i.e. the
decomposition into policies, decorators, services(of the domain type) etc. can
be hidden from the user behind the model api.

~~~
brynary
That's a good point. It sounds like what you're describing is essentially a
Facade. That can be useful if you need coordinated objects to manage
complexity, but want to keep it simple for clients to use that part of the
application. I sometimes create a Facade for a Ruby module that is a package
of fine grained classes.

------
chrisconley
Cool stuff. @brynary, how do you organize your app? Are all seven techniques
dumped into app/models or do you keep each technique in its own directory?

~~~
brynary
I wrote about that bit in my previous post:

[http://blog.codeclimate.com/blog/2012/02/07/what-code-
goes-i...](http://blog.codeclimate.com/blog/2012/02/07/what-code-goes-in-the-
lib-directory/)

But really, in short, don't worry too much about it. Plan on reorganizing once
or twice as you find what works for you.

-Bryan

------
damoncali
Perhaps it's my lack of training or the projects I work on, but I've never
seen a Rails model so large/cumbersome as to justify even one of these
techniques.

Is this a case of fixing the wrong problem?

~~~
jweir
I have a project which will benefit from Bryan's advice.

There are models which are overly large (the project started as a Rails 1.2
project now at 3.0). The project has moved through different hands and the
cruft and bloat has built up.

I look forward to applying some of Bryan's points to these models.

------
mcgain
Good article. Concise. Something I will come back and reference.

