DDD for Rails Developers. Part 2: Entities and Values

This entry is part 2 of 3 in the series DDD for Rails Developers

DDD for Rails Developers

In my previous article about DDD for Rails Developers, I talked about using the layered architecture for tackling domain complexity. I showed a few typical violations of the layered architecture and gave some advice on how to fix them.

The Building Blocks of Domain Driven Design

This time I’d like to start talking about the building blocks of Domain Driven Design, and how they can be used for modeling.

Entities and Values

In Domain Driven Design, an important distinction is drawn between Entities and Value Objects.

  • “An Entity is an object defined not by its attributes, but by a thread of continuity and identity.” An example of an Entity would be a bank account. Many bank accounts can exist in our system at the same time. Some of them can be assigned to the same branch or have the same owner, but it’s important for our system to treat them as different accounts as long as they have different identities. In case of a Rails application, an identity of an Entity is usually represented by an auto-generated primary key.

  • “A Value Object is an object that describes some characteristic or attribute but carries no concept of identity.” As there is no identity, two Value Objects are equal when all their attributes are equal. An example of a Value Object would be Money.

More on Entities

In the Rails community we have a good understanding of what Entities are. Practically, almost every object extending ActiveRecord::Base is an Entity.

Entities have the following characteristics:

  • Entities care about their identity. The identity is usually represented by an auto-generated primary key, which is used to compare two Entities.
  • They are mutable. The only field that cannot be changed is the primary key.
  • They have long lives. Most Entities are never deleted from the database.
  • Since Entities are mutable and long-lived, they usually have a complex life cycle:
    * An Entity Object is created.
    * It is saved in the database.
    * It is read from the database.
    * It is updated.
    * It is deleted (or marked as deleted).

Due to mutability and a complex life cycle, dealing with Entities is complicated. Therefore, every time you define an Entity, think over how you are going to persist it, what attributes you have to make mutable, what Aggregate (more on Aggregates in the next post) should contain it, etc.

More on Values

Value Objects, on the other hand, are underused in the Rails community. As a result, most Rails applications suffer from Primitive Obsession:

  • Primitive values such as integers and strings are used to represent important concepts of the domain.

Firstly, as the logic of dealing with a group of attributes is spread out over dozens of classes, Primitive Obsession is usually a source of code duplication. Secondly, using primitives instead of domain specific abstractions clutters high-level services with unnecessary details and makes the intent of your code unclear. Value Objects offer a good remedy for Primitive Obsession.

Value Objects have the following characteristics:

  • Value Objects have no identity.
  • They are immutable. For instance, adding 3 to 5 does not change any of these values. A new value is returned instead. Ideally, working with Value Objects should feel like working with primitives.
  • Value Objects do not have a complex life cycle.

Creating Value Objects in Rails

There are many ways of creating and managing Value Objects in Rails, and I’d like to show three of them.

Use composed_of

Imagine, we are writing another blog application. We’ve decided in favor of this model:

  • Blog has many Posts.
  • Every Post has many Comments.
  • Posts and Comments have location attributes associated with them.

We can create Posts and Comments this way:

We can also search them by their location:

In addition to this, we have a presenter to display the location attributes:

As you can see, we always use the country and city attributes together. Even when I was explaining the application behavior I wrote: ‘by their location’. Apart from having duplication we’ve missed an important part of our domain. There is a notion of a location that our model (our code) does not reflect. Let’s fix it.

Let’s start with defining a class that will encapsulate the location attributes:

Now we need to configure Post to wrap location_country and location_city into an instance of Location:

This is the result of our refactoring:

The biggest gain after this refactoring is making an important concept of our domain explicit in the source code. Also, extracting a Value Object helped us to raise the level of abstraction, which leads to more readable code:

Value Objects Extending ActiveRecord::Base

Some people say that everything extending ActiveRecord::Base is an Entity. I disagree with this opinion. In my view, it doesn’t really matter how you implement your Value Objects as long as they have neither state nor identity.

Let’s define the Location class:

Using Location stays pretty much the same:

Such requirements as supervising the list of all possible locations dynamically or attaching some additional information to every object (for example, a link to a wikipedia article) may push your decision in favor of this approach.

Plain Old Ruby Objects

Those developers who learned Ruby via Rails tend to solve all their problems using Rails building blocks. Need to persist something? It’s only ActiveRecord. Need a Value Object? Use composed_of. Everything that does not use Rails feels dirty for them. For example, all models not extending ActiveRecord::Base go to the lib folder. Even though it may work for small applications, building a complex model will require using Factories, Services, Value Objects, etc. Therefore, don’t be afraid of implementing a Value Object without Rails at all.

Summary

To sum up, Entities and Value Objects are extremely important. They are the core elements of object models. Thus, software developers should have a solid understanding of differences between them.

DDD for Rails Developers

<< DDD for Rails Developers. Part 1: Layered Architecture.

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • http://dockyard.com Brian Cardarella

    Very nice, thank you for writing.

  • Gerhard

    I was trying to find a post to help some Java/GWT developers understand DDD, and found this very helpful. Thank you

  • Jalal Ashrafi

    Great post, thanks,
    But I’m disagree with “In case of a Rails application, an identity of an Entity is usually represented by an auto-generated primary key”.
    I think auto generated keys are technical concepts and when we talk about Domain and DDD, we should consider identity an attribute with “Domain meaning” for example in a domain SSN attribute of Person entity may identify a particular person thus identity should be SSN not Technical key generated by rails or hibernate( in java ) or db or anything like that!

  • qtpeng

    Nice ariticle, which answered my a lot of questions.

    Actually, building a large website,no matter what language you use, needs strong OO design skills, and deep understanding on multi-layer architecture.

    Using only what rails provided us can only build small websites.

    We need to combine Ruby OO programming with Rails.