Skip to main content

An Overview of DDD — Strategic Design and Tactical Design

In the previous chapter, we saw what happens when business logic is scattered across controllers and models. We offered DDD as the answer, but the word "DDD" covers more ground than what that chapter showed.

This chapter is a map. It lays out what layers DDD has and which of them this guide covers in depth. We do not define the individual terms here. Chapter 5 defines the strategic terms, and Chapter 7 onward defines the tactical ones.

There are two kinds of design

DDD consists of two layers.

  • Strategic design: how to divide the domain. Where to draw the boundaries, and over what range each model holds
  • Tactical design: how to build what is inside a boundary. How to use value objects, entities, aggregates, and repositories

Evans's original book is organized into four parts; Part II covers the building blocks of a model-driven design and Part IV covers strategic design. Part II is the tactical side, Part IV the strategic one. Tactical comes first in the book, but that is the order of the book, not the order in which you apply them.

The Fat Controller problem from the previous chapter is solved with tactical tools. Gather the conditions for confirming an order into Order::confirm(), and the rule has a home.

That, however, is about how to build the inside of a line. How far the model called Order actually holds is a separate question.

What happens when you adopt only the tactical patterns

The tactical patterns have a visible shape, which makes them easy to adopt on their own. Split directories into Domain/ and Application/, create a Money class, extract an OrderRepositoryInterface. All of this can be copied mechanically.

The problem is that the result is not always a domain model.

Martin Fowler calls this the Anemic Domain Model. The objects are there, the relationships and structure look rich, but when you look at behavior they are little more than bags of getters and setters, with all the logic living in service classes.

it's contrary to the basic idea of object-oriented design; which is to combine data and process together

— Martin Fowler, AnemicDomainModel

The cost Fowler points out is concrete. You pay all the costs of a domain model and get none of the benefits. The object-relational mapping work is still there and the class count still grows, but what you end up with is a transaction script.

// A value object in name only. It has no behavior and is just a container.
final class Money
{
public function __construct(
public readonly int $amount,
public readonly string $currency,
) {}
}

// The calculation lives entirely on the service side
final class PriceService
{
public function add(Money $a, Money $b): Money
{
// The judgment about what happens when the currencies differ lives here
return new Money($a->amount + $b->amount, $a->currency);
}
}

This code has a type called Money, but the rule "you cannot add amounts in different currencies" lives outside Money. The same judgment gets written again in another service, and one day only one of the two gets fixed.

The tactical patterns are tools for deciding where a rule lives. Creating types is not the goal. We come back to this distinction with real implementations from Chapter 8 onward.

How strategic and tactical relate

What strategic design decides is the range over which a model holds. Only once that range is fixed can you use the tactical patterns inside it.

The arrows are containment. One bounded context holds several aggregates, and one aggregate holds several entities and value objects.

If the line is in the wrong place, no amount of care inside it helps. We look at a concrete case — the word "product" meaning different things in different places — in the next chapter. Introducing value objects or carving out aggregates does not fix it, because the position of the line is what is wrong.

Which layer this guide covers where

LayerChaptersHow much depth
Strategic design5-6How to draw boundaries, and a workshop for finding them
Tactical design7-13With implementation code. The center of this guide
Implementing the layers14-22How to arrange them in Laravel
Comprehensive practice25-26Putting it together on the order system

The weight sits on the tactical side because the goal is to leave you able to write Laravel code. Strategic design is a judgment about scope, and the material for that judgment differs by business, so there is less that code can show.

Some things are out of scope. The "large-scale structure" of Evans's Part IV (responsibility layers, evolving order) is about the scale at which several teams develop in parallel, which sits outside what this guide assumes (a single Laravel application).

A map of the terms

Here are the terms that appear in later chapters, grouped by layer. We do not define them here — only where each one is covered.

Strategic design

TermWhere it first appears
SubdomainChapter 5
Bounded contextChapter 5
Ubiquitous languageChapter 5
Context mapChapter 5
Anticorruption layerChapter 5

Tactical design

TermWhere it first appears
Value objectChapter 8
EntityChapter 9
AggregateChapter 10
Domain serviceChapter 11
Domain eventChapter 12
RepositoryChapter 17

Where to start

When you bring DDD into an existing project, whether to start from the strategic or the tactical side depends on the situation.

  • Start tactical when the boundaries are already clear. If one business area lives in one application and the meaning of the model does not shift from place to place, you can start by introducing value objects
  • Start strategic when the same word means different things in different places. If you already know that "product," "user," or "case" refers to different things in different departments, draw the lines first

When you cannot tell, laying out the flow of the business in the workshop from the next chapter makes it visible. The places where the business people say "that's a different matter" and cut the conversation are usually the candidates for a boundary.

Summary

PointContent
Two layersStrategic = where to draw the lines / tactical = how to build the inside
OrderYou cannot build the inside until the line is fixed. But start tactical when the boundary is obvious
Adoption in name onlyIf you create types but the rules live outside them, you pay the cost and get no benefit
This guide's weightTactical design and implementation. Chapters 5-6 cover how to decide the scope

In the next chapter, we look concretely at how to draw the lines.

Further reading