A Bounded Context is a boundary within which a specific domain model applies. Inside the boundary, every term has one precise meaning, and the same concept, like Order, can have a completely different model in each context. Bounded Contexts are the most important strategic pattern in Domain-Driven Design, and here's how to identify and implement them in .NET.
Most teams instead try to build one unified domain model, and it slowly turns into a class with 50 properties that nobody owns. Bounded Contexts prevent this by giving each part of the business its own focused model, with explicit boundaries and explicit integration points.
What Is a Bounded Context?
A Bounded Context is a boundary within which a specific domain model applies. Inside a Bounded Context, every term has a precise, unambiguous meaning.
The word "Order" means different things to different parts of a business:
- In Ordering, an Order is a customer's purchase request with line items and pricing
- In Fulfillment, an Order is a picking list with warehouse locations and shipping labels
- In Billing, an Order is an invoice with payment terms and tax calculations
Each of these is a different model of "Order" - with different properties, different behaviors, and different rules.
A Bounded Context makes this explicit: each context has its own Order class, and they don't need to match.
Why Bounded Contexts Matter
Without Bounded Contexts, teams try to build a single unified model - one Order class that serves Ordering, Fulfillment, and Billing.
This "God Model" approach creates:
- Classes with 50+ properties - most irrelevant to any single use case
- Coupling - a change for Billing breaks Fulfillment
- Unclear ownership - who owns the
Orderclass? - Slow development - every team coordinates on the same model
Bounded Contexts solve this by giving each team their own model. Models are simpler, focused, and independently evolvable.
Bounded Contexts vs Subdomains
These are related but different:
- A Subdomain is a problem space concept - a natural division of the business domain
- A Bounded Context is a solution space concept - a boundary you define around a model
Ideally, each Bounded Context aligns with one subdomain. In practice, one subdomain might span multiple Bounded Contexts, or you might start with a single Bounded Context covering multiple subdomains and split later.
Identifying Bounded Contexts
Look for these signals:
Different language. If the Sales team and the Fulfillment team use the same word to mean different things, there's a boundary. This is where the Ubiquitous Language earns its keep.
Different data. If two teams need different properties for the same concept, they need different models.
Different rules. If the same operation has different validation rules in different parts of the system, those parts are different contexts.
Organizational boundaries. Teams that work independently usually represent different Bounded Contexts.
For a typical e-commerce system, you might identify:
Bounded Contexts in Code
In a Modular Monolith, each Bounded Context maps to a module:
Modules/
Sales/
Domain/
Product.cs ← Sales-specific product (pricing, promotions)
Application/
Infrastructure/
Ordering/
Domain/
Order.cs ← Ordering-specific order (line items, status)
Product.cs ← Just ProductId + Name + Price, not the full catalog
Application/
Infrastructure/
Fulfillment/
Domain/
Order.cs ← Fulfillment-specific order (warehouse, shipping)
Application/
Infrastructure/
Each module has its own Product and Order - different classes, different properties, different rules.
The Sales Context Product
// Sales Context
public sealed class Product : AggregateRoot
{
public string Name { get; private set; }
public string Description { get; private set; }
public Money BasePrice { get; private set; }
public Money? SalePrice { get; private set; }
public bool IsActive { get; private set; }
public List<Category> Categories { get; }
}
The Fulfillment Context Product
// Fulfillment Context
public sealed class Product : Entity
{
public string Sku { get; }
public string WarehouseLocation { get; }
public Weight Weight { get; }
public Dimensions Dimensions { get; }
public int QuantityInStock { get; private set; }
}
Same real-world concept. Completely different models. This is intentional.
Communication Between Bounded Contexts
Contexts communicate through well-defined integration points - not by sharing models. Those relationships belong on a context map, where the technical integration and the team dependency are both explicit.
Integration Events
The most common approach: publish events that other contexts consume.
// Ordering Context publishes
public sealed record OrderPlacedIntegrationEvent(
Guid OrderId,
Guid CustomerId,
List<OrderItemDto> Items,
decimal TotalAmount);
// Fulfillment Context consumes
public sealed class CreateFulfillmentOrderHandler
: IIntegrationEventHandler<OrderPlacedIntegrationEvent>
{
public async Task Handle(OrderPlacedIntegrationEvent @event, ...)
{
// Create fulfillment order from the integration event
var fulfillmentOrder = FulfillmentOrder.Create(
@event.OrderId,
@event.Items.Select(i => new PickingItem(i.ProductId, i.Quantity)));
}
}
The integration event is a contract - a shared schema that both contexts agree on. It's not a domain event (which is internal to a context).
For reliable delivery, use the Outbox pattern.
Anti-Corruption Layer
When consuming data from another context, use an Anti-Corruption Layer to translate external models into your context's language:
// Fulfillment Context - translates Ordering language into Fulfillment language
public sealed class OrderPlacedTranslator
{
public FulfillmentOrder Translate(OrderPlacedIntegrationEvent @event)
{
var pickingItems = @event.Items
.Select(item => new PickingItem(
ProductId: item.ProductId,
Quantity: item.Quantity,
WarehouseLocation: _locationService.GetLocation(item.ProductId)))
.ToList();
return FulfillmentOrder.Create(@event.OrderId, pickingItems);
}
}
The ACL prevents the Ordering Context's model from leaking into Fulfillment.
Data Isolation
Each Bounded Context should own its data:
- Separate schemas -
sales.products,fulfillment.products - Separate databases - in microservices architectures
- No cross-context queries - never JOIN across context boundaries
When one context needs data from another, it maintains a local copy updated through integration events. The Fulfillment Context doesn't query the Sales database for product details - it maintains its own product data, synced through events.
This is the Database per Module pattern.
Context Mapping
Context Mapping describes the relationships between Bounded Contexts:
- Partnership - two teams cooperate and evolve together
- Customer-Supplier - one team provides, the other consumes
- Conformist - the consuming team accepts the supplier's model as-is
- Anti-Corruption Layer - the consuming team translates the supplier's model
- Shared Kernel - two contexts share a small, co-owned model
- Separate Ways - no integration, contexts are fully independent
For a deeper dive into relationships between modules, see my article on module communication patterns.
Common Mistakes
1. Single shared model across contexts. The "God Model" anti-pattern. If every context uses the same Order class, you have coupling, not boundaries.
2. Too many contexts. Each context has overhead (separate data store, integration events, mapping). Start with fewer, larger contexts and split when needed.
3. Contexts aligned to technical layers. "Database Context" and "API Context" are not Bounded Contexts. Contexts align to business capabilities.
4. No data isolation. If two contexts share database tables, they're not really separate. A change in one context's schema breaks the other.
Summary
Bound the model before the code. Bounded Contexts are the foundation of strategic Domain-Driven Design. They define clear boundaries where specific domain models apply, giving teams the freedom to model their part of the business independently.
Start by listening to the language. When the same word means different things to different people, you've found a boundary.
Thanks for reading, and stay awesome!
Frequently Asked Questions
What is a bounded context in DDD?
A bounded context is a boundary within which a specific domain model applies. Inside the boundary, every term has one precise meaning. The same real-world concept, like Order, can have a completely different model in each context.
What is the difference between a bounded context and a subdomain?
A subdomain is a problem space concept, a natural division of the business. A bounded context is a solution space concept, a boundary you draw around a model. Ideally they align one to one, but in practice they can diverge.
How do you identify bounded contexts?
Listen to the language. When the same word means different things to different teams, or two teams need different data and rules for the same concept, you have found a boundary. Organizational boundaries are another strong signal.
Can two bounded contexts share a database?
They should not share tables. Each context owns its data, typically through separate schemas or databases, and other contexts get a local copy synchronized through integration events.
How many bounded contexts should a system have?
As few as you can justify. Each context adds overhead: separate data, integration events, and translation. Start with fewer, larger contexts and split when the language or the team structure forces it.



