Ways to Decompose Fat ActiveRecord Models
blog.codeclimate.com
blog.codeclimate.com
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...
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.
* 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
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?
It's important to have a meta-awareness while programming - how firm is your grasp on the code? How confident are you that your changes will do what you expect? At some point, you feel that grasp slipping, and then it's time to refactor.
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.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.
instead having operation on object doing this and clearly encapsulate behavior and hopefully data as well
http://blog.codeclimate.com/blog/2012/02/07/what-code-goes-i...
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
Is this a case of fixing the wrong problem?
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.