# Bounded Context in DDD Explained With Examples

> Bounded Contexts are the most important strategic pattern in Domain-Driven Design. They define explicit boundaries around domain models, giving the same word different meanings in different contexts. Here is a practical guide with .NET examples.

Published: 2026-07-04. Last updated: 2026-07-14. Author: Milan Jovanović.

Canonical: https://milanjovanovic.tech/blog/bounded-context-ddd-explained

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 `Order` class?
- **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:

![An e-commerce domain split into five bounded contexts: Sales for catalog and pricing, Ordering for cart and placement, Fulfillment for inventory and shipping, Billing for invoicing and payments, and Customer for registration and profiles](https://milanjovanovic.tech/blogs/articles/bounded-context-ddd-explained/ecommerce-bounded-contexts.png)

## Bounded Contexts in Code

In a [**Modular Monolith**](https://milanjovanovic.tech/blog/modular-monolith-architecture-dotnet), 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

```csharp
// 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

```csharp
// 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.

![The Ordering Context publishing an OrderPlaced integration event that passes through an Anti-Corruption Layer, which translates it into the Fulfillment Context](https://milanjovanovic.tech/blogs/articles/bounded-context-ddd-explained/context-integration.png)

### Integration Events

The most common approach: publish events that other contexts consume.

```csharp
// 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**](https://milanjovanovic.tech/blog/outbox-pattern-dual-write-problem).

### Anti-Corruption Layer

When consuming data from another context, use an **Anti-Corruption Layer** to translate external models into your context's language:

```csharp
// 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**](https://milanjovanovic.tech/blog/modular-monolith-data-isolation) 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**](https://milanjovanovic.tech/blog/modular-monolith-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.
