Architecture & DesignImplementing Domain-driven Design: Important Blocks of Model-driven Design

Implementing Domain-driven Design: Important Blocks of Model-driven Design

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

In the previous article of this series, I described the basics of Domain-driven Design and how to create a DDD structure in PHP. In most cases, we are not able to convert the model directly to implementation.

As you might remember, in the previous article I defined Model-driven Design as a connection between the model and the implementation. In this article, I’m going to describe other important blocks of Model-driven design and how to prevent developers from reconceptualizing the domain on their own.

Value Objects

I’m a big fan of Value Objects. I think they are the heart of OOP. Although they seem simple, DDD value objects are a serious point of confusion for many, including me. I’ve read and heard so many different ways of describing value objects from a variety of perspectives. Luckily, each of the different explanations, rather than conflicting with one another, helped me build a deeper understanding of value objects.

Value objects are accessible by their value rather than their identity. They are Identity-less, immutable, highly Cohesive, and also can be Self-validating objects. Their values don’t change (or change rarely) and have no life cycle. Examples are currencies, dates, countries, and so forth.

Eric Evans defines the value object as follows:

An object that represents a descriptive aspect of the domain that has no conceptual identity is called a Value Object. Value Objects are instantiated to represent elements of the design that we care about only for what they are, not who they are.

In this case, I think descriptive aspect means that they are (not always) an entity’s state, describing something about the entity or the things it owns. Value objects can have an ID, but, as Evans says, they do not have a conceptual identity. It means they do not have an identity in the domain. For example, they may have IDs using some database persistence strategies that haven’t any definition in the domain.

There are two keys that make value objects important:

  • Representing a Descriptive Concept
  • Enhancing Explicitness

When a concept does not have a conceptual identity, it’s a big clue that it should be a value object. Encapsulation is very important and should not be avoided at all cost. All attributes should be private or protected by default and no public attribute is allowed.

Value objects are Combinable. This means the values of the two can be combined. As I mentioned earlier, value objects are immutable, and the result will be a new value object with the desired value. The original objects remain unchanged.

When you want to cast value object into a string, please make sure you return a complete value object:

$Distance = new Distance(7, 'kilometers');
// The statement is incomplete
$this->assertEquals('7', (string) $Distance);

it should be:

$Distance = new Distance(7, 'kilometers');
// The statement is complete now
$this->assertEquals('7 kilometers', (string) $Distance);

As you see, the first one is incomplete because 7 is confusing. It is not clear which type of distance was returned. Is it meters, centimeters, miles, or inches?

Entities

Entities are objects that are accessible with an identity in our application. Exactly, in fact, an entity is domain concepts with identity and continuity.

Each entity must have an operational way of establishing its identity with other objects, and it’s distinguishable even from another object with the same descriptive attributes. An identifying attribute must be guaranteed to be unique within the system (such as UUID); however, that system is defined—even if distributed, and even when objects are archived.

One of most important things about your entities is ensuring they are self-validating and always valid. This also is true for invariants. Invariants are facts about an entity. They limit the values of certain attributes and must fall within a certain range to be an accurate representation of the entity being modeled and make sure all of them are implemented; otherwise, there would be the danger of the anemic object.

Using setters in your entities is a big risk. This will lead your entity to an unstable state and, at every moment, there is a danger of a change in other parts of the system in a way that offers no explanation of why it is being updated.

Entities vs. Value Objects

The essential distinction between these two is that identity matters for entities but does not matter for value objects. Value objects typically can be represented as immutable objects; changing one property of a value object essentially destroys the old object and creates a new one because you have changed the definition of the value object. So, whether an object is an entity or a value object really depends on the context of how you are using it within your application. Eric Evans believes the following:

Does an object represent something with continuity and identity—something that is tracked through different states or even across different implementations? Or, is it an attribute that describes the state of something else? This is the basic distinction between an ENTITY and a VALUE OBJECT.

Service

A Service in Domain-driven Design is simply a stateless object that performs an action. While analyzing the Domain model, you may be faced with some aspects of the domain that are not easily mapped to objects. In other words, sometimes domain model behaviors will not fit in domain objects. I mean, if we combine them with a domain object, the domain object is not OOP anymore. We would break Encapsulation, the single responsibility principle, low coupling, high cohesion, and another important aspect of OOP. By the way, they represent an important behavior of the domain, so they cannot be neglected or simply incorporated into some of the entities or value objects. In this case, the idea of pure fabrication according to GRASP is good idea.

To continue, I will describe the signs of a service in Domain-driven Design. There are four main reasons that convince you to create a service:

  • The operation performed by the service refers to a domain concept which does not naturally belong to an entity or value object.
  • The service is stateless.
  • It represents behavior, and thus has no identity.
  • The service is derived from the ubiquitous language.

In number one, a service will violate OOP if you perform some operation on an entity or value object. To prevent that, you need a new class that is pure and OOP. Number two is about being stateless; for example, with encryption, it does not matter what you enter in your input or what you get in your output; you will always get encrypted data out. The service itself does not store a copy of those encrypted data and so it is stateless. Ubiquitous language is very important while you are doing DDD, so you must obey it not only in service but also in every part of your project.

Services are a useful concept; you need to be careful not to push too much logic into services, which can lead to inaccurate, confusing, and anemic domain models with low conceptual cohesion. In other words, your service must not separate data, or be stored in entities, and behaviors. this is an anti-pattern that violates the information expert aspect of OOP.

There are different types of service in Domain-driven Design. The service which belongs to the domain Layer is a service that is used for encapsulating domain logic that does not naturally fit on any existing Domain Objects.

The service definition in the Infrastructure Layer is about dealing with the technical details of the infrastructure, such as e-mail, and so on. The last one is an Application Layer Service, which typically is used to show how the outside world interacts with your application. Exactly, in fact, it acts as an abstraction layer upon your domain layer and looks like an API. It makes it possible to contact your domain model. You must be careful of exposing objects; doing so directly can be cumbersome and lead to leaky abstractions.

Conclusion

As you can see, the implementation of Domain-model is not an easy job. For example, it’s not easy to decide whether you should model a particular piece of functionality as a Domain Service or an Application Service.

It really comes down to the context of the application you are building and what is important to the business you are building it for. In the next article, I will cover more subjects in DDD and PHP.

I look forward to seeing your comments.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories