Sunday, February 27, 2011

Rails: Many classes in one file

Some time ago I asked if there is any way of having many classes in one file and being able to work normally in the development environment (reloading classes between requests).

Luckily, Jan Dudek (thanks!) came up with some nice solutions. The best of them uses ActionDispatch::Callbacks in combination with require_dependency. Here is an example:

config/initializers/load_classes.rb
ActionDispatch::Callbacks.before do
  require_dependency "app/models/events.rb"
end

I have tried it in one project and it works pretty well in all environments.

Why would I want to keep many classes in one file?


I agree that this technique breaks the Rails convention of keeping one class per file. Possibly, it can confuse some developers on where to find classes definitions. In this case it's important to communicate with the team whether it's worth trying and explaining how it works before using it.

For me this technique is useful when I have many Rails models, all very short, related to the same feature. The example above (app/models/events.rb) keeps definitions of Event, Attendee, Attendance, AttendanceType, EventBoard. All of them are short and instead of jumping between many files I've got one file which keeps it all in one place. It's hard to measure whether it's good or bad, works well for me so far.

Monday, February 14, 2011

An application or a set of resources?

My last post about DCI and Rails triggered many interesting discussions. One of the topics that came up very often can be summarized to this question:

Do you think of your web application as an application or as a set of resources?


My answer is clear: it's an application.

I noticed that not everyone agrees with it.

In the Rails world a resource is a very popular word. Mostly because of the REST architecture that we follow in our apps. Whenever I can I use the REST approach, but I think of it as a way of using URL's and accessing the application. I find it a nice way of structuring my controllers. REST is just an interface. From outside and for my users it's an application, not a set of resources. They use resources but they access them through an application which to me is an important distinction.

This topic caused that I decided to publish the assumptions that are needed for a DCI architecture, at least how I understand it:

1. As developers we model the real/business world.
2. If a concept appears in the model we reflect it in the code.
3. Most of the web apps scenarios has the pattern of "a user does something with the app"
4. If user and app are so important in the domain then we make them classes/objects.
5. It results in big user/app classes.
6. The problem of big classes can be solved by using the idea of "injecting roles" runtime.

Usually point 5. discouraged people from reflecting the user/app importance in the code. The alternative is chosen which is things like this in the controller:

@discounted_products = Product.discounted

whereas I prefer this:

@discounted_products = shop.discounted_products 
# shop is like the application object here

Anyway, point 5 can be now solved by the idea of injecting roles and DCI as described in my last blog post and in my opinion we can start using the user and application objects more often. Once you start using them, many of the OOP decisions you need to make become easier, because you just reflect the domain in the code.

Friday, February 11, 2011

DCI and Rails

I got really excited about DCI recently.  I'm still not sure if I get it right, but let me try to describe the way it works and how it can improve our Rails applications.

Here is the short version of this blog post:

#controller action 


current_user.extend Buyer 
current_user.add_to_cart(product_id, quantity)


DCI stands for Data Context Interaction. It was invented by Trygve Reenskaug, the same scientists who formulated MVC. It's a new paradigm, but it's very similar to OOP and fits especially well with the MVC architecture that we use in Rails applications.


Let me start with the evolution of Rails applications, so that I can explain current problems with the Rails model layer (in my opinion).

Very early we (the Rails community) agreed that it's better to move the logic down to models and keep controller very thin. I also wrote a blog post on how to write Rails controllers which is still my opinion on this topic.

As a result we had to cope with so called "fat models". This topic was an area of my research for years. It resulted in some techniques that I described in the "How to deal with fat models" blog post.

One of the things that I personally dislike is the overuse of Global State and Class Methods in Rails controllers. I have seen it everywhere and it my opinion in bigger apps it can result in maintenance problems. You can read why it's bad in this article by Hevery Misko. I must admit that with ActiveRecord it's very hard not to use Class Methods, but at least we can minimize it.

I was researching more OOP techniques that can be used to keep a nice OOP design in Rails model classes. I found the composite pattern (resulting in delegation) to be the closest to what I want to have. I asked the Ruby community why we don't use composition so often. The answers confirmed my observations that Ruby mixins (modules) are much more popular.

Anyway, it doesn't really matter - the result of "traditional usage" of mixins and composition in Ruby is very similar - you end up with a class which has lots of responsibilities. It doesn't matter that most of the responsibilities are delegated to other objects, actually it leads to a problem called self schizofrenia. This is especially visible in classes like User (in a social network app) or Article (in portal apps), which are very often a central place of application logic.

There are two techniques that I found useful during my research.

1. Current user pattern

This pattern was just reflecting the fact from a real life - a user interacts with our website so whenever a user does something there is a method in the User class responsible for that. Some methods can be grouped into "roles" and extracted to another class or a module, but they're declared in the User class.

2. Website pattern

This is the other side of the current user pattern. The user is interacting with a website, so it sounds like a good idea to have some kind of Website (or Application) class. This class encapsulates all the data and behaviour that is accessible from the website.


As you are probably seeing it now, it results in huge User and Website classes.

Just to be clear - both the current_user (in the Devise/Authlogic meaning) object and the website object are accessible from ApplicationController, so any controller could have access to them. The advantage here is that you don't need to introduce Global State and with the website pattern you can eliminate calls to class methods.

It was always kind of a smell to me that any controller takes the whole user object just to interact with a very small subset of the user interface. Let's say that you have a PostsController which needs to display all the posts. It takes the website object just to call website.posts, but theoretically it can call any other unrelated method. Static languages deal with it by communication using interfaces. It is a solution but it's still not perfect.

Here is where DCI comes in.

One of the things DCI claims is that most of the time we don't do object oriented programming but we do class oriented programming. It's more visible in the Java/.Net world but I think the same applies to most of us - Rails programmers. We put our declarations in classes and then instantiate objects with lots of unnecessary stuff.

DCI suggests that we keep our core model classes very thin. Zero logic, only data, if anything.


The logic/behaviour should be kept in roles. In Ruby we can nicely use mixins for that.


In an e-commerce app you would have User class but the buying/cart logic would be in the Buyer module. The User class knows nothing about cart, buying.

In a social network app you can create a GroupUser role and keep all the group related logic inside.

You can also extract some roles from the Website class. Some candidates in an e-commerce could be ProductRepository, OrderDepartment, NewsletterManager, Blog etc.

This logic is then injected runtime, at the object level, in the context of a specific use case.  In my opinion contexts or use case fits very well with the rails thin controllers rule.

Usually, one controller action is a specific use case. One example may be adding some product to a cart. The action could look like this:

current_user.extend Buyer
current_user.add_to_cart(product_id, quantity)

Another example, with a website:

class EventsController
  def index
    website.extend EventsBoard
    @events = website.recent_events
  end
end

As you see we use .extend method here which allows extending an object with a module. It all happens at the object level.

To me, this technique is most crucial for DCI (from a Rails perspective). Extending the object with a role at the controller level simply solves all of the OOP problems I had to deal with. The class doesn't know about all the roles, there's no problem with huge classes or object schizofrenia. You just create roles which are usually small modules and that's it.

DCI suggests that we keep use cases as classes. If my understanding is correct the place for creating use cases would be at the controller level. Notice that usually a single request is some kind of a use case. However, usually the code is so small in this area that I'm not sure it deserves a class. I can see the advantage of seeing all the interaction in one place so I just need to get used to this kind of thinking.

Terminology

In order to fit DCI into the Rails terminology I assume that we can call our thin model classes the Data layer. Our controllers actions can be wrapped into a class and called Contexts. Those actions take the Data, inject the roles (as Ruby modules) and run some Interactions.

Problems


I have already tried this approach in one small application. It worked pretty well. One of the problems that appeared was a situation when I actually want the object playing almost all of the available roles. This kind of situation often takes place at main pages or in some kind of dashboards.
I solved it by using the Cells framework. In short, Cells let you create mini-controllers (cells) that can be responsible for one part of the view. It can access the controller and execute a "mini-action". In my case I used in a way that every cell extends the website with the role that is associated with this view. It worked pretty well and I'm quite happy with the result.

Summary


In my opinion DCI fits very well with the way we create Rails apps. It simplifies the Model layer a lot. If you're happy with your current OOP design then you will probably not gain too much from DCI. If not, wikipedia entry is a good start for getting familiar with this concept at a more theoretical level. The whole concept is actually very simple and intuitive. It brings the user perspective to your code, which in my opinion is a good thing. We're here to model the real world/business etc.



 If you read this far you should Follow andrzejkrzywda on Twitter and subscribe to my RSS.