Strategic DDD identifies business priorities, bounded contexts, and relationships between models and teams. Tactical DDD implements behavior inside a context using patterns such as entities, value objects, aggregates, and domain events. Start with the strategic questions, then choose only the tactical patterns that the context's business rules and complexity justify.
What Is the Difference Between Strategic and Tactical DDD?
Domain-Driven Design has two distinct parts:
- Strategic DDD - the big picture. Where are the boundaries? How do teams collaborate? Which parts of the domain are most important?
- Tactical DDD - the implementation details. Aggregates, entities, value objects, domain events, repositories.
Most developers jump straight to tactical patterns. They learn about aggregates and value objects and apply them everywhere. But without strategic thinking, you end up with perfectly modeled code inside the wrong boundaries.
| Concern | Strategic DDD | Tactical DDD |
|---|---|---|
| Focus | Business priorities and model boundaries | Behavior and consistency within a model |
| Tools | Subdomains, bounded contexts, context maps | Entities, value objects, aggregates, domain events |
| Key question | Which model belongs to which context? | Where should this rule be enforced? |
| Participants | Domain experts, developers, and responsible teams | Domain experts and developers implementing behavior |
| Feedback | Integration reveals boundary problems | Working code reveals missing concepts and invariants |
Strategic DDD
Strategic DDD answers the question: how should we divide and organize the system?
Bounded Contexts
A bounded context defines a boundary where a domain model applies. The same real-world concept has different meanings in different contexts.
"Customer" in the Sales context has a name, company, and purchase history. "Customer" in the Billing context has a payment method and billing address. Same word, completely different models.
Context Maps
A context map shows the relationships between bounded contexts:
- Partnership - two teams cooperate, both sides adapt
- Customer-Supplier - upstream provides, downstream consumes
- Conformist - downstream adopts the upstream model as-is
- Anti-Corruption Layer - downstream translates the upstream model into its own language
- Open Host Service - upstream provides a published API
- Shared Kernel - two contexts share a small piece of the model
Subdomains
Not all parts of the system are equally important:
- Core subdomain - what differentiates your business, such as an order-matching algorithm or a specialized pricing engine. Prioritize its modeling and development effort.
- Supporting subdomain - important but not differentiating (inventory management). Use simpler models and moderate investment.
- Generic subdomain - a capability with established solutions that does not differentiate this business. Email delivery or authentication often qualifies, but an identity provider would classify its authentication product differently.
This classification guides investment, while actual rule complexity guides the implementation. A core capability does not need every tactical pattern, and a supporting capability can still have complex rules that benefit from a rich domain model.
Ubiquitous Language
Ubiquitous language is a shared vocabulary between developers and domain experts within a bounded context. The same terms appear in conversations, documentation, and code:
// The code reads like the domain experts talk
public interface IOrderActions
{
void Place();
void Confirm();
void Ship();
void Cancel(string reason);
}
This interface illustrates names rather than a complete lifecycle.
Choose names domain experts use instead of hiding distinct operations behind UpdateStatus().
Tactical DDD
Tactical DDD answers the question: how do we model within a bounded context?
Building Blocks
- Entity - has identity and a lifecycle (
Order,Customer) - Value Object - defined by its attributes (
Money,Address,Email) - Aggregate - a consistency boundary (
Orderwith itsLineItems) - Domain Event - a record of something that happened (
OrderPlacedEvent) - Domain Service - domain behavior that does not naturally belong to an entity or value object (
PricingService) - Repository - a persistence abstraction (
IOrderRepository) - Factory - complex object creation (
Order.Create(...)) - Specification - encapsulated query rules (
PremiumCustomerSpec)
Aggregate Example
This standalone domain example uses USD prices and keeps the order lifecycle explicit.
Put these types in Order.cs; an application handler supplies catalog prices, saves the aggregate, and dispatches its recorded events after a successful commit:
public enum OrderStatus { Draft, Placed }
public abstract record OrderEvent(Guid OrderId);
public sealed record OrderCreatedEvent(Guid OrderId) : OrderEvent(OrderId);
public sealed record OrderPlacedEvent(Guid OrderId, decimal TotalUsd)
: OrderEvent(OrderId);
public sealed record LineItem(Guid ProductId, int Quantity, decimal UnitPrice);
public sealed class Order
{
private readonly List<LineItem> _lineItems = [];
private readonly List<OrderEvent> _events = [];
private Order(Guid customerId)
{
Id = Guid.NewGuid();
CustomerId = customerId;
_events.Add(new OrderCreatedEvent(Id));
}
public Guid Id { get; }
public Guid CustomerId { get; }
public OrderStatus Status { get; private set; } = OrderStatus.Draft;
public decimal TotalUsd => _lineItems.Sum(i => i.Quantity * i.UnitPrice);
public IReadOnlyList<LineItem> LineItems => _lineItems.AsReadOnly();
public IReadOnlyList<OrderEvent> Events => _events.AsReadOnly();
public static Order Create(Guid customerId)
{
if (customerId == Guid.Empty)
throw new ArgumentException("A customer is required.");
return new Order(customerId);
}
public void AddLineItem(Guid productId, int quantity, decimal unitPrice)
{
if (Status != OrderStatus.Draft)
throw new InvalidOperationException("Only draft orders accept items.");
if (productId == Guid.Empty || quantity <= 0 || unitPrice < 0)
throw new ArgumentException("Invalid line item.");
_lineItems.Add(new LineItem(productId, quantity, unitPrice));
}
public void Place()
{
if (Status != OrderStatus.Draft || _lineItems.Count == 0)
throw new InvalidOperationException("A nonempty draft is required.");
Status = OrderStatus.Placed;
_events.Add(new OrderPlacedEvent(Id, TotalUsd));
}
}
Common Mistakes
Mistake 1: Tactical Without Strategic
Applying aggregates, value objects, and domain events without first understanding bounded contexts. Result: a tangled monolith with rich domain objects that have the wrong boundaries.
Mistake 2: DDD Everywhere
Using tactical DDD patterns for every part of the system. The user settings page doesn't need aggregates and domain events. It needs simple CRUD.
Mistake 3: Ignoring Context Maps
Building modules in isolation without mapping their relationships. Result: inconsistent integration, duplicated concepts, and unclear ownership.
Mistake 4: One Model to Rule Them All
Creating a single Customer entity used across Sales, Billing, and Support. Each context needs its own model of the customer.
Decision Framework
Summary
Strategic vs Tactical DDD:
- Strategic first - establish candidate boundaries, then refine them through implementation feedback
- Bounded contexts define where models apply - same word, different meaning
- Context maps define how contexts relate - partnership, ACL, shared kernel
- Subdomain classification guides investment, while rule complexity guides implementation
- Tactical patterns belong inside bounded contexts - aggregates, entities, value objects
- Choose patterns selectively - a context does not need every tactical pattern to use DDD
Revisit the boundaries when the code or integration work exposes a mismatch in language or ownership.
Thanks for reading.
And stay awesome!
Frequently Asked Questions
What is the difference between strategic and tactical DDD?
Strategic DDD is about dividing the system: identifying subdomains, defining bounded contexts, and mapping their relationships. Tactical DDD is about modeling inside a boundary with aggregates, entities, value objects, and domain events.
Which comes first, strategic or tactical DDD?
Begin with strategic questions and refine the boundaries as implementation teaches you more. Applying tactical patterns inside the wrong boundaries produces well-modeled code that still couples the wrong things together.
What are the three types of subdomains in DDD?
Core subdomains differentiate your business and deserve the most investment. Supporting subdomains are important but not differentiating. Generic subdomains have established solutions and are candidates for reuse or purchase; their classification depends on the business.
Do all parts of a system need tactical DDD patterns?
No. Reserve full tactical DDD for core subdomains with complex rules. Supporting subdomains can use simpler models, and generic subdomains are usually best served by CRUD or off-the-shelf solutions.
What is a context map in DDD?
A context map documents the relationships between bounded contexts: partnership, customer-supplier, conformist, anti-corruption layer, open host service, and shared kernel. It makes integration and team dependencies explicit.



