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

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

Hey people, how are you?

I have recently read a lot of DDD articles and realized that many people (especially devs) are still quite confused about what DDD really is, its strengths, its weaknesses. Perhaps this is due to the huge variety of views on DDD that we find out there and the fact that DDD uses some terms that are part of a developer’s daily life that have totally different meanings.

The first thing to keep in mind is: No, DDD won’t save your life, and in some cases, it will only make it worse (at the beginning) so that you have to rewrite all of your code after a while.

Forgive me if I was too direct, but I need to say that. I promised myself I wouldn’t use that phrase but I couldn’t resist it: “There are no silver bullets.”

So, use it only in complex software, in other words, don’t use a bazooka to kill a fly! (I know, one more cliche).

Another thing that I would like to inform you about from the start: No, DDD is not just a way to fix your project directories.

Before we start breaking apart DDD let’s understand what BBM (Big Ball of Mud), also known as SPAGHETTI CODE is.

I’ll be very brief here and quote from Wikipedia:

A big ball of mud is a software system that lacks a perceivable architecture. Although undesirable from a software engineering point of view, such systems are common in practice due to business pressures, developer turnover and code entropy. They are a type of design anti-pattern

That is, basically, Spaghetti Code comes to life with the beginning of code creation without a solid study of the business rules and problems to be solved. To learn more, click here.

Now that we know what Spaghetti Code is, let’s get down to business!

Let’s talk about Domain.
For starters, I would like to make something very clear, Domain x Domain Model.

Domain = Your Business

Domain Model = Code that represents this business

DDD x Development Time

Unfortunately, I bring here a truth that many do not want to accept.

Yes, DDD greatly increases software delivery time and I will explain why.

DDD forces you to follow patterns that will often result in more classes, more complexity (at first glance), more entities, more services, which will help you in the long run (maintainability), but if you need something small that every second makes a difference to your delivery and you are not concerned about maintaining your software, do not use DDD.

DDD Topics

The DDD is divided into 2 major topics:

  • Strategic
  • Tactical

Did you notice that I mentioned the strategic one first? It was not a coincidence. Do not start applying DDD by Tactical (creation of Entities, Repositories, Value Objects and etc.). Spend time on Strategic because that’s where your architecture will be defined!

Strategic

Before I start talking about the strategic part of DDD, I would like to reinforce a very important concept at this stage, Domain Experts (DEs).

Domain Experts are people with a good knowledge of the problem to be solved in that specific domain. We can say that today Product Managers play this role, but don’t limit yourself to them, I’m just trying to create a quick understanding of the term.

But what do I, the developer, have to do with the business?

And the answer is simple, EVERYTHING!

Developers need to understand the business, and for this to happen there must be intense interaction between devs and DEs.

Here is a tip for devs: Before coding, stop. Think about if a feature really makes sense for that domain, discuss with the DE if there is a simpler way. Don’t first accept a solution given by DEs, it’s your role to really understand what you’re going to create!

Hmm, it makes sense, but how do I make DEs understand a technical language and how can I understand the business language?

That’s exactly where the Ubiquitous Language comes in!

But before I start talking about the ubiquitous language I want to show you something very important.

Do you realize that DDD is directly linked to the Manifesto principles for agile software development?

When we talk about the strong interaction between developers and Domain Experts we are referring to 2 of the 4 principles of the manifest, and they are:

  • Individuals and interactions over processes and tools
  • Customer collaboration over contract negotiation

With that in mind, let’s talk more about the ubiquitous language.

Ubiquitous Language

From now, there is no longer the business language (Domain) and the development language. The team will speak the same language!

Let’s say you’re in a real estate domain where DEs use the term HOUSE and devs use the term APARTMENT (including code). Can you imagine the confusion at the meetings? And to explain this to the new people coming in on the team?

In addition to simple terms, we must make clear in our code the actions that are used by DEs as well.

I think it is easier to explain with code!

  • Bad code example:
house.setTenant(user);
house.setRealState(realState);
house.setStartDate(LocalDateTime.now());
  • Good code example:
realState.rentAHouse(tenant, house, startDate);

Did you notice anything else in the code above? That’s right, DDD has ALL to do with Clean Code and some standards.

KISS: Keep it simple stupid

YAGNI: You ain’t gonna need it

DRY: Do not repeat yourself

TDA: Tell don’t ask

SoC: Separation of Concerns

I will not explain each of these patterns, but I leave here a very interesting link!

Do you realize that DDD is nothing more than the use of various concepts already existing but in a simplified way?

The actual conclusion will take us to the next step of our journey, which is the division of contexts, that is, we will now make the famous Divide (your domain) to Conquer.

Yes, DDD also advocates that you have totally limited and independent contexts, so that your architecture is as clean as possible and we can do that using the Bounded Context approach.

Bounded Context

I believe that the heart of DDD lives here.

Imagine the following scenario:

You have the Ship Company domain (sell ships) and in it, you have some entities and value objects such as: Profit and Ship.

Let’s say that the Profit has an attribute called `net`, responsible to specify the net gains, and the Ship has an attribute called `net` to specify the number of nets that the boat has.

Let’s go to the code:

public class Profit {
private Double net;
private Double gross;
private Company company;
}
public class Ship {
private Integer net;
private List<Person> crew;
}

Now let’s say we are writing code to validate some net profit to take a business decision of the company for one ship.

When we open the entity and look at the methods of class Ship we will find the method getNet(), and when we open the entity Profit we will find the same method, getNet() with different data-type.

Did you realize that Net for the Financial context has one meaning and for the Fishing context has another?

That’s exactly where the Limited Context comes in.

As we are talking about 2 contexts, we should have independent applications for each context (in this case Financial and Fishing). That is, a domain can be divided into subdomains that are separated into contexts.

The Bounded Context or Limited Context has 3 key points:

  • Delimited Space (External Interface)
  • Each context has its own ubiquitous language.
  • Each context has its own architecture (Independent Implementation)

Are you thinking the same thing about contexts like me?

That’s right, you can have this strategy as the foundation for building a microservice architecture!

By creating a microservice using DDD, you are automatically already delimiting space (context), creating your own ubiquitous language for each microservice and also gaining the freedom to choose which architecture you want to build them on.

Imagine you now have a microservice representing the Financial context created using Spring Boot, a microservice representing the Fishing context using Quarkus, and another representing the Marketing context written in Node.js.

As a result, you now have 3 fully independent microservices, with independent deploys and independent code, including existing independencies.

Can I create a new marketing campaign? If your business (based on your domain) says that you need to start a new marketing campaign, you don’t need to change anything in the other 2 microservices, meaning your contexts are totally isolated.

Did you realize that we have just encompassed the other 2 points of the agile manifesto?

  • Working software over comprehensive documentation
  • Responding to change over following a plan

Now with our standalone contexts, we have the facility to respond to changes faster (standalone deploys) and with the ubiquitous language, we have clean code, which doesn’t require that extensive documentation. (Tests are the better documentation for a good microservice)

Below is an image that clearly defines this division!

Reality shock moment

Microservices bring, in addition to great benefits, a lot of complexity to its architecture.

I could write a few lines here about the disadvantages of using microservices but I’ll quote a great author named Chris Richardson:

This solution has a number of drawbacks:

Developers must deal with the additional complexity of creating a distributed system:

Developers must implement the inter-service communication mechanism and deal with partial failure

Implementing requests that span multiple services is more difficult

Testing the interactions between services is more difficult

Implementing requests that span multiple services requires careful coordination between the teams

Developer tools/IDEs are oriented on building monolithic applications and don’t provide explicit support for developing distributed applications.

Deployment complexity. In production, there is also the operational complexity of deploying and managing a system comprised of many different services.

Increased memory consumption. The microservice architecture replaces N monolithic application instances with NxM services instances. If each service runs in its own JVM (or equivalent), which is usually necessary to isolate the instances, then there is the overhead of M times as many JVM runtimes. Moreover, if each service runs on its own VM (e.g. EC2 instance), as is the case at Netflix, the overhead is even higher.

There are a lot of drawbacks when we want to follow this ideology.

Now with some clear concepts, we need to create a diagram that allows us to clearly visualize these divisions and integrations, and that’s where the Context Map comes in.

Context Map

Forget all those “fancy” context map descriptions you’ve read. Context Map is nothing more than a diagram (or any other approach) that allows you to clearly visualize your domain, subdomains, and contexts with their limitations and dependencies.

How to map dependencies?

Simple, we can use the concept of Upstream and Downstream to create a diagram explaining the communication and influence of each context over another.

But what do these words mean?

  • Upstream: Influencing Context
  • Downstream: Influenced by an upstream change

Nothing better than a practical example to fix an idea, so here we go:

Let’s say we are once again in our Ship Company domain and we have the following rule:

Whenever there is a change in the Financial context, the Marketing context will be affected, but this change will not affect the Fishing context, for instance in the relationship between the Financial context and the Marketing context, Financial would be Upstream and Marketing would be Downstream. Already in the relationship between Financial and Fishing, there is no kind of influence, so the diagram should report a simple communication.

That simple!

Now imagine the following scenario: You need to integrate a new microservice into a legacy system and do not want to change the communication protocol of the legacy system (since it is very expensive to maintain).

Let’s say this legacy service expects to receive a SOAP request but your microservice is sending a REST request, how to do it?

One way to solve this is to use the famous Anti-Corruption Layer (ACL).

But how to implement it?

One of my favorite definitions can be found in an article on the Microsoft website.

Implement a facade or adapter layer between different subsystems that don’t share the same semantics. This layer translates requests that one subsystem makes to the other subsystem. Use this pattern to ensure that an application’s design is not limited by dependencies on outside subsystems. This pattern was first described by Eric Evans in Domain-Driven Design.

In other words, use a middleware responsible for intercepting and translating requests between certain systems.

Cool Julio, but how can I describe this to my diagram?

Very simple, for every communication that uses this pattern, create a box written ACL next to the context that is receiving the request.

Here’s an example below:

Another point where Context Map can help a lot is in identifying context types.

We can have 3 types of contexts:

  • Core Context — The heart of your domain (where the money is)
  • Supporting Context — As the name implies, these are responsible for supporting the core context.
  • Generic Context — The other contexts that you know exists but are not so important to your domain

These 3 terms will help you define in which context you should invest your efforts the most!

I think that is too enough information for only one article, so I’ll explain the Tactical section in another article (Part 2), which you can read here

https://medium.com/tradeshift-engineering/my-vision-as-a-software-engineer-about-ddd-domain-driven-design-part-2-973bcf5a9848

I hope you are enjoying my vision!

--

--