DDD: Strategic vs Tactical Design
Two Levels of Domain-Driven Design
Domain-Driven Design (DDD) is often misunderstood as a single set of coding patterns. In reality it operates on two distinct levels: Strategic and Tactical. Understanding the difference is key to applying DDD effectively.
1. Strategic Design
Strategic design operates at a high altitude. It is concerned with defining the business problem, aligning teams, and determining overarching system boundaries.
Goal: Understand business capabilities and determine what to build.
Core Concepts
Subdomains - The specific areas of the business the software needs to address. A domain is rarely uniform; it contains:
- Core Domain - The competitive differentiator. Where you invest the most.
- Supporting Subdomain - Necessary but not a differentiator. Can be built in-house cheaply.
- Generic Subdomain - Commodity functionality. Buy or use off-the-shelf solutions (e.g. authentication, billing).
Bounded Contexts - Strict boundaries within which a specific domain model is valid and consistent. The same word can mean different things in different contexts - Customer in a Sales context is not the same object as Customer in a Support context.
classDiagram
namespace SalesContext {
class SalesCustomer["Customer"] {
+name
+creditLimit
+loyaltyTier
}
}
namespace SupportContext {
class SupportCustomer["Customer"] {
+name
+openTickets
+preferredChannel
}
}Context Maps - Document how Bounded Contexts relate and integrate with each other (e.g. via an Anti-Corruption Layer, Shared Kernel, or Customer/Supplier relationship).
Impact
High. Strategic decisions shape team organisation and high-level architecture. Deciding which Bounded Contexts map to separate services is a strategic call - one that is expensive to reverse. This is the level where Conway’s Law applies: teams build systems that mirror their communication structure.
2. Tactical Design
Tactical design operates at the code level. Once the boundaries (Bounded Contexts) are established, tactical design dictates how the software is built inside those boundaries.
Goal: Model complex business logic accurately and cleanly in code.
Core Concepts
Entities - Objects with a unique identity that persists over time. Two Order objects with the same data are still different orders if their IDs differ.
public class Order {
private final OrderId id; // identity
private OrderStatus status;
private List<LineItem> items;
}
Value Objects - Immutable objects defined entirely by their attributes. Two Money objects with the same amount and currency are the same thing - no identity needed.
public record Money(BigDecimal amount, Currency currency) {}
Aggregates - A cluster of Entities and Value Objects treated as a single unit for data changes. The Aggregate Root is the only entry point for mutations, enforcing invariants within the boundary.
public class Order { // Aggregate Root
public void addItem(Product p, int qty) {
if (this.status != OrderStatus.DRAFT) {
throw new IllegalStateException("Cannot modify a confirmed order");
}
this.items.add(new LineItem(p, qty)); // Entity inside aggregate
}
}
Domain Events - Represent something meaningful that happened in the domain. They decouple Bounded Contexts without tight integration.
public record OrderPlaced(OrderId orderId, Instant occurredOn) {}
Repositories - Abstractions for retrieving and persisting Aggregates. They hide storage details from the domain model.
public interface OrderRepository {
Optional<Order> findById(OrderId id);
void save(Order order);
}
Impact
Localised. These are day-to-day developer decisions that can be refactored without breaking the entire system. A poorly modelled Value Object can be fixed in an afternoon; a poorly drawn Bounded Context boundary can require months of migration.
Strategic vs Tactical at a Glance
| Dimension | Strategic Design | Tactical Design |
|---|---|---|
| Level | Business / Architecture | Code |
| Goal | Define what to build | Define how to build it |
| Key outputs | Subdomains, Bounded Contexts, Context Map | Entities, Value Objects, Aggregates, Repositories |
| Audience | Architects, Product, Team leads | Developers |
| Cost of mistakes | Very high - hard to reverse | Lower - contained within a context |
| Applied when | Before writing code | During implementation |
Putting It Together
The two levels are complementary, not alternatives. Strategic design gives you the map; tactical design gives you the tools to build what the map describes.
A common mistake is jumping straight to tactical patterns (Aggregates, Repositories) without first doing the strategic work of identifying Bounded Contexts. The result is a well-structured model for the wrong problem.
Get the boundaries right first. The patterns inside will follow naturally.