Domain Drive Design - My takeaways II

Disclaimer: This blog post is my notes and interpretation on concepts and ideas presented in the book together with some personal reflections from my personal experience and career.

The second part of the book is a presentation of design patterns that helps the software engineers capture the domain in software code. It’s more like the building blocks for designing and maintaining domain-driven systems. All patterns proposed in this chapter are influenced by two concepts, which I was taught early in my career but never really defined their origin and name:

  • Responsibility-driven design: A principle mostly described by the shift of thinking about design as data and algorithms to thinking about objects as roles and responsibilities. Each object is responsible for a well defined portion of the application and collaborates with other objects for achieving the application’s goals.

  • Design By Contract: A principle that mostly defines that objects and software components should have formal, precise and verifiable interface specifications. These specifications, which basically are contracts contain preconditions, postconditions and invariants.

I have cross paths with the concept of design by contract in academic courses and research on formal verification and specification. In the industrial world, Clojure supports pre and post conditions out of the box, while in Java we come across it in the Bean Validation and JML.

Design Patterns for Domain-Driven Systems

The core concept that Domain Driven Design promotes is the importance of identifying the domain objects and isolating them from satellite or technical functions of the system. The first pattern, which the book introduces for achieving this separation, is the well-known Layered Architecture. An architecture used on most of the enterprise web applications out there and it consists of the following four conceptual layers:

  • Presentation Layer: Responsible for presenting information and interpreting user commands.
  • Application Layer: A thin and stateless layer responsible to coordinates the application’s activity.
  • Domain Layer: This layer contains the business domain information together with the fundamental business rules.
  • Infrastructure Layer: The most technical layer of all, acts as a supporting library. Responsible for generic technical capabilities, such as interacting with the database or an external email service, etc.

Those conceptual layers are accompanied by some ‘rules’:

  • Each layer depends only on layers below
  • Communication downwards happens through the layered dependencies
  • Communication upwards happens using callbacks or observer pattern.

While all those layers, rules and recommendations are of tremendous value, the first and most important step is to isolate the domain model. After isolating the domain we should define it using software. For achieving that, six more concepts/patterns are introduced Entity, Value Object, Service, Aggregates, Factory, Repository. The first three help us to express and implement the business model and the last three help us with the associations and lifecycle management of the model objects.

Entity

An Entity is an object that is primarily defined by continuity and identity. It encapsulates business logic and has continuity through it’s life cycle and even through various systems and modules. Entities derive from the Ubiquitous language’s vocabulary and are the ‘words’ that demonstrate identity. The characteristics of entities are:

  • Two entities of the same type and with the same defined identity are considered the same entity. In other words, two instances of the same object with different attribute values but the same UUID are considered equivalent.
  • Entities are mutable, which in a technical level it means that they will have getters and setters.

Value Object

A Value Object is an object that is defined by it’s attributes and their values and not by any identity. In other words, we do not care if a value object is the same with the one we had before or with any other object that has the same attribute values. Value objects derive as well from the Ubiquitous language’s vocabulary. The characteristics of value objects are:

  • They are completely interchangeable, therefore
  • they are immutable and
  • have a short life cycle
  • They exist in compositional relationship with entity objects.

Services

A Service is a domain concept that does not have an identity nor has attributes, which in domain driven terms means that is not an entity or a value object. It is an operation or action that the model offers as an interface. The characteristics of services are:

  • The operation they model is offered by the business logic
  • The interface they represent is defined using other elements of the domain e.g entities
  • They do not have any state in the sense that any instance can be used by any client without affecting the outcome.

Aggregates

An Aggregate is a set of domain objects, entities and value objects, that can be grouped together to serve higher level business concepts.
Every aggregate is represented by the aggregate root, which is an entity and it’s ID is being used to identify the whole aggregate. This root is being used by any other object, which does not belong to the aggregate, to reference the aggregate. Also, entities internal to an aggregate can not be referenced by any other object or aggregate than the one they belong to.

An aggregate represents a conceptual boundary in the domain, meaning that is created, retrieved and stored as a whole and is always in a consistent state, which the root is responsible for, by enforcing the business rules.

Whenever we design and entity, we should decide if it will act as an aggregate root or it will be an entity internal to an aggregate. Since local entities are referenced only inside the aggregate, it is enough for their identity to be unique only in the aggregate boundaries. On the contrary, entities that are aggregate root need to have a unique identity throughout the whole system. At this point it should be noted that not every parent child relationship between two entities implies that the parent object is also the root of a potential aggregate.

If the entity can not be modified without making changes to another entity then it is probably an aggregate root. If the entity needs to be referenced by other aggregates then it is definitely a root entity. In all other cases we should look how is the entity going to be searched and modified inside the application

Factory

Factories is a common and I guess a well known pattern outside domain driven design. In the “Gang of Four design patterns” it’s introduced as “The Factory Method” and belongs to the creational design patterns. Although it’s not something new, it has an important role in Domain Driven Design. Despite the fact that factories do not correspond or represent anything in the domain, they are added to the domain layer’s responsibility because they:

  • Encapsulate the needed knowledge to create complicated objects like aggregates. The internal structure and dependencies of the object are shielded in the factory, which makes each creation atomic and enforces all the constraints of the produced object.

  • Provides an interface that reflects the goals and the needs of the client together with an abstract view of the created object. The created object is abstracted to the type that the client needs rather to concrete classes.

  • Remove the burden from domain objects of their creation, especially when these objects are associated with other entity and value objects. As has been mentioned before, the biggest strength in domain driven applications is the clear separation of the domain. Therefore, the usage of factories is one step forward to declutter domain objects.

As everything in this life, factory pattern can be abused when developers over-engineer solutions. A Factory is a good choice in one of the following scenarios:

  • The creation of the aggregate involves logic and rules that derive from the business.
  • The structure and content of the aggregate may be very different depending on the input data
  • The input data is so extensive that the factory will guide the creation using the builder pattern.

Repositories

The next and last pattern presented in the second part of this book is the Repository pattern. It is a pattern that is presented in the “Patterns of Enterprise Application Architecture” book as well.

In domain driven application , repositories is between the domain layer and the infrastructure layer, which is responsible for storing, retrieving and locating aggregates. Each aggregate root has a one to one relationship with the repository, which controls the transactional consistency and the invariants during the storage. A repository is an interface into an external data storage such as a relational database, a NoSQL database, a directory service or even a file system. On top of that, repository pattern facilitates testing by allowing the exchange of the actual storage implementation with a mock/dummy one.

Personal Thoughts

The second part of the book is also the most popular part according to What do we (not) know about DDD. Some of the patterns have been addressed and described in equally important and fundamental books of software engineering. Looking back, I wish I had read this book when I firstly started my career as a software developer. And this is because it presents in a simple and descriptive manner how technical patterns and approaches can depict the business concepts and rules