Create your own
Lesson illustration

Decomposing Systems with Bounded Contexts

Hello! Welcome to the final module of our course: System Design & Production Readiness. In the previous modules, we've focused on building, connecting, securing, and operating individual microservices. We've covered everything from synchronous communication and event-driven patterns to deployment on Kubernetes and implementing feature flags.

Now, we're going to zoom out. This module is all about the big picture—how to design the entire system from the ground up. This is a crucial skill set for the mid-senior level roles you're targeting, as system design questions are a core component of those interviews.

Today's learning outcome is to decompose a complex system (e.g., an e-commerce platform) into microservices by applying the bounded context pattern. This is the single most important principle for defining sane, maintainable service boundaries. Getting this right is the difference between a successful microservices architecture and a dreaded "distributed monolith."

1. The Goal: Avoiding the Distributed Monolith

You already know the benefits of microservices over monoliths—independent deployment, technology diversity, and team autonomy. However, if you decompose a system incorrectly, you can end up with the worst of both worlds: the complexity of a distributed system combined with the tight coupling of a monolith. This is known as a distributed monolith.

In a distributed monolith:

  • A change in one service often requires changes in many other services.
  • Deploying a single feature requires a coordinated release of multiple services, negating the benefit of independent deployability.
  • Teams constantly step on each other's toes because functional responsibilities are smeared across service boundaries.

To understand why this happens and why a principled approach to decomposition is so critical, let's hear from microservices expert Sam Newman.

Monolith Decomposition Patterns - Sam Newman

This video from NDC Conferences provides an excellent overview of the challenges in monolith decomposition. We'll focus on the concept of the distributed monolith and why Domain-Driven Design is the key to avoiding it.

Please watch from 02:18 to 18:49. Pay close attention to: The definition and symptoms of a 'distributed monolith' (starts around 07:45). His argument for why Domain-Driven Design (DDD) is the most important tool for defining service boundaries (starts around 14:56).

As Sam Newman emphasizes, if you get the logical domain model wrong, no amount of fancy technology like Kubernetes or service meshes will save you. This is why we turn to Domain-Driven Design (DDD).

2. The Principled Approach: Domain-Driven Design (DDD)

DDD is an approach to software development that centers on modeling the software to match a business domain. Instead of organizing your code by technical layers (controllers, services, repositories), you organize it around business concepts.

For microservices, we primarily focus on Strategic DDD, which helps us define the large-scale structure of our system—the service boundaries. The central pattern in Strategic DDD is the Bounded Context.

To get a high-level introduction to how DDD provides the theoretical foundation for microservices, let's watch a segment from a talk by the team at VMware Tanzu.

Deconstructing Monoliths with Domain Driven Design - Rohit Kelapure, David Turanski, Rohit Sood

This video introduces DDD as a systematic way to break down monoliths. It clearly explains what a Bounded Context is using a great real-world example.

Watch the following two clips: From 02:09 to 03:10: This sets the stage, positioning DDD as the 'theoretical underpinnings' of microservices. From 03:34 to 15:09: This is the core section. It explains fundamental DDD concepts, but your focus should be on the Bounded Context (starts around 10:25). Notice how the meaning of 'Cargo' changes completely between the 'shipping' context and the 'accounting' context. This is the essence of a bounded context.

Key Concepts from the Video:

  • Ubiquitous Language: A common, rigorous language shared between developers and domain experts within a specific context. In the 'accounting' context, they talk about "Bill of Lading," not "shipping containers."
  • Bounded Context: The explicit boundary within which a domain model and its ubiquitous language apply. Inside the boundary, the model is consistent and unambiguous. Outside, it's a different story.
Domain-driven Design: Example of Bounded Context
This diagram is a perfect illustration of Bounded Contexts. The concept of a 'Customer' exists in both 'Sales' and 'Support', but it means different things and relates to different entities in each context. In Sales, a customer has a 'Pipeline' and 'Opportunity'. In Support, a customer has 'Tickets' and 'Defects'. A microservices architecture would likely have a `SalesService` and a `SupportService`, each with its own model of a 'Customer'.

A bounded context is the ideal candidate for a microservice boundary because it is, by definition, a boundary of high cohesion and loose coupling at the business level.

3. A Practical Playbook for Decomposition

Theory is great, but how do you actually identify these bounded contexts in a real-world project? You can't just guess. You need a systematic process involving collaboration with the business.

3.1 Collaborative Discovery: Event Storming

One of the most effective techniques is Event Storming. It's a collaborative workshop where you get developers, architects, and domain experts together in a room with a large wall and lots of sticky notes.

The process, at a high level, is:

  1. Identify Domain Events: Everyone writes down significant events that happen in the business domain on orange stickies (e.g., "Order Placed," "Payment Processed," "Item Shipped").
  2. Enforce a Timeline: Arrange the events chronologically to tell a story.
  3. Identify Commands: For each event, identify the command that caused it (e.g., the PlaceOrder command causes the OrderPlaced event).
  4. Identify Aggregates: Group the commands and events around the entity they operate on (e.g., the Order aggregate).
  5. Find the Bounded Contexts: Look for natural clusters and seams in the event flow. These clusters of related aggregates and events often reveal the bounded contexts.

Let's see how this looks in practice.

Deconstructing Monoliths with Domain Driven Design - Rohit Kelapure, David Turanski, Rohit Sood

The same VMware Tanzu talk has an excellent segment explaining how they use Event Storming to identify bounded contexts in a real project.

Watch from 26:21 to 30:56. Observe how the colored sticky notes are used to map out the business process and how the blue lines are drawn to delineate the bounded contexts that emerge from the workshop.

3.2 Domain Analysis from Requirements

If a full-blown Event Storming workshop isn't possible, you can apply a similar thought process by carefully analyzing the system's requirements or user stories. This is a very developer-friendly technique.

The process involves:

  1. List User Stories/Functional Requirements: Write down what the system needs to do.
  2. Extract the Nouns: Identify all the key nouns. These are candidates for your entities and aggregates (e.g., Customer, Order, Product, ShoppingCart).
  3. Extract the Verbs: Identify the actions or verbs. These are candidates for your service operations (e.g., checkout order, list products, apply coupon).
  4. Group Nouns and Verbs: Group related nouns and verbs together. This grouping process helps reveal the subdomains and, ultimately, the bounded contexts.

This approach is less collaborative but still forces you to think in terms of the business domain.

4. Case Study: Decomposing an E-Commerce Platform

Let's put this all together and apply it to the classic e-commerce example mentioned in the learning outcome. We'll use the domain analysis approach.

Decomposition of Microservices Architecture

This article, 'Decomposition of Microservices Architecture' by Mehmet Ozkaya, provides a fantastic, practical walkthrough of decomposing an e-commerce system using the patterns we've just discussed.

Please read the following sections: Introduction and Decomposition Patterns: Read from the start down to 'Bounded Context Pattern' to understand the high-level patterns. Bounded Context Pattern: Read this section to reinforce your understanding of the core concept. Analysis E-Commerce Domain: Read the 'Use Cases', 'User Stories', 'Nouns and Verbs', and 'Identifying and Decomposing' sections. This is the practical application. Follow how the author goes from requirements to a list of potential microservices.

By following this process, you arrive at a set of potential microservices that are aligned with the business capabilities of an e-commerce platform.

E-Commerce Application Decomposition into Microservices
This diagram shows a potential outcome of the decomposition process. The monolithic 'E-Commerce Application' is broken down into services like `Order Management`, `Product Management`, and `Payment`, each corresponding to a distinct business capability and bounded context.

A crucial point to remember is that one bounded context does not always equal one microservice. A bounded context is a logical boundary from Strategic DDD. A microservice is a physical unit of deployment. While a bounded context is the best starting point for a service, you might occasionally split a single context into multiple services for technical reasons (e.g., one part is extremely high-traffic and needs to scale independently). However, you should always start with the assumption that a bounded context maps to a single service.

5. Conclusion and Interview Preparation

You've learned the most critical skill in microservice system design: how to find the seams in a complex domain and use them to define service boundaries.

Key Takeaways:

  • Avoid the Distributed Monolith: The primary goal of decomposition is to achieve loosely coupled services. Incorrect boundaries lead to a tightly coupled distributed system, which is the worst of both worlds.
  • Decompose by Business Domain: Use Domain-Driven Design (DDD) to align your service boundaries with business capabilities, not technical layers.
  • Bounded Context is Key: The Bounded Context is the core pattern from Strategic DDD for defining service boundaries. Within a context, the domain model is consistent and uses a shared Ubiquitous Language.
  • Use Practical Techniques: Use collaborative workshops like Event Storming or analytical methods like analyzing nouns and verbs from requirements to identify bounded contexts.
  • Bounded Context as a Starting Point: A bounded context is your default candidate for a microservice, ensuring high cohesion and loose coupling.
Senior Engineer Interview Question

"You are leading a project to build a ride-sharing platform like Uber or Lyft from scratch. The leadership team wants to use a microservices architecture. How would you begin decomposing this system? Walk me through your thought process and identify three to four initial microservice candidates, explaining your rationale for choosing them."

Show detailed answer

"My primary approach would be to use Strategic Domain-Driven Design to identify the core business capabilities and define service boundaries around them. I would start by facilitating an Event Storming session with product managers, business stakeholders, and engineers to map out the entire lifecycle of a ride.

My thought process would be to identify the distinct subdomains within the ride-sharing business:

  1. Trip Management / Rider Experience: This context is concerned with the rider's journey: requesting a ride, seeing driver ETAs, being matched with a driver, the in-ride experience, and rating the trip. The language here includes 'pickup location', 'destination', 'fare estimate', and 'ride request'.

  2. Driver Management: This context handles everything related to the driver: onboarding, document verification (license, insurance), vehicle information, availability (online/offline status), and earnings. The language involves 'driver profile', 'payouts', and 'acceptance rate'.

  3. Payment & Billing: This context is responsible for calculating fares, processing payments from riders, and handling payouts to drivers. Key terms are 'fare calculation', 'payment method', 'transaction', and 'invoice'.

  4. Location & Matching: This is a very technical but distinct context. It's responsible for tracking the real-time location of all active drivers, finding the nearest available drivers for a ride request, and calculating routes and ETAs.

Based on this analysis, my initial three microservice candidates would be:

  • Trip Service (Trip Management Bounded Context): This service would manage the state machine for a single trip, from REQUESTED to ACCEPTED, IN_PROGRESS, and COMPLETED. It would be the central coordinator for a ride. I chose this because the lifecycle of a trip is a core, cohesive business process.

  • Driver Service (Driver Management Bounded Context): This service would be the system of record for all driver-related information and state (e.g., is_available). Separating it is crucial because driver management has its own complex logic and lifecycle (e.g., background checks, vehicle inspections) that is independent of any single trip.

  • Payment Service (Payment & Billing Bounded Context): This service would handle all financial transactions. I'd separate this because payment processing is a highly specialized and critical capability. It has different security, compliance (PCI), and reliability requirements than, say, the trip management service. It also needs to integrate with external payment gateways.

By defining these boundaries along business capabilities, we create services that are highly cohesive, loosely coupled, and can be developed and scaled independently by dedicated teams."

Excellent work today. You've tackled the foundational concept of system design for microservices. In our next lesson, we'll build directly on this. Now that we have identified our services, we'll need to figure out exactly how they communicate. Our next topic is: Design API contracts and choose appropriate communication patterns for key inter-service interactions.

Can't find a good explanation? Sign up and we'll make it for you

Sign up