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.

4 comments:

Erik said...

Scala also has mixins, called traits. You might want to have a look at that.

Carlo on Data/Software engineering said...

Hi Erik.

Yeah Scala is in my long list of things to learn :)

Hurol said...

Hey Erik,

Coincidentally I'm reading up on Java interfaces and I can see Java interfaces being used to implement your Ruby style domain modeling. Do you think this observation is far fetched?

Thx for ur blog post. It's helpful in grasping Domain Driven Design.

Carlo on Data/Software engineering said...

Hi Hurol,

The problem with interfaces is that they don't have any implementation, So if for example you want your class to be able to be persisted, Xml serializable, etc, and you separate this into different interfaces (like Persistable, XmlSerializable), you still have to implement this methods in your class, mixing your domain business logic with serialization and persistence logic.
In Ruby, you can just implement your business logic in your object and let the implementation of the persistence and serialization to xml in different modules that you just need to include.

As I explain in the beggining of the post, you can achieve something similar in Java with Spring Introductions or Mixins