Contents

Data mesh was introduced by Zhamak Dehghani in 2019 [1] as an answer to the complexity of data projects at scale. The term is young, but not the ideas behind it: software engineers working in operational systems already know bounded contexts, domains, subdomains and explicit interfaces. As Dehghani argues, these concepts are essential to make teams responsible (and accountable) for what they build and, as a consequence, to help companies grow without losing the ability to change quickly [2, ch. 2].

This series unpacks those ideas with enough detail for practitioners to start writing code and kick off a data mesh implementation from the bottom up. You don't need leadership sponsorship to start, but you will need it to scale, and that conversation can be a lot easier if you bring working code along with your slides.

Domains, Subdomains, Bounded Contexts and Context Map

The core idea behind both data mesh and Domain-Driven Design is to split the business into parts that can each be understood and managed on their own. A domain is the large problem we need to solve, often tied to the area of the business we work in. We break it into smaller problems, the subdomains, that are easier to reason about. The solution we build for a subdomain ideally lives in a bounded context: the boundary inside which our language means one thing, from the words we use in meetings to how we name variables and write code comments. A bounded context also includes everything that makes the solution work: the code, the data, the processes and the tools, owned by a team [3, ch. 3]. And how bounded contexts relate to each other is the context map.

A domain split into subdomains, each solved by a bounded context with its own language. Lines between the bounded contexts form the context map.

A domain split into subdomains, each solved by a bounded context with its own language; the context map is the set of relationships between bounded contexts.

Let's emphasize again: a bounded context is owned by a team made of people.

Each boundary decides who talks to whom, and each line between two contexts is a relationship that people have to keep alive. Two old ideas explain why this matters, and why data mesh insists on giving each boundary one team that owns it.

Binding Social and Technical Aspects

Conway's Law, Dunbar's Number and Data Contracts

Most of the books I read about managing technical teams mention Conway's Law and Dunbar's Number.

Conway's Law says a system's design mirrors the communication structure of the organization that builds it. If you work in tech, I'm sure you have experienced some sort of integration problem in a 3-tier flow. Think of a web application where the Frontend didn't talk to the API developed by Backend Engineers, and the Backend Engineers had to beg the Database Administrators to change an index in the database. In data, we have a similar problem with pipelines split into three layers, each owned by one team: an ingestion team serving data to a transformation team, and a BI team building reports on top of that. If the BI team needs a new column, we have three new items in three different backlogs, and we need to coordinate the three of them. That is Conway's Law in action.

Data mesh attacks this problem from the other side, with the inverse Conway maneuver: instead of letting the system mirror the organization we happen to have, we shape the organization to increase the chances of building the system we want [4, ch. 2]. We place everything and everyone we need to solve a business problem within the bounded context. Teams are empowered with the tools they need to build a solution, and are also accountable for what they build, instead of placing tickets in other teams' backlogs [1].

A bounded context around the team, however, does not make its dependencies on other teams disappear, and every dependency is a relationship someone has to keep alive. Dunbar's Number tells us how few of those we can afford: we keep about 5 close relationships, 15 people we trust, 50 we know well, and around 150 whose capabilities we can remember. We can turn those limits into design rules: keep teams small, and keep the number of teams each one depends on small too, because every extra dependency eats into the team's cognitive load [4, chs. 3, 7].

See what happens when those dependencies outgrow the people who have to keep track of them. Picture a team that already owns its pipeline end to end, but still reads a table owned by another team. One morning, a column came back null.

A two-panel meme: the data pipelines pass a note labeled "Data" to the BI tool, which opens it and finds only "null" values.
A two-panel meme: the data pipelines pass a note labeled "Data" to the BI tool, which opens it and finds only "null" values.

After carefully debugging the code, the team found that the problem was in the source table. The producer team that owns that table, however, had no idea who read it or why other teams needed that column, and they changed its data type anyway. So the producer team made a new rule: every change gets an email to everyone in the company, so anyone can check whether it breaks their pipeline. Now everyone has to keep track of everyone else's work, far more relationships than anyone can hold, and every change triggers a flood of emails and a round of alignment meetings.

This broadcast communication across different teams is what data contracts replace. A data contract is an interface, in the programming sense: an explicit agreement between the teams that produce data and the teams that consume it, on what the data looks like and what it means [5, ch. 5]. Contracts live in version control systems, in CI/CD and in the data pipelines. When a contract changes, the pull request can ask consumers to review the change. When a change breaks the contract, the pull request fails and notifies the data owners and consumers [5, ch. 6]. And if code changes in a way that doesn't honor the contract, the pipeline fails or quarantines the bad data instead of delivering it downstream.

Context Mapping: How Consumers and Producers Interact

Every contract spells out the relationship between two bounded contexts, and the context map tells us who carries the burden of keeping the contract working. Here, we are going to introduce the patterns Customer-Supplier, Open Host Service with Published Language, Conformist and Anti-Corruption Layer.

Four context map relationships between an upstream producer and a downstream consumer: Customer-Supplier, Open Host Service, Conformist and Anti-Corruption Layer.

Four relationships, from the consumer with the most say to the consumer with none. Customer-Supplier: the consumer helps shape the contract, which is code in the producer's Git and CI/CD. Open Host Service: the contract is the Published Language the producer offers to many consumers. Conformist: there is no contract, and the consumer adopts the producer's model as is. Anti-Corruption Layer: the consumer keeps its own contract and validates and translates the producer's data.

In "data words" these are all variations of Consumer-Producer relationships. One bounded context produces data upstream, the other consumes it downstream, and who calls the shots defines the pattern [3, ch. 4]. Let's talk about those patterns, from the consumer with the most power to the consumer with none.

When consumers have a say, the contract starts with them. The consumer identifies a constraint and requests a contract, the producer confirms it is viable, and both agree before it enters CI/CD [5, ch. 4]. The producer carries the burden of honoring the contract, and the consumer carries the burden of saying what it actually needs. This is a Customer-Supplier relationship [6, ch. 3], and even though DDD literature says that suppliers can succeed on their own, a data asset that nobody uses has no value.

As consumers multiply, the cognitive load on the producer grows, because each new consumer is one more relationship to keep track of. To fix this, the producer takes the lead with an Open Host Service: one stable, well-documented interface for all of them, written in a Published Language that anyone can read and that doesn't change every time the producer's internal model does [3, ch. 4]. A data contract is how we write that language down for data: the schema and the meaning, who owns it, versioned and checked in CI/CD. The producer honors the contract, and consumers just read it. Breaking changes become a new contract version, with the old one kept alive until consumers migrate. Ten consumers or a hundred, the producer still has one interface to look after, not a hundred relationships.

And sometimes the consumer has no say at all. Picture a data provider outside the company, such as a vendor selling market data or a partner sending a daily file, or an internal team racing to ship a new feature by the end of the quarter, with no time to implement a contract. The consumer cannot ask for a contract, cannot review the provider's pull requests, and will not be warned when a column is renamed or a format changes. Now, the consumer can be Conformist and accept whatever the provider sends, which is fine if the model is stable, or carry the whole burden and build an Anti-Corruption Layer (ACL) that serves as a translation step between the provider's model and its own, so the provider's decisions and vocabulary stay outside the consumer's bounded context [6, ch. 3]. With an ACL, the data contract becomes one-sided and lives on the consumer's side: the schema, types and rules the consumer expects, versioned in Git and checked in CI/CD and again when the data arrives. Data that matches is translated into the names and types of the consumer's own model. Data that doesn't can be quarantined, so a change nobody announced never reaches the people who use that model. It costs the consumer effort, but the provider's changes stop leaking into its pipelines.

The Data Mesh 30,000-Foot View With DDD and What Comes Next

Now, let's place those concepts in a data mesh architecture. Keep the image below in mind.

Data mesh architecture: domains serving data products to one another, a self-serve data platform underneath, and global governance across all of them.

Domains own their data and serve it as products to one another through an Open Host Service with a Published Language. A domain that consumes an external source protects itself with an Anti-Corruption Layer. Every domain builds on a self-serve data platform, under global governance and open standards. Adapted from [1].

In this post, I focused on the part in the middle: domain teams (or stream-aligned teams) and how they relate to each other. Stream-aligned teams are those that solve a problem directly related to the business [4, ch. 5]. In data, they are mostly composed of data practitioners writing transformation code to meet a business need. Simplifying, they care about transforming data to feed a report that will be used to make a decision. They care about understanding the company's customers' behavior and recommending them a product available in the company's portfolio. With all that work, domain teams don't have time to think about policies or how the infrastructure behaves when the code they deliver is running, and the harsh truth is: they shouldn't have to.

It sounds more like a house of cards than a data architecture, right? How can domain teams be accountable for things they deliver but don't master?

To fix this, we need other types of teams and other interaction modes between them. We need domain-agnostic teams responsible for building tools that help domain teams deliver value faster in a stable way. How?

That is something I'm going to write about in the next posts of this series :)

If you don't want to wait for that, here is a route through the sources below.

Further Reading

Topic Source Chapters
Data mesh in one sitting Dehghani [1] full article
Domain ownership Dehghani [2] 1–2
Domains, subdomains, bounded contexts Khononov [3] 1–3
Context map patterns Khononov [3], Vernon [6] 4, 3
Conway's Law, cognitive load Skelton & Pais [4] 2–3
Data contracts Sanderson et al. [5] 4–7

References

  1. Zhamak Dehghani, How to Move Beyond a Monolithic Data Lake to a Distributed Data Mesh, martinfowler.com, 20 May 2019.
  2. Zhamak Dehghani, Data Mesh: Delivering Data-Driven Value at Scale, 2022.
  3. Vlad Khononov, Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy, O'Reilly Media, 2021.
  4. Matthew Skelton and Manuel Pais, Team Topologies: Organizing Business and Technology Teams for Fast Flow, IT Revolution Press, 2019.
  5. Chad Sanderson, Mark Freeman and B. E. Schmidt, Data Contracts: Developing Production-Grade Pipelines at Scale, O'Reilly Media, 2025.
  6. Vaughn Vernon, Implementing Domain-Driven Design, Addison-Wesley Professional, 2013.