My vision, as a Software Engineer, about DDD (Domain Driven Design) — Part 2

Júlio Falbo
Tradeshift Engineering
10 min readSep 17, 2019

Heey people!

This is the second part of the series where I’m trying to show my vision, as a Software Engineer about DDD.

In the first part, I did an introduction about some ideas around DDD and I wrote about the Strategic topic. Now I’ll talk about the Tactical topic.

Here we go!

Tactical

One of the most important things here is to know that DDD does not require you to follow ANY specific TECHNOLOGY. DDD’s efforts are focused on domain definition and not on telling you that Spring Boot is better than Quarkus or RabbitMQ is better than Kafka.

Knowing this we can focus on what matters in this first tactical step, the definition of architecture.

At this point let’s look back at the Clean Code and use a principle we learned from SOLID, the Dependency Inversion Principle for architecture level.

Oops, does that remind you of anything?

That’s right, SOLID’s Dependency Inversion Principle becomes Dependency Rule, which is at the heart of Clean Architecture. Also, the Single Responsibility Principle becomes an architecture-level Common Closure Principle, which is also very helpful in helping us define our services.

We must keep in mind that our core layers should not depend on outer layers, and the image below, created by our beloved Uncle Bob to convey the idea of ​​Clean Architecture, helps us realize this.

Clean Architecture by Uncle Bob

Another good practice here is to write adapters so that our entities are not exposed directly. (Can you see another pattern here?)

Exactly, in most cases, these adapters will be DTOs (Data Transfer Objects) or any implementation responsible to convert your data.

The DTO is an object responsible for being your entity’s public view of other services, thus following the SOLID Dependency Inversion Principle pattern.

Let’s say a microservice sends new data to an existing API of your Core Service, breaking the contract.

If there is no adapter (DTO) to intermediate this request, the entity would be vulnerable to external changes, that is, the addition of new data in the contract would result in the need to change the entity, even if for business, this new data is not relevant.

In other words, DTO shields the domain totally from outside interference.

You probably realized that I mentioned the above entities, and that’s exactly what we’re going to talk about now, domain modeling.

According to Eric Evans, we have 3 options for modeling our domains and they are:

  • Entity
  • Value Object
  • Service

Entity

Before explaining entities I want to say one thing.

DDD Entity has NOTHING to do with Hibernate Entity, they are different concepts but may converge at some point.

Martin Fowler has this definition of what Evans calls Entity Object.

“Objects that have a distinct identity that runs through time and different representations. You also hear these called ‘reference objects’.”

Although I think the above explanation is quite straightforward, I will give mine.

Entities are actors important enough to be unique and have identifiers, simple as that!

Example: In our Financial context some entities would be Profit, Cost, Company, etc.

public class Company {
private Long id;
private String name;
private Profit profit;

}

Value Object

Martin Fowler defines the Value Object like this

Objects that matter only as the combination of their attributes. Two value objects with the same values for all their attributes are considered equal.”

That is, it is an object in which we are interested only in its attributes, not its identity. Therefore Value Objects do not have identities and should be objects less complex than entities and immutable, thus facilitating their creation and manipulation.

Example: In our Fishing context a Value Object would be a Type.

//Entity
public class Ship {
private Long id;
private Type type;

}
// Value Object
public class Type {
private String type;
private Integer height;
private Integer width;

}

Another very important thing is that when we map this entity and this value object in the database, only the entity will become a table, the fields of the Value Object will be contained in the entity table.

A very simple way to map this model using JPA would be to use the @Embedded and @Embeddable annotations.

To learn more, click here.

Please note: An Entity may have 0 or n Value Objects just as the Value Objects themselves may have other Value Objects inside.

Before we talk about Services, let’s quickly understand the concept of the Rich Domain Model and the Anemic Domain Model.

Rich Models and Anemic Models

Rich Domains are models that have full control of their data and do not rely on external objects to manipulate them and Anemic Domains are models that rely on other classes to validate their data.

  • Anemic Domain Model Example:
public class Ship {
private Long id;
private Type type;
private Company company;
public setType(Type type){
this.type = type;
}
public setCompany(Company company){
this.company = company;
}
}
public class ShipService {
public Ship createACruiseShip(Company company){
if(Objects.isNull(company){
throw new RuntimeException(“Company is required.”);
}
Ship ship = new Ship();
ship.setType(ShipType.CRUISE);
ship.setCompany(company);
return ship;
}
}
  • Rich Domain Model Example:
public class Ship {
private Long id;
private Type type;
private Company company;
public Ship(Type type, Company company{
if(Objects.isNull(company) || Objects.isNull(type)){
throw new RuntimeException(“Type and Company are required.”);
}
}
}

As you can see, the build validation is within the object itself, thus ensuring that you always have a Ship with a company and type.

Cool, but what’s the point?

Now you have centralized, isolated and independent, a place where all the rules regarding this entity itself are!

That means you avoid code duplication and that famous “where is this rule implementation at all?”

And that’s exactly the thought DDD wants you to have!

Now let’s talk about Services.

Service

When we talk about Service in an anemic domain (Domain Service in this case), it would be an object where the business logic of one or more entities is concentrated. When using this strategy make sure your Service is stateless, meaning it has no state and only pure functions.

Pure functions are functions (or methods) that do not change the value of any object outside it, thus avoiding side effects and guaranteeing the same output for certain inputs, ie it needs to be completely deterministic.

Now when we talk about domain rich services, it would be a layer responsible only for external communications in a single context.

Example: Perform REST communication with a microservice from another context.

Talking about communication, I would now like to talk about Domain Events.

Domain Events

This is one more point where you will find thousands of different definitions and even hooks for some types of architectures like Event Sourcing and CQRS.

But following the purpose of the article, I want to keep the understanding of the strategies simple and in my view, the Domain Events strategy is nothing more than an event fired from a Context X microservice with information that may interest other contexts.

Usually, this strategy is used when we would like to have asynchronous behavior between our contexts, since the context responsible for publishing the message will not expect a return, for instance, the action that the consumer will take based on this event does not matter to him. For it is always important to think about notifying past actions.

In this case Context X (publisher) will publish an event/message in a message broker (RabbitMQ, Kafka, HornetQ, SQS, etc.) and the contexts that are interested in this message will register as consumers of this event/message.

Doing this, the interested context will receive the message as soon as it is published, following the FIFO pattern of queues. (First In, First Out) *.

* In the case of Kafka are topics and FIFO is guaranteed only within the partition, but do not care about it now, the final concept is the same and what matters here is the DDD.

Important to say that each publisher can have 0 or more consumers

To better understand, let’s take a practical example:

  • Action: A Ship was bought.
  • Financial Context: Publish an event called “shipSold” to a message broker.
  • Marketing Context: Consume all the messages from the “shipSold” event to send an email to the buyer offering some service.

Now, we come to a part that generates much controversy but is extremely simple!

Package and Module Organization

I see many projects where the organization of folders is based on responsibilities and not contexts, for example:

  • Organization by Responsibility
com.juliofalbo.ddd
.services
ShipService.java
ProfitService.java
.repositories
ShipRepository.java
ProfitRepository.java
.entities
Ship.java
Profit.java
.adapters
ShipAdapter.java
ProfitAdapter.java
  • Organization by Context
com.juliofalbo.ddd
.fishing
.services
ShipService.java
.repositories
ShipRepository.java
.entities
Ship.java
.adapters
ShipAdapter.java
.finances
.services
ProfitService.java
.repositories
ProfitRepository.java
.entities
Profit.java
.adapters
ProfitAdapter.java

Which one is right?

Both. That’s right, there is no right or wrong in this case!

I believe this is more a team consensus than precisely a DDD strategy. So I will give my opinion here, ok?

If you have one module or microservice for each context, you will not have this problem as you will not have two 2 concurrent contexts (ideal model). So, in this case, the organization of responsibility is my choice (obviously).

But we know that often real life is different. So if in our module or application we have more than 1 context (usually in monolithic applications), I advise the use of the organization by contexts, because with that we can get closer to the strategies proposed by DDD.

Note: As stated at the beginning of the text, it is not worth organizing your packages if you have ignored everything you have read so far. It would be the same as ordering the Big Mac menu with Coke Zero because you want to lose weight. :-)

Aggregates

Once again something confusing and in practice, you do every day!

According to Martin Fowler, a DDD aggregate is a cluster of domain objects that can be treated as a single unit. An example may be order and its line-items, these will be separate objects, but it’s useful to treat the order (together with its line items) as a single aggregate.

With the example, he cites it is easier to understand what aggregate is, but I will try to simplify it further.

An aggregate is a set of Entities and Value Objects that do not make sense alone.

Every aggregate has a root entity, which will be responsible for providing all methods involving business rules that will modify its child entities or change the value of the attributes. (Following the Rich Domain Model)

Following the logic of the example cited by Grandmaster Martin Fowler, it makes no sense for an Order Item to exist without an Order, so in this case, the Order will be the Root Entity while the Order Item will be the Aggregate Entity.

As you can see, by using the concept of Aggregates we end up creating complex objects and this makes it very difficult to create them. That’s why DDD advocates the use of Design Patterns to help us create these objects.

Factory

DDD factories are nothing but the use of Creation Patterns of GoF (Gang of Four), for example.

Some of these patterns are: Factory Method, Abstract Factory, and Builder.

These patterns advocate the creation of an interface or class that will be responsible for creating your objects.

And yes, I put the builder here because the end goal is the same, even though its implementation is different from a factory.

Repository

I will start here with the dictionary definition of the word repository:

“Place to keep, file, collect something.”

As the name implies, our repositories will be responsible for storing our Entities, Aggregates and Value Objects.

Please do not confuse the DDD Repository concept with the Spring Boot Repository concept.

In the context of Spring Boot, a repository is an interface responsible for creating communication with your database (also known as DAO).

Although Spring’s Repository concept was based on DDD, the definition proposed by DDD is not limited to just one database as storage.

In the DDD context, a repository can be, from a database to a simple list (collection) in Java memory, where you can add your objects through the .add method, retrieve through the .get method maintaining the same state when it was inserted.

Nice Julio, now I can understand the topics of DDD but the pros and cons are not too clear.

Cool! So, I’ll make a summary of the pros and cons of DDD.

Pros

  • The whole company talking the same language, reduced risk of misunderstandings
  • Now you have a segregated architecture
  • Good integration with Business layer and Engineering layer
  • Maintainability. Smaller software is easier to maintain
  • Flexible. Now your services are independent
  • Domain and Architecture mapped. Side effects are not a surprise anymore
  • Quality. DDD gives us a lot of Patterns to create our architecture and our code. (SOLID, KISS, DRY, SoC, etc)

Cons

  • Complex architecture. As I explained above, microservices has some drawbacks
  • Additional efforts. Now we need to have a lot of meetings and spend some time mapping our domain
  • New mindset. For a good implementation of DDD, everyone needs to be aligned, both in vocabulary and ownership (here is the most important point of DDD)
  • Prioritize tasks. For DDD, technical debts are not tasks that will be in the backlog forever. It is really important to have a solid architecture with a quality code in production, this is also a value for the client.

Well, guys, I think that’s it.

I tried to pass on a little of what I have learned over the years acting as a DDD software developer and hope that after that DDD is no longer so obscure to you!

--

--