views:

290

answers:

4

I have been using Rails for over 4 years so obviously I like Rails and like doing things the Rails Way and sometimes I unknowingly fall to the dark side.

I recently picked up Clean Code by Uncle Bob. I am on Chapter 6 and a bit confused whether we as rails developers break the very fundamental rule of OO design, i.e. Law of Demeter or encapsulation? The Law of Demeter states that an object should not know the innards of another object and it should not invoke methods on objects that are returned by a method because when you do that then it suggests one object knows too much about the other object.

But very often we call methods on another object from a model. For example, when we have a relationship like 'An order belongs to a user'. Then very often we end up doing order.user.name or to prevent it from looking like a train wreck we set up a delegate to do order.name.

  1. Isn't that still like breaking the Law of Demeter or encapsulation ?

  2. The other question is: is ActiveRecord just a Data Structure or Data Transfer Object that interfaces with the database?

  3. If yes, then don't we create a Hybrid Structure, i.e. half object and half data structure by putting our business rules in ActiveRecord Models?

+6  A: 

IMHO if you follow the purist approach too much then you end up in a mess like Java where it uses all the right design patterns but no-one can remember the eight lines of code you need just to open a file and read its contents.

Rails' ActiveRecord framework is an implementation of Martin Fowler's Active Record design pattern. Active Records in Rails are certainly not just dumb data structures or DTOs because they have behaviour: they perform validation, they can tell you if their attributes have changed etc. and you're free and indeed encouraged, to add your own business logic in there.

Rails in general encourages good practice e.g. MVC and syntactic vinegar to make doing bad things difficult and/or ugly.

John Topley
Rails definitely encourages good practices, and as you mentioned one of the basic premises being MVC, skinny controller and fat model, etc. However, about your first part of the answer I am not sure whether someone can really end up in a mess by following right patterns in any particular language although one can definitely create a mess in any language, including ruby, by blindly applying a design pattern that is not suitable for a given problem.
nas
In the Java case it's a mess because it hinders productivity. It's extremely easy to get things done with Rails.
John Topley
True and I couldn't agree more! But if you have a small rails app and if you don't follow good design principles then it may not matter much. Although when the application grows above a certain level and you keep adding code without much thought then it may become as messy as any other code written in any other language. Especially when you see train wrecks (order.user.name.split(' ')) and Model objects being accessed directly from views. It becomes a nightmare to maintain as well as add new features to the existing code.
nas
That's true for any language though, isn't it? You can create a big hairy ball of mud using PHP and from what I've heard it probably leads you down that path. On the other hand, if you're a good, disciplined programmer it's perfectly possible to create a maintainable code base using any language.
John Topley
+3  A: 

Rails is Rails. What else is there to say. Yes, some of the idioms in Rails violate good design principles. But we tolerate this because it's the Rails way.

Having said that, there is far too much model usage in most rails applications. Far too often I see view code directly accessing models. I see business rules folded into the active record object. A better approach would be to isolate the business rules from the active records and isolate the views from the models. This wouldn't violate any rails idioms, and would make rails applications a lot more flexible and maintainable.

Robert C. Martin
Would it be really worth it to introduce yet more objects in a 2000 line system? I agree for a 10k+ system, but small scale ones?
François Beausoleil
If that makes the design cleaner and easier to extend then I think that should be considered because I have seen rails apps with more than 200 models and some models are over 1000 lines. Now one can say that its because bad design. There could be many reasons for that but one of those could be not following or violating basic principles. That's just a thought.
nas
IMHO what uncle bob said above "A better approach would be to isolate the business rules from the active records and isolate the views from the models." makes sense. The question is how can that be done cleanly if there is order or user ActiveRecord Object and there needs to be a bunch of business rules for those?
nas
You don't have to write DAO or a bunch of column mappings or messing up your model with getter/setters like Java, ActiveRecord has already done these dirty jobs for you. DAOs or mapped object is separated so easily that you don't have to care about them. Your models are free to do business rules, they are clean and empty already! View logic is suggested to be in helpers, not in models.Some authors of the old books didn't use rails, and thought their guidelines to be universal, that's wrong.
ns
Uncle Bob I've often thought this too, that ActiveRecord really isn't a "Model" so much as it is a data layer that should be abstracted away from business rules manipulated by actual "Models" in the classic sense of MVC. Is this a misnomer in the Rails idiom or was it a valid use of the concept to call ActiveRecords "Models"? In your opinion does this lead to some confusion as to the roles ActiveRecords should really play in a Rails app?
Dave Sims
+1  A: 

Concerning "Law of Demeter" one thing I've not seen mentioned is the concept of distance. By that I mean, "How closely related are the object involved?" It is my opinion that this would make some difference whether I care to follow "Law of Demeter" or not.

In the case of ActiveRecord, the objects involved in most of the LoD violations are inseparably bound together into a close relationship. Changing the internal data structure of these objects require a change in the database to reflect that new structure. The tables of a database are typically "bound" together into a single database, which even reflects these "associations" through foreign key constraints (or at least contain primary & foreign keys).

So I don't generally concern myself with following LoD between my AR objects. I know that they are tightly bound to each other due to their very nature.

On the other hand I would be more concerned about LoD between more distant objects, especially those that cross MVC boundaries or any other such design device.

Robert Walker
+2  A: 

Yes, ActiveRecord deliberately breaks encapsulation. This is not so much a limitation of Rails as it is a limitation of the pattern it's based on. Martin Fowler, whose definition of ActiveRecord was pretty much the template Rails used, says as much in the ActiveRecord chapter of POEAA:

Another argument against Active Record is the fact that it couples the object design to the database design. This makes it more difficult to refactor either design as a project goes forward.

This is a common criticism of Rails from other frameworks. Fowler himself says ActiveRecord is mainly to be used

...for domain logic that isn't too complex...if your business logic is complex, you'll soon want to use your object's direct relationships, collections, inheritance and so forth. These don't map easily onto Active Record.

Fowler goes on to say that for more serious applications with complex domain logic the Data Mapper pattern, which does a better job of separating the layers, is preferable. This is one of the reasons that Rails upcoming move to Merb has been generally seen as a positive move for Rails, as Merb makes use of the DataMapper pattern in addition to ActiveRecord.

I'm not sure Demeter is the primary concern with ActiveRecord. Rather I think breaking encapsulation between the data and domain layers breaks Uncle Bob's Single Responsibility Principle. Demeter I think is more a specific example of how to follow the Open/Closed Principle. But I think the broader idea behind all these is the same: classes should do one thing and be robust against future changes, which to some degree ActiveRecord is not.

Dave Sims