DDD for Rails Developers. Part 2: Entities and Values

Share this article

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:

[gist id=”2202292″]

We can also search them by their location:

[gist id=”2202296″]

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

[gist id=”2202298″]

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:

[gist id=”2202302″]

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

[gist id=”2202306″]

This is the result of our refactoring:

[gist id=”2202307″]

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:

[gist id=”2202310″]

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:

[gist id=”2202311″]

Using Location stays pretty much the same:

[gist id=”2202322″]

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.

Frequently Asked Questions (FAQs) about DDD for Rails Developers

What is the difference between Entities and Value Objects in DDD?

In Domain-Driven Design (DDD), Entities and Value Objects are two fundamental building blocks. Entities are objects that have a distinct identity that persists over time and across different states. For example, a user in a system is an entity. On the other hand, Value Objects are objects that are defined by their attributes or properties and do not have a distinct identity. For instance, a user’s address can be considered a Value Object as it is defined by its properties (street, city, country) and does not have an identity of its own.

How do I implement Entities in Rails?

In Rails, you can implement Entities as models. These models are typically backed by a database table and include methods for business logic. You can use Rails’ Active Record for this purpose, which provides a rich API for interacting with the database. However, it’s important to note that in DDD, Entities should encapsulate business logic and rules, and should not be concerned with database operations.

How do I implement Value Objects in Rails?

Value Objects in Rails can be implemented using plain Ruby classes. These classes should include methods for business logic related to the Value Object. Since Value Objects do not have an identity, they do not need to be persisted in the database. However, their state can be stored as part of an Entity.

What is the role of Associations in Rails?

Associations in Rails are a way to declare relationships between different models. For example, a User model might have many Posts, and a Post might belong to a User. These relationships can be declared using Rails’ association methods like has_many, belongs_to, and has_one. Associations allow you to easily navigate and manipulate related objects in your domain.

How does DDD fit into the Rails framework?

Rails is a framework that follows the Model-View-Controller (MVC) pattern. DDD can fit into this pattern by treating Models as Entities or Value Objects, and Controllers as the means to handle user interactions and orchestrate changes in the domain. The View in MVC corresponds to the user interface, which is not a concern of DDD.

How can I design an Entity in Ruby on Rails?

Designing an Entity in Ruby on Rails involves creating a model that encapsulates the business logic and rules related to that Entity. This model should have a unique identity, typically represented by a primary key in the database. The model can also include methods for any operations or behaviors related to the Entity.

What are the benefits of using DDD in Rails development?

DDD can help in creating a more maintainable and understandable codebase. By focusing on the domain and its logic, you can create a model of the system that closely matches the business reality. This can lead to a more robust system that is easier to evolve and adapt to changing business requirements.

How can I handle complex business logic in Rails?

Complex business logic in Rails can be handled by encapsulating it in models (Entities or Value Objects) or in service objects. Service objects are plain Ruby objects that perform a specific task in the domain. By breaking down complex logic into smaller, more manageable parts, you can create a system that is easier to understand and maintain.

How can I persist Value Objects in Rails?

While Value Objects themselves do not have an identity and are not persisted, their state can be stored as part of an Entity. For example, a User’s address (a Value Object) can be stored in the User’s database record. Rails’ Active Record provides methods for handling this kind of nested attributes.

How can I validate data in Rails?

Rails provides several methods for validating data. These include presence, uniqueness, format, and length validators, among others. These validators can be used in your models to ensure that only valid data is saved to the database. In DDD, these validators can be seen as part of the invariants of an Entity or Value Object.

Victor SavkinVictor Savkin
View Author

Victor Savkin is a developer interested in Domain Driven Design, Enterprise Architecture, and Domain Specific Languages. He works on large enterprise applications written in Rails. Being a language nerd he spends a lot of his time playing with Smalltalk, Groovy, Scala, Clojure, Ruby, and Ioke.

ddd
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week