Context Mapping in Domain-Driven Design

Making the Relationships Between Bounded Contexts Explicit

Software Design

Software rarely lives in one place. Different teams own different parts of the system, each with their own model of the domain. Those models need to work together. Data flows between them, and a concept owned by one team is referenced by several others. How those relationships are managed determines whether the system stays coherent as it grows.

Context mapping is a practice from domain-driven design for making those relationships explicit. Instead of letting integration patterns emerge by accident, a context map names the relationships between parts of the system, describes the direction of influence, and makes the assumptions between teams visible before they become problems.

The previous article introduced bounded contexts as explicit boundaries where a model is consistent and its language is unambiguous, using an online learning platform as the running example. But bounded contexts do not exist in isolation. The learning context needs to know when a student has paid. The certification context needs to know when a course has been completed. Defining the boundaries is only part of the story. Context mapping is the other part.

The Problem With Ignoring Relationships

When the relationships between bounded contexts are left implicit, they tend to become accidental. One team starts calling the other team’s API directly. A shared database table grows columns that serve two different contexts. A concept leaks from one model into another, and suddenly two contexts are coupled in ways nobody planned.

The result is the same problem that motivated bounded contexts in the first place: a model that is trying to serve multiple masters, rules that conflict, and changes in one part of the system breaking another.

Making the relationships explicit forces conversations that are otherwise left until something goes wrong. Which context owns a piece of data? Who is responsible for translation when the models diverge? What happens when the upstream changes its model?

Upstream and Downstream

Every relationship between bounded contexts has a direction. The upstream context influences the downstream context. Changes in the upstream model ripple downstream. The downstream context depends on the upstream, not the other way around.

On the learning platform, the enrollment context is upstream of the billing context. When a student enrolls, billing needs that information to generate an invoice. The enrollment context does not care about invoices. It will not change its model to suit billing. Billing depends on enrollment, so billing is downstream.

Enrollment Context as upstream and Billing Context as downstream
Enrollment Context as upstream and Billing Context as downstream

Understanding the direction matters because it shapes the options available to the downstream context. How much the upstream sets the terms, and how much the downstream can influence it, is not fixed. It depends on the relationship between the teams, and that is what the patterns describe.

The Relationship Patterns

The direction of a relationship tells you who depends on whom. The pattern tells you how that dependency is managed. The options range from tight coordination to full separation, and the right choice depends on how much leverage you have over the upstream and how much its model would affect your own.

Shared Kernel

Two contexts share a small, explicitly agreed-upon piece of the model. Both teams own the shared piece together and must coordinate on any changes to it.

On the learning platform, the learning context and the certification context might share a common definition of what it means for a course to be completed: the criteria, the data structure, and the rules for evaluating them. Neither context wants to duplicate this definition, and if the two implementations diverged, a student could satisfy one without satisfying the other.

The Learning Context and Certification Context sharing a completion definition
The Learning Context and Certification Context sharing a completion definition

The shared kernel is a strong commitment. It requires ongoing coordination and a high level of trust between teams. The risk is that the shared piece grows over time, and the coordination cost grows with it. A field gets added to the completion definition by one team without coordinating with the other. The change seems harmless in isolation. Six months later, a student who has completed the course according to the learning context has not satisfied the certification context, and nobody immediately connects the breakage to a shared module that changed quietly.

That coordination cost is what makes the shared kernel a last resort rather than a default. It is worth it when sharing the actual implementation is safer than maintaining two separate definitions that must stay in sync. If the two definitions diverging in ways that silently break the integration is a bigger risk than the overhead of coordinating every change, a shared kernel is justified.

Customer/Supplier

The upstream context acts as a supplier. The downstream context is the customer. The customer can raise requirements and influence the supplier’s roadmap, but the supplier ultimately decides what it provides and when.

The enrollment context supplying data to the billing context is a customer/supplier relationship. Billing depends on what enrollment exposes. Billing can request changes, but enrollment is not obligated to accommodate every request immediately. That means the billing team waiting on the enrollment team to make the changes it needs.

The Enrollment Context as supplier providing data to the Billing Context as customer
The Enrollment Context as supplier providing data to the Billing Context as customer

This is one of the most common relationships in practice. When the upstream ships what the downstream needs in time, the relationship works. When it does not, the downstream team starts calling internal APIs directly or duplicating data it cannot get through the official interface, and the boundary quietly dissolves.

Conformist

The downstream context simply adopts the upstream model without translation. The downstream team accepts whatever the upstream provides and maps their own thinking to it.

This happens when the upstream has no incentive to accommodate the downstream and building a translation layer is not worth the cost. On the learning platform, the learning context integrates with a third-party video delivery provider. The provider has its own data model: video identifiers, playback states, completion percentages. The learning context has no influence over how that model is structured.

Conforming to it is a pragmatic choice. The decision of whether to conform or introduce a translation layer comes down to how much the upstream model would corrupt your own if adopted directly.

Conforming is fine when the upstream’s concepts map cleanly onto your domain. It becomes a problem when the provider’s vocabulary starts appearing in your own model, and your code reflects the upstream’s structure rather than the domain it is supposed to represent.

Anticorruption Layer

The downstream context introduces a translation layer that converts the upstream model into its own vocabulary. The downstream model is protected from the upstream’s concepts.

An anticorruption layer is appropriate when the upstream model is poorly structured, uses different language, or would corrupt the integrity of the downstream model if adopted directly. On the learning platform, if the content management context is a legacy system with its own data model and terminology, the learning context might wrap it in an anticorruption layer to translate between the legacy concepts and its own clean model.

Learning Context communicating with a Legacy CMS through an Anticorruption Layer
Learning Context communicating with a Legacy CMS through an Anticorruption Layer

The anticorruption layer adds complexity and needs to be maintained as the upstream changes. That cost is real, but so is the alternative.

Skip it, and the legacy concepts start appearing in the learning context directly. A ContentNode class that mirrors the CMS’s data structure. Field names copied from a system nobody fully understands anymore. A new developer looking at the learning context cannot tell which concepts belong to the domain and which leaked in. The upstream’s problems become your own.

Open Host Service

The upstream context defines a stable, well-documented interface that multiple downstream contexts can consume. What makes it stable is the published language: an explicit data model designed to be understood without knowledge of the upstream’s internal model. The interface is general enough to serve many consumers without requiring customisation for each one.

On the learning platform, the course catalog context exposes an API that both the learning context and the billing context consume. The catalog does not expose its internal Course entity. It publishes a CourseSummary, a deliberately designed contract that downstream contexts can rely on without knowing how the catalog models a course internally.

The Course Catalog Context exposing a published language through an open host service
The Course Catalog Context exposing a published language through an open host service

Because downstream contexts depend on CourseSummary rather than the internal model, the catalog can restructure its internals without breaking its consumers. Downstream contexts gain the same freedom in return: they can evolve against a stable contract without coordinating with the catalog team.

Choosing the Right Pattern

The patterns describe relationships that exist whether or not you name them. Naming them forces a deliberate decision rather than letting one accumulate by default.

Two questions drive most of the choice. The first is how much leverage you have over the upstream. If you can negotiate what the upstream exposes and when, Customer/Supplier is the natural fit. If you cannot, you are already in Conformist or anticorruption territory. The second is how much the upstream model would corrupt your own if adopted directly. A clean, stable interface is worth conforming to. A poorly structured or alien model justifies the cost of a translation layer.

The same logic applies in reverse when you are the upstream serving multiple consumers. Rather than each downstream context coupling to your internals, an open host service with a published language gives them a stable contract to work against independently of how you structure things internally.

The context map makes these choices visible across the whole system. When a team sees that they are conformist with five different upstream contexts, that is a signal worth examining. When a shared kernel has been growing for months without coordination, that is a risk worth surfacing.

Drawing the Context Map

A context map is an explicit representation of the relationships between bounded contexts. It shows which contexts exist, how they relate, and what kind of relationship each one is. It is not a system architecture diagram and it is not a data flow diagram. It captures something more fundamental: the social and technical reality of how teams and their models interact.

A context map does not need to be a formal artifact. The most useful version is a whiteboard sketch: bounded contexts as labeled boxes, relationships as arrows, upstream and downstream direction made clear, and the pattern name written on each arrow.

A rough context map for the learning platform
A rough context map for the learning platform

What matters is that it reflects reality rather than aspiration. A context map that shows how things should work is less useful than one that shows how they actually work, including the messy relationships and the legacy dependencies that nobody is proud of.

The context map is also a conversation tool. Drawing it with the teams involved surfaces assumptions, clarifies ownership, and makes implicit dependencies explicit before they cause problems. A context map that lives on a whiteboard and gets updated as the system evolves is more valuable than a polished diagram that reflects an ideal nobody actually works toward.

What Comes Next

A context map names the relationships between bounded contexts and makes explicit what might otherwise accumulate by accident. It tells you the direction of each dependency, how much leverage each side has, and which patterns govern the integration. What it does not capture is how those relationships work when the system is running.

When a student enrolls, how does the billing context find out? When a course is completed, how does the certification context react? The next article covers how to answer those questions and why the right approach is not the same for both.

This article is part of the Domain-Driven Design series

  1. 1. What Is Domain-Driven Design?
  2. 2. Subdomains and Bounded Contexts in Domain-Driven Design
  3. 3. Context Mapping in Domain-Driven Design
domain-driven-design