Monday, March 21, 2011

Making rich domain models in Ruby is nicer than Java

Being a Java developer starting to do some programing in Ruby i found many different nice things in the language and the way things are done.

One of the nicest things i found is the notion of module mixing, and although you can artificially do the same with Java and Spring (google for @DeclareParents) , it’s not as neat and clean as Ruby does it.

Module mixins basically allow us to add behaviour to our objects without inheritance. Is like if in Java we could use an Interface that has a default behaviour associated to it, so we don’t need to actually implement the methods of the interface.

The objects that has the module mixed in, can receive method calls on the module like if the methods belong to the object itself. This allows to a nice approach to domain rich programming, while at the same time maintaining a great separation of concerns and avoiding filling the classes and objects with lots of not very related functionality.

To explain the last paragraph better, let’s take a simple example. a Person class.

In our example a person can have emotions (happy, sad, etc), can do actions (play, sleep), have measure attributes and queries(height,weight). It can also be used in database operations (If we want like an ActiveRecord approach) (save, update, etc).

In a Model centric approach (Instead of using services, DAOs, etc) we would like to have Person objects that allow us to do thing like:

  1. if (person.sad?)
  2.  
  3. person.playWith(toy)
  4.  
  5. if (person.higher_than?(person2))
  6.  
  7. person.store

We can consider each of these methods addressing different concerns, but all valid thing for a Person to know about. if we were to program this with Java, we would end up with a Person class with all these methods like:

  1. public class Person{
  2.     public boolean isSsad(){...}
  3.     public void playWith(Toy toy){...}
  4.     public int store(){...}
  5.     public boolean isHigherThan(Person person2) {...}
  6. }

We could argue that a better approach would be to have “behavioural” modules and plug them individually to our class. For example in Ruby we could have something like:

  1. class Person
  2.    include Sentiments
  3.    include Actions
  4.    include Persistence
  5.    include Measures
  6. end

We can have then each module dealing with its own concern, and making all this knowledge and capacities available to the Person objects as if they were defined on the Person class.

So for example our “Measures” module could have something like:

  1. module Measures
  2.   attr_accesor :height  
  3.   def higher_than?(entity)
  4.    return self.height > entity.height
  5.   end
  6. end

Then we can do something like:

  1. a= Person.new
  2. a.height = 150
  3. b=Person.new
  4. b.height=200
  5.  
  6. b.higher_than? a

This same approach can be used for the other modules like Persistent or Sentiments.