The most surprising truth about bounded contexts is that they aren’t about your services, they’re about other people’s services.

Imagine you’re building an e-commerce platform. You’ve got a lot of moving parts: customers browsing, products being stocked, orders being placed, payments processed, and shipments going out. If you just start building services for each of these, you’ll quickly end up with a tangled mess.

Let’s see this in action. Here’s a simplified view of how a Product Catalog might interact with an Order Management service.

Product Catalog Service (Simplified)

// GET /products/{productId}
{
  "productId": "PROD12345",
  "name": "Wireless Mouse",
  "description": "Ergonomic wireless mouse with long battery life.",
  "price": {
    "amount": 29.99,
    "currency": "USD"
  },
  "stockQuantity": 150
}

Order Management Service (Simplified)

// In OrderService.java
public Order createOrder(CustomerId customerId, List<OrderItem> items) {
    // ...
    for (OrderItem item : items) {
        Product product = productCatalogClient.getProduct(item.getProductId());
        if (product.getStockQuantity() < item.getQuantity()) {
            throw new InsufficientStockException(item.getProductId(), product.getStockQuantity(), item.getQuantity());
        }
        // ... other order logic
    }
    // ...
    return new Order(...);
}

This looks straightforward, right? But the problem arises when you try to define the boundaries. Should Order Management know about stockQuantity? What if the Inventory Management service is the real source of truth for stock, and Product Catalog just displays it?

This is where bounded contexts come in. A bounded context is a conceptual boundary within which a particular model is defined and applies. Inside the Product Catalog context, "product" might mean its name, description, and price. Inside an Inventory Management context, "product" might mean its SKU, warehouse location, and current stock count. And in an Order Management context, "product" might be its ID, the quantity ordered, and the price at the time of order.

The key is that each bounded context has its own, consistent model. These models are different but related. You don’t want the Order Management service to directly query the Product Catalog for stock levels, because the Product Catalog might not even have that information or might present it in a way that’s not useful for fulfillment. Instead, Order Management should talk to Inventory Management.

Here’s how you might define these contexts and their relationships:

  • Product Catalog Context: Owns the definitive product information for browsing and display. Its model includes productId, name, description, currentPrice.
  • Inventory Management Context: Owns the definitive stock levels and warehouse locations. Its model includes productId, sku, warehouseId, quantityOnHand.
  • Order Management Context: Owns the state of customer orders. Its model includes orderId, customerId, List<OrderItem>, orderStatus, totalAmount. When an order is placed, it might need to reserve stock from Inventory Management.

The magic happens at the context boundaries. You use "integration patterns" to communicate between them. For example, Order Management might publish an OrderPlaced event. Inventory Management subscribes to this event and decrements the stock. Product Catalog might subscribe to ProductUpdated events from a Product Master context to keep its display information fresh.

The exact levers you control are the models within each context and the communication mechanisms between them. You decide what data lives where, how consistency is maintained (e.g., through events, APIs), and what each service is responsible for.

The one thing most people don’t realize is that the same entity, like a "Product," can exist in multiple bounded contexts with entirely different attributes and behaviors. The productId is the common identifier, but the name and description belong to the Product Catalog, while quantityOnHand belongs to Inventory Management. You’re not trying to create one "true" model for everything; you’re creating multiple, specialized models that serve specific business capabilities and integrating them carefully.

Understanding and correctly defining these boundaries is crucial for building scalable, maintainable microservices. The next step is often figuring out how to handle distributed transactions or eventual consistency across these newly defined contexts.

Want structured learning?

Take the full Microservices course →