Domain-Driven Design Handbook

Domain-Driven Design Handbook

Source: https://medium.com/carvago-development/domain-driven-design-handbook-4d34b069bec4

How to apply

1. Hear the story
2. Understand terms
3. Extract scenarios
4. Select use cases
5. Model concepts
6. Write tests + code
7. Connect to the infrastructure

1. Hear the story

The first and most important step of programming. A person who knows the domain, the topic we are going to program, has to explain it to us. Such a person is known as a domain expert. We have to listen carefully.

Car dealer is the domain expert. The car dealer tells us what it means to sell cars, what happens all the time and what happens rarely. The car dealer tells us his story.

“As soon as a customer walks to my place, I have to sense whether it’s going to be a serious business case or just a wasted time. When I feel an opportunity, I present the best fitting cars, make offers and always ask at least for a phone number.”

2. Understand terms

The story is full of new terms for us, terms that repeat over and over again and have important meaning in the domain. Although we may find terms familiar from different domains, we shall explicitly ask for explanation of each important term. Once we understand domain terms, we have a better understanding of the told story. We shall never invent our own terms.

“As soon as a customer walks to my place, I have to sense whether it’s going to be a serious business case or just a wasted time. When I feel an opportunity, I present the best fitting cars, make offers and always ask at least for a phone number.”

• Customer is a person that is going to buy a car.
• Business case is a sales opportunity, or could be understood as a customer need.
• Car is a vehicle with four wheels and an engine.
• Offer is a concrete car with concrete price for concrete customer.

Map terms

Practice shows that terms in a written form aren’t enough for understanding. Any graphical form of terms and their relations is almost necessary. A mind map seems to be a way to describe our understanding back to domain experts and to colleagues.

You can read more about story and terms in the The Language article.

3. Extract scenarios

The story is the overall explanation of the domain expert daily work, may be abstract, and may be somewhat vague. To understand the domain well, we have to break the story down into smaller pieces that can capture all details. And these smaller pieces are called scenarios.

The dealer offers cars to the customer. This means the dealer has to understand customers’ needs, find appropriate cars, consider purchase price of the car and car costs and then offer each car for price. The dealer may add winter tires to keep the price higher and also the customer will try to bargain for a lower price.

4. Select use cases

We have to capture key scenarios that will be maintained by our system. Selecting use cases isn’t therefore only domain-related, now we are moving the boundary of the system we create. We define, in other words, capabilities of the system.

This is the first time we really model — we extract the most important parts of the domain and at the same time we ignore irrelevant parts that won’t appear in our system.

It is wise to visualize use cases by a diagram.

The dealer offers cars to the customer. T̶h̶i̶s̶ ̶m̶e̶a̶n̶s̶ ̶t̶h̶e̶ ̶d̶e̶a̶l̶e̶r̶ ̶h̶a̶s̶ ̶t̶o̶ ̶u̶n̶d̶e̶r̶s̶t̶a̶n̶d̶ ̶c̶u̶s̶t̶o̶m̶e̶r̶s̶’̶ ̶n̶e̶e̶d̶s̶, find appropriate cars, c̶o̶n̶s̶i̶d̶e̶r̶ ̶p̶u̶r̶c̶h̶a̶s̶e̶ ̶p̶r̶i̶c̶e̶ ̶o̶f̶ ̶t̶h̶e̶ ̶c̶a̶r̶ ̶a̶n̶d̶ ̶c̶a̶r̶ ̶c̶o̶s̶t̶s̶ ̶a̶n̶d̶ ̶t̶h̶e̶n̶ offer each car for price. The dealer may add winter tires to keep the price higher a̶n̶d̶ ̶a̶l̶s̶o̶ ̶t̶h̶e̶ ̶c̶u̶s̶t̶o̶m̶e̶r̶ ̶w̶i̶l̶l̶ ̶t̶r̶y̶ ̶t̶o̶ ̶b̶a̶r̶g̶a̶i̶n̶ ̶f̶o̶r̶ ̶a̶ ̶l̶o̶w̶e̶r̶ ̶p̶r̶i̶c̶e̶.

You can read more about extracting scenarios and use-cases in The Modeling article.

5. Model concepts

The model is extracted from domain terms and use cases. Modeling is a hard discipline, we have to understand both the domain deeply and we have to be good technicians. The model that fulfills both aspects is a good model.

Modeling means to extract important aspects of the domain that will be programmed in our system, and more importantly ignore irrelevant. Model should be validated by a domain expert, therefore the model must be represented in an understandable form.

The model should be visualized as a class diagram enriched by identification of entities, value objects, aggregates and their boundaries. These terms are explained in different sources like the DDD book or online articles, like Implementation model, and we won’t copy them here.

You can read more about technical concepts in The Simplify the object model article.

6. Write tests + code

Now it’s up to us, we are programmers and programming (+testing) is the job we do the best. We program the domain code, the domain code is completely independent from surrounding layers and therefore it’s very easy to unit test.

We start with tests first. Yes we don’t even have a class, but we already start to test it. Test first helps us define the class interface (methods are the interface of the class).

Well, what do we even test? What is the expected result? Test scenarios should come from the use case diagram.

We have 2 scenarios
• Find a car (not related to business case, so we’ll skip this one)
• Offer a car to customer

Ok, we can offer a car, and this offer will be stored in the system. Do we also somehow show the offer in the system?

Such questing forces us to think also about reading use cases, like “show all business cases”, “show offers for a given business case”. Such use cases don’t occur in the domain itself, but use cases lie in the boundary between the domain and the system, and we may have a need to express such use cases.

Let’s agree with our domain expert that our system will support
• Show offers for a business case

<?php

class OffersInBusinessCaseTest extends TestCase
{
    public function testOfferCarResultsInOneOffer(): void
    {
        $carUuid = Uuid::uuid4();
        $price = Price::create('10000', Price::EUR);

        $customerUuid = Uuid::uuid4();
        $businessCase = new BusinessCase($customerUuid);
        $businessCase->offer($carUuid, $price);

        $expected = [new Offer($carUuid, $price)];
        self::assertEquals($expected, $businessCase->showOffers());
    }
}

 

Now we have a failing test, and the test expresses all our key concepts — identifiers, value objects, entities, responsibilities and the behavior.

The only thing left is to implement the beautiful code.

You can read how to implement such code in The Implementation article.

7. Connect to the infrastructure

Domain interfaces

Domain layer contains our domain. Up to this point, it was everything we already discussed.

The domain contains interfaces that provide access to parts that are not our domain.

Imagine our domain use-case contains sending an e-mail, but our domain is not about sending emails. Then we can create an interface that will do the sending e-mail use-case. We use this interface in the domain layer and thus keep the domain layer clean, separated.

Another typical domain interface is a repository.

<?php

interface BusinessCaseRepository
{
    public function add(BusinessCase $businessCase): void;
    public function get(UuidInterface $uuid): BusinessCase;
}

 

You can read about repositories in The Repository article.

Infrastructure

Infrastructure refers to the fundamental facilities and systems, it consists of sewers, pipes, electrical nets, water supply, garbage collecting and so on.

Infrastructure is a set of services and systems that keep us alive. But it is also a set of services that we don’t want to bother with. Yes, we need sewers, but who wants to keep them clean? Or fix when something breaks?

Infrastructure is also disposable, interchangeable. What happens when our factory needs a better power supply? We simply change cables or suppliers. We cannot rely on a specific infrastructure because it would tie us down.

The software infrastructure has the same story, it makes our system alive, but it has to be interchangeable. Thanks to changeability we can postpone an important decision. What database do we have to use? Should it be PostgreSQL, MySQL? Does it have to be even relational? Will our application be transactional heavy or read heavy? How many requests will we serve? If we make infrastructure interchangeable, we don’t have to answer all these questions at the beginning of the project, we can postpone the decision or even change the decision later.

From the code point of view, the infrastructure layer implements domain interfaces.

Naïve implementation of a repository using Doctrine

<?php

final class DoctrineBusinessCaseRepository implements BusinessCaseRepository
{
    private EntityManagerInterface $entityManager;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    public function add(BusinessCase $businessCase): void
    {
        $this->entityManager->persist($businessCase);
        $this->entityManager->flush();
    }

    public function get(UuidInterface $uuid): BusinessCase
    {
        return $this->entityManager->find(BusinessCase::class, $uuid->toString());
    }
}

 

You can read more about persistence to relational database in
Doctrine Repository article
ORM… WTF? article
And a bonus topic Alternative Doctrine Mapping article

Summary

5 steps out of 7 aren’t coding. Domain-Driven Design is about
• Understanding the domain
• Discussions with domain experts
• Searching for use-cases and behavior
• Modeling

Only after we finish these steps we shall start programming.

Leave a Reply