Create your own
Lesson illustration

Defining Service Boundaries with DDD

Hello! Welcome back to your course on preparing for microservices interviews.

In our last lesson, we established the fundamental differences between monolithic and microservices architectures. We concluded that the key challenge isn't just deciding whether to use microservices, but rather how to decompose a complex system into a set of coherent, independent services. Simply breaking a monolith apart without a clear strategy often leads to a "distributed monolith"—a system with all the drawbacks of distributed computing and none of the benefits.

This is where today's topic comes in. We will explore Domain-Driven Design (DDD), a powerful strategic approach for modeling complex business domains. Specifically, you will learn to apply the core DDD concept of a Bounded Context to identify logical, robust service boundaries from a sample monolith. Mastering this skill is crucial for senior-level system design interviews, as it demonstrates you can think architecturally and align technology with business needs.

1. The Core Idea of Domain-Driven Design (DDD)

Before diving into service boundaries, let's understand the philosophy of DDD. At its heart, DDD is about placing the business domain—its logic, processes, and rules—at the center of your software. The structure and language of your code should mirror the business domain it represents.

To achieve this, DDD emphasizes a close collaboration between developers and domain experts (the business stakeholders). This collaboration produces a Ubiquitous Language—a common, rigorous language shared by everyone involved in the project. When a domain expert says "Customer," it should mean the exact same thing to a developer, with the same attributes and behaviors reflected in the Customer class in the code.

This may sound simple, but as systems grow, ambiguity creeps in. Does "Customer" mean the same thing to the sales team as it does to the support team? This is the problem DDD sets out to solve.

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

To understand why DDD is so critical for decomposing monoliths, let's watch this introductory segment from the talk 'Deconstructing Monoliths with Domain Driven Design'. The speakers explain how DDD provides a systematic approach to breaking down complex systems.

Watch the clip from 02:09 to 03:10. Notice how they position DDD as the 'theoretical underpinnings of microservices'.

2. Bounded Context: The Blueprint for Microservices

The most important strategic pattern in DDD for our purpose is the Bounded Context.

A Bounded Context is a clear boundary (like a fence) within which a specific domain model is consistent and self-contained. Inside this boundary, every term in the Ubiquitous Language has a precise, unambiguous meaning. Outside that boundary, the same term might mean something completely different.

Consider an e-commerce platform:

  • In the Sales Context, a Product has attributes like price, description, and discountEligibility.
  • In the Shipping Context, a Product is defined by weight, dimensions, and isHazardous.
  • In the Support Context, a Product might be linked to manuals, knownIssues, and warrantyPeriod.

It's the same "product" in the real world, but it has different models and behaviors depending on the part of the business you're in. Trying to create a single, "god" Product class that serves all three contexts would lead to a bloated, complex, and fragile design.

Bounded Contexts in Domain-Driven Design
This image visualizes two Bounded Contexts: Sales and Support. Notice how concepts like 'Customer' and 'Product' exist in both but are modeled independently, reflecting the specific needs of each business domain. This separation is the key to defining clean service boundaries.

The link to microservices is direct: A Bounded Context is the ideal candidate for a microservice boundary. A service should fully own its context, including its data and business logic.

Let's explore this with a concrete example.

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

The following clip from the same talk provides a great example using a cargo shipping application. It perfectly illustrates how different departments (logistics vs. accounting) have different 'views' of the business, leading to distinct Bounded Contexts.

Watch the segment from 04:31 to 10:03, where the speaker explains the core ideas of Ubiquitous Language and introduces the shipping example. Pay close attention to the 'aha!' moment around 08:30 when the accounting team introduces a new concept ('Bill of Lading') that was irrelevant to the shipping team. Then, continue from 13:33 to 15:49 to see how this leads to the formal definition of Bounded Contexts and how they map to teams, touching upon Conway's Law.

This idea is also strongly advocated by industry leaders like Martin Fowler.

How to break a Monolith into Microservices

This article from Martin Fowler's blog, 'How to break a Monolith into Microservices', reinforces the DDD approach. Pay special attention to the section that explicitly links Bounded Contexts to service boundaries.

Please read the section titled 'Go Macro First, then Micro'. It provides pragmatic advice on using Bounded Contexts to define service boundaries, cautioning against making services too small initially.

3. A Practical Technique: Event Storming

Theory is great, but how do you actually find these Bounded Contexts hidden inside a legacy monolith? One of the most effective techniques is a collaborative workshop called Event Storming.

The process looks like this:

  1. Gather the Right People: Get developers, architects, and domain experts from different parts of the business into a single room with a large wall and lots of sticky notes.
  2. Brainstorm Domain Events: Everyone writes down significant "events" that occur in the business process. An event is something that has happened in the past, expressed in the past tense (e.g., Order Placed, Payment Failed, Shipment Delivered).
  3. Create a Timeline: Arrange the events on the wall in chronological order, telling the story of the business process from start to finish.
  4. Identify Commands and Aggregates: For each event, identify the user command that triggered it (e.g., Submit Order command leads to Order Placed event) and the core business entity, or Aggregate, that was acted upon (e.g., the Order aggregate).
  5. Look for Boundaries: As the timeline grows, you will see natural clusters and pivot points in the flow. The language may change from one part of the wall to another. These shifts and clusters are strong indicators of Bounded Context boundaries.

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

Event Storming is a powerful, hands-on way to uncover a system's domain model. Let's watch how it's done in practice.

Watch the clip from 26:21 to 30:56. The speaker explains the entire Event Storming process, from putting events on a wall to identifying Bounded Contexts (the blue lines they draw on the board). This is exactly how you would approach a monolith decomposition in a real-world project.

4. From Context to Code: Aggregates and Transactional Boundaries

Once we've identified a Bounded Context, we need to think about the code inside the corresponding microservice. DDD provides tactical patterns for this, with the most important being the Aggregate.

  • Aggregate: A cluster of related objects (Entities and Value Objects) that are treated as a single unit for data changes.
  • Aggregate Root: A specific entity within the aggregate that acts as the single entry point. External objects can only hold a reference to the root.
  • The Golden Rule: Any transaction must only create or modify a single aggregate instance.

This rule is fundamental for microservice design. It ensures that each service maintains its own consistency. The Aggregate Root is responsible for enforcing all business rules (invariants) before any state change is committed. If a business process needs to update multiple aggregates, it signals that you need a higher-level coordination pattern (like Sagas, which we'll cover in Module 4), not a single transaction spanning multiple services.

The Azure Architecture Center provides an excellent summary of this process.

Identify microservice boundaries - Azure Architecture Center

This article from Microsoft provides a clear, step-by-step guide for moving from a domain model to microservices, emphasizing the role of Bounded Contexts and Aggregates.

Read the sections 'From domain model to microservices' and the validation criteria that follow it. This text crystallizes the process: Bounded Context -> Aggregate -> Microservice. The validation criteria are an excellent checklist for interview scenarios.

Test your understanding!

You are tasked with decomposing a monolithic application for a university. The application manages student registration, course scheduling, and grade reporting. During an Event Storming session with the registrar's office and faculty members, you identify the following sequence of events:

  1. Course Catalog Published
  2. Professor Assigned to Course
  3. Student Enrolled in Course
  4. Tuition Bill Generated
  5. Tuition Bill Paid
  6. Final Grade Submitted
  7. Transcript Updated

Based on these events, propose at least three potential Bounded Contexts and briefly justify why they are separate.

Show answer

Based on the event flow, here are three logical Bounded Contexts:

  1. Course Management Context: This context is concerned with the academic offerings of the university. It would include events like Course Catalog Published and Professor Assigned to Course. The core aggregates here would be Course and Professor. The language revolves around academic scheduling, prerequisites, and faculty assignments.

  2. Student Enrollment Context: This context focuses on the student's relationship with courses. It would contain the Student Enrolled in Course event. The key aggregate is likely Student or Enrollment. Its language is about registration periods, waitlists, and credit limits. While it references courses, its primary concern is the student's academic record, not the definition of the course itself.

  3. Billing & Financials Context: This context handles all monetary aspects. It would include Tuition Bill Generated and Tuition Bill Paid. The language here is financial (ledgers, payments, fees, deadlines) and is distinct from the academic language of the other contexts. The Student in this context is viewed as an AccountHolder with a balance.

Justification: Each context has a distinct business purpose and uses a different model for shared concepts. The process of defining a course (Course Management) is separate from a student enrolling in it (Enrollment), which is in turn separate from paying for it (Billing). Trying to manage all these concerns in one service would create a highly complex and coupled system.

Key Takeaways

  • DDD aligns software with the business. It's a strategic tool for managing complexity.
  • A Bounded Context defines a boundary where a domain model is consistent. It is the primary candidate for a microservice.
  • The Ubiquitous Language is a shared vocabulary that eliminates ambiguity within a Bounded Context.
  • Event Storming is a practical, collaborative workshop to discover events, aggregates, and Bounded Contexts from a business process.
  • An Aggregate defines the transactional boundary within a microservice, ensuring data consistency. A transaction should only ever modify a single aggregate.

Next Up

You now have a powerful framework for strategically identifying service boundaries. But are these boundaries correct? How can we refine and validate them? In our next lesson, we will apply the Single Responsibility Principle (SRP) to our proposed boundaries to ensure our services are truly cohesive, independent, and maintainable.

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

Sign up