Giving some examples of how to implement the basic building blocks from Domain Driven Design in Rails.

This article is quite old.Time flies when you're having fun. I've been writing for my blog for a long time. Stuff changes fast, especially in the Ruby world. That's why I've put this warning on old posts. The article might still be valid, though.

Domain Driven Design: Building Blocks in Ruby

A few weeks ago I talked about Domain Driven Design in Ruby at the local Ruby user group: Rotterdam.rb. It had a great turnup (even though the weather prevented some from coming) and there was a good discussion going on. Thank you for that! Follow @rotterdamrb to hear about future meetups with free beer and pizza, sponsored by Finalist IT Group!

It was a long talk, so I couldn't cover all the topics I wanted to cover. I talked about ubiquitous language, bounded contexts, core and support domains, and showed some ways to do this in Ruby, using modules. Basically, I concentrated on techniques to organize essential complexity, while keeping an eye on practical usage with Ruby and Rails.

When you talk about Domain Driven Design, you hardly cannot do that without mentioning the three types of objects that Eric Evans discusses in his book. It's Entity objects, Value objects and Service objects. Still I managed to do just that. Time to fix it; here is what I failed to mention.

Entities

Entity objects are objects that represent something in the real world and can be referenced as such. Translated into Rails terms these would be instances of (most of) your models. You can reference a post instance by getting it via its id from the database.

These objects are prime candidates for using plugins like friendly_id, making the fact that these objects have a real identity in real life clearer. This is because you can now reference them by name, instead of some database id. To use friendly_id with Rails 3, point your gemfile to the 'edge' branch on github.

Value Objects

Objects that don't have any real identity are called "Value objects". Any object that is a value object has no real identity, nor is it important to know its identity.

Addresses are a good example. The value of the address (e.g. street, house number, city, country) is important. But it's less obvious to store this in a database and reference it by an id. This id would be purely superficial and have no meaning in the domain you are designing.

A pure Ruby way to do this with Structs, which I have mentioned before on this blog. If you're using a document based database, like MongoDB, these would obviously be embedded documents. With ActiveRecord you can use the composed_of-method. Allow me to demonstrate that:

# Attributes of Person include:
#
# * first_name  string
# * name_infix  string
# * last_name   string
# * male        boolean
#
class Person < ActiveRecord::Base
  composed_of :name,   :mapping => Name.members
  composed_of :gender, :mapping => Gender.members
end
class Name < Struct.new(:first_name, :name_infix, :last_name, :gender)

  def to_s
    [ first_name, name_infix, last_name ].select(&:present?).join(' ')
  end

  def first_name
    super.presence || title
  end

  def title
    I18n.t(gender.key, :scope => :titles)
  end

end
class Gender < Struct.new(:male)

  def to_s
    I18n.t(key, :scope => :genders)
  end

  def key
    male ? :male : :female
  end

end

By defining to_s on these structs you can output their gender or name in views without doing anything special:

%dl[@person]
  %dt= Person.human_attribute_name(:name)
  %dd= @person.name

  %dt= Person.human_attribute_name(:gender)
  %dd= @person.gender

In this example, Person is actually an aggregate of the entity Person and two value objects, called Name and Gender. Although all attributes are flattened out in your persistence layer (in this case, your database table); they do have a deeper structure in your code. And it's easier to test too!

Services

Things that aren't really a 'thing' in the domain you're designing are usually services. They are not really part of any entity or value object, but do something with them.

In Rails, these would be observers or plain Ruby objects lying around. Maybe it's time to call them what they are and place them in app/services.

Every Rails developer knows the pattern "Fat Model, Skinny Controller". This is a pattern to remember that you shouldn't put model logic in your controller but in your model. But this pattern is often taken too far. There are people that give the params-object, or worse, the entire controller-instance, to the model and do their shit there. This is not right. Use a service for that.

Pagination and searching are good candidates for a service. But this blog post is on the long side, so I'll save an example implementation of that for another time. No post is complete without a promise to a follow-up that never comes, right?

Conclusion

It's the same as I said during my talk: Rails helps you by keeping accidental complexity at a minimum. You can use the techniques described in Domain Driven Design to organize essential complexity and make your application more maintainable. Just be careful not to over-engineer it, that would defeat the purpose. Always be critical of your own code and continue to ask yourself the same question: "Does this make my code better?"

comments powered byDisqus