Article
Data mesh in motion: 6 scenarios to understand the roles and the tooling
Who does what, with what, and how the data flows.
The data mesh is a complex topic to grapple with. In this article, I’ll try to show you what it looks like in practice. I’m bringing together here six practical scenarios, one per situation you might encounter with a data mesh architecture. They cover what I deemed essential, from the birth of a data product to its eventual retirement. The article is aimed at people who have heard the term “data mesh” and want to understand concretely how a data product moves between roles, tools, and decisions: data architects, product or business owners, data engineers, executives wondering whether mesh is relevant for them.
Before we begin, here’s a quick reminder of the four principles[1] of the data mesh:
- domain-oriented data ownership: each business domain owns and serves its own data;
- data treated as a product: with an owner, a contract, and consumers;
- a self-service platform, that equips domains without owning anything itself;
- federated governance: global rules, decided together, then enforced automatically and locally.
Now let’s see these principles in motion. The concepts and tools enter the scene progressively, but you can find the full synthesis in the annexes.
Opening · The film
A data product, from end to end
The data mesh makes the most sense “in motion”. Before presenting who does what and with what, let’s watch the film: a data product, from its birth to its retirement, and at each step, which role passes the baton to which other.
A routine scene, to start: the marketing team wants to target the active customers for its follow-up campaigns. But which data to use? Who guarantees its freshness? Is the email masked? Who do you ask for access? That’s precisely what the data mesh seeks to make simple, traceable, and safe; this scene will serve as our running thread throughout the article, under the name clients-actifs-fr.
Seven actors interact: the product owner (who owns the product), the data engineer (who builds it), the self-service platform (which equips), the governance and the consumer. On the governance side, we meet the reviewer (who validates the contract, independent of the author) and the DPO, the data protection officer, guardian of GDPR compliance, who arbitrates access to personal data.
We’ll follow six scenarios together: the birth of a data product, its construction, its discovery, its consumption, its evolution and its retirement. The diagram below gives the big picture; each scenario unfolds with a click.
▸ Click a scenario to expand or collapse it; the first one is open to start.
S1Birthsteps 1–4expand
- Declare the product (name, domain, sources, classification)▶
- Draft the data contract (ODCS)▶
- Request review▶
- Technical + governance validation (reviewer ≠ author)gate◀
S2Constructionsteps 5–11expand
- Request provisioning▶
- Development environment deployed◀
- Build (ingest → transform → store)▶
- Add quality tests▶
- Request merge to prod▶
- CI + governance GATE, sign (audit log)gate◀
- Publish (output port + catalog)▶
S3Discovery & accesssteps 12–15expand
- Discover at catalog◀
- Request access (purpose declared)◀
- PBAC: access granted for a purposegate▶
- Cost allocated (chargeback / showback)▶
S4Consumesteps 16–18expand
- Consume (SQL / API / stream / viz)◀
- PII / DPO: mask lifted per purposegate▶
- Use: BI / ML / AI agent↺ loop
S5Incident & operationssteps 19–21expand
- SLO / quality alert↺ loop
- Trace the cause◀
- Price per step↺ loop
S6Evolve / end of lifesteps 22–25expand
- New version (versioning)▶
- Impact: who consumes v1?◀
- Re-sign (revalidate the contract)gate◀
- Deprecation banner → Retired▶
The film · Six practical scenarios
The six moments of the lifecycle
Six zooms on the film, six concrete situations. In each scene, we also name the role entering the scene and the tooling block they operate, and we detail the flow in a dedicated sequence diagram.
1 · Birth: from use case to data product
What business need justifies this product?A data product doesn’t start with code, nor even with data: it starts with a use case: a business need, an expected impact.
We create a data product because we want to obtain something: a better-informed decision, a missing dashboard, a model to feed. This expectation is the real starting point. The first step is therefore not technical: it’s about understanding the desired impact, then filtering between what we need and what we already have.
And it’s more subtle than it seems, because information isn’t always visible in the raw data. Data that “says nothing” at first glance can, once transformed, cross-referenced and enriched, answer a business case we didn’t even imagine was addressable. Precisely because of this, defining a data product benefits from being a collective and creative exercise, bringing together varied profiles (business, data, platform): no single person has both deep knowledge of the need and of the data that’s actually available.
To structure the workshop, a simple and readable tool for business stakeholders: the Data Product Canvas (popularized by INNOQ consulting firm)[2]. It’s a sheet in eight boxes that we fill in together, and which forces us to answer the right questions before writing a single line of code. Each box has its guiding question: you can read them directly on the diagram below, next to the filled canvas for our running thread: clients-actifs-fr.
1Consumer & use case
Who uses the product, and for what?
The campaigns marketing team wants to target the active customers for its follow-ups. Expected impact: better campaign conversion rates.2Domain
Which business domain does this product belong to?
Marketing: the domain that owns the data and commits to its quality.3Name
A clear, stable name by which you find it in the catalog.
Active customers FR, stable identifier:clients-actifs-fr.4Sources
Where does the data come from, including outside the mesh (legacy, SaaS…)?
CRM (daily export); e-commerce orders (CDC, near real-time).5Ubiquitous language
The shared business vocabulary, so that “customer” means the same thing to everyone.
“Active customer” = at least one order over 12 rolling months, a shared business/data definition.6Contract & output ports
What the product guarantees: schema, SLO (Service Level Objectives: measurable goals the producer commits to, such as freshness or completeness), sensitivity; and how it exposes itself (table, API, stream…).
Freshness ≤ 24 h, completeness ≥ 99 %; contains PII (email) → DPO gate, masking by purpose; exposed as SQL table, REST API and Kafka stream.7Classification
What type of product is this: source-aligned (close to a source), aggregate (crosses multiple sources) or consumer-aligned (shaped for a specific use)?
Aggregate: it combines CRM and orders to produce a reusable business concept (“active customer”).8Architecture
The technical sketch: sources, transformations, materialization.
CDC ingestion → Bronze / Silver / Gold layers → publication + policies.
Once the canvas is filled, the data contract crystallizes: it puts in black and white what the product guarantees. This is what we declare, before writing the code.
For further reading — other ways to frame a data product (Event Storming, DDD)
The canvas isn’t the only starting point. When the scope of a domain is still fuzzy, we can start with an Event Storming (Alberto Brandolini’s method)[3]: a workshop where we lay out on a large temporal timeline all the business events, everything that happens in the domain, formulated in the past tense (“order placed”, “payment received”…). We start from events rather than processes or data, because an event is an objective fact, which a business expert can state without needing to agree on a data structure. The timeline reads left to right (chronology), and we identify where the vocabulary shifts — where the same word changes meaning or two words mean the same thing: we’ve found a seam, a bounded context (a perimeter where each word has a single meaning) [4], a natural candidate to become a data product.
Each unit reads top to bottom: the actor, the read model (the info to decide), the command (the intent, imperative) and the event it provokes (past tense). The purple policy encodes an automatic reaction (“when Payment received, then Reserve stock”). At the seam crossing, the word “order” gives way to “package”: the language shifts, we hold two bounded contexts. The hotspot on “Payment received” (“ anti-fraud? ”) is a business decision: in the Data Product Flow sense, every decision opens a candidate output port.
The question then is: what does this candidate contain? Two criteria, drawn from practice rather than pure DDD theory[5], allow us to decide. First: among the attributes the timeline reveals, keep only those that measure the domain’s business value, not everything the source system knows internally[3]. Second, formalized by Agile Lab under the name Data Product Flow[6]: for each business decision identified on the timeline, ask which data would make it easier to take or automate that decision; each answer becomes a candidate output port, and their grouping, under lifecycle and ownership coherence, draws the data product.
This second method carries its own limitation within it. Its authors explicitly restrict it to data products source-aligned[6]: a nuance to keep in mind. Event Storming was originally aimed at operational modeling, and extending it to analytics is an accepted adaptation, not an automatic equivalence. The DDD seam — however you find it — therefore yields candidates, not a final perimeter. There’s no 1:1 mapping between sub-domain, bounded context, and data product; this ambiguity is resolved through two trade-offs.
Granularity, first: a rich bounded context often yields multiple products, at a finer grain than the full context; conversely, several small generic contexts (in the DDD sense: core / supporting / generic) can merge into a single convenience product.
Downstream demand, second: the Event Storming split is source-aligned by construction; it follows the operational model. But Dehghani distinguishes three data product alignments[7]: source-aligned (closest to the producer system), aggregate (reassembled from multiple products) and consumer-aligned (tailored for a specific use). A good share of the value plays out in the latter two; Dehghani herself warns against overly ambitious aggregates that recreate centralization. If we freeze boundaries without listening to consumers, we recreate operational silos on the data side; that’s precisely the role of the canvas’s “Consumer & use case” box, which we fill first.
Despite their differences, all these methods converge on the same idea: the contract-first. Define the contract before the code, to “fix quality at the source” rather than through reactive testing (that’s the shift-leftapproach).
Detailed flow and roles
This declaration isn’t yet a product: it goes through a first validation gate, with two faces. On the technical side, the platform checks that the declaration is coherent and the contract well-formed (do the sources exist? does the schema hold up?). On the governance side, a reviewer, independent from the author, checks the classification, personal data and purpose. Honest provenance: the rule “reviewer ≠ author” is an engineering control (separation of duties, like a PR review) that I propose, not a mesh doctrine: Dehghani posits federated governance, not this specific separator. Here’s this first flow in detail:
- Frame the use case (8 blocks: consumer, sources, classification…)
- Product boundary + classification (aggregate)
- Declare the data contract (schema, SLO, quality, sensitivity, purpose)
- Reference the draft in the catalog (discovery port + guarantees)
- Request the review
- Independent review: reviewer ≠ authorgate↺ loop
- Gate 1: technical (coherence) + governance (PII, purpose) validatedgate
- Draft ready to build↺ loop
One useful nuance: we say here that we reference the draft of the data product in the Catalog & discovery block, but that’s not necessarily where the data contract is validated: it really depends on the tooling. What matters isn’t the exact location: it’s having a declaration space where the data product request is made, can be validated, then used to provision the technical environment that will make it possible to materialize the product (scene 2).
In the data contract
The contract is formalized in a file. Several standards exist; the most widespread is ODCS (Open Data Contract Standard), driven by Bitol, a Linux Foundation initiative, and born from an internal PayPal model[8]. It’s written in YAML, versionable with code, therefore readable, diffable and enforceable in CI. At birth, we lay down the identity skeleton of the product:
# Open Data Contract Standard (ODCS / Bitol)
# annotated excerpt for pedagogical purposes; do not validate as-is
apiVersion: v3.1.0
kind: DataContract
id: clients-actifs-fr # stable, unique identifier
name: Active customers FR
version: 0.1.0 # SemVer; 0.x = still in draft
status: draft # proposed → draft → active → deprecated → retired
domain: marketing # the owning domain
description:
purpose: Active customers, for campaign targeting
usage: Analytical reading; no rewriting
schema:
- name: clients_actifs
properties:
- name: client_id
logicalType: string
required: true
unique: true # → testable in quality
- name: email
logicalType: string
classification: PII # marks sensitivity (see scene 4)
- name: derniere_commande
logicalType: dateWhy these fields matter. The id is the product’s stable anchor (it never changes, even when the name evolves). The status drives the lifecycle (proposed / draft / active / deprecated / retired); the version in SemVer later encodes breaking vs non-breaking (scene 6). The logicalType describes the business type independent of the engine (the physicalType concrete (VARCHAR, STRING…) clarifies depending on the server of storage). Special case: for a streaming product, there’s an additional technical contract such as a Schema Registry (Avro/Protobuf)[9], more on that in scene 5.
How the contract is organized. ODCS distinguishes on one side the native sections that the standard defines and a linter can check: identity (id, name, version, status, domain), schema, SLO (slaProperties), output ports (servers), roles & approvals (roles); and on the other side the extension point customProperties, where each organization branches its own house rules (masking by purpose, contract compatibility, retention, legal basis, GxP context…) without breaking compliance with the standard. This is what makes the contract both valid (it passes an ODCS linter) and specific (it carries your org’s policy). The following scenes add their blocks to the contract; each time a field isn’t native, it will live under customProperties.
For further reading
The data product and its five ports (the shared grammar)
An image that carries through the whole rest: a data product is an architectural quantum[1][10], the smallest unit deployable autonomously, combining code, data and metadata, infrastructure, and rules. It relies on platform blocks and exposes itself through output ports. We recognize five ports[11]: an input port at entry, then four exposition ports: output (the served data), discovery (its identity card and guarantees), observability (its signals, including quality) and control (access and enforcement).
Input port
the entry
Data product = architectural quantum
at the core, the transformations (the code), then four exposition ports
This grammar now has its standard. Bitol (the Linux Foundation initiative driving the ODCS contract standard, introduced earlier under “In the data contract”) publishes ODPS (Open Data Product Standard), whose v1.0.0 came out in October 2025[12]. Where ODCS describes a contract, ODPS describes the product itself: its identity, lifecycle, and ports: each output port references the ODCS contract that guarantees it, and the ports discovery, observability and control declare themselves via managementPorts. The five-port product image is therefore no longer a metaphor: it’s a declarable and versionable artifact, the emblematic artifact of the data product experience plane (Dehghani’s three planes, detailed in the annexes).
# ODPS (Open Data Product Standard / Bitol): the product, not the contract
# annotated excerpt for pedagogical purposes; do not validate as-is
apiVersion: v1.0.0
kind: DataProduct
id: 3f2504e0-4f89-11d3-9a0c-0305e82c3301 # stable product UUID
name: Active customers FR
status: draft # proposed → draft → active → deprecated → retired
domain: marketing
inputPorts:
- name: crm-contacts
contractId: crm-contacts-fr # ODCS contract of an upstream source
version: 1.2.0
outputPorts:
- name: clients-actifs-sql
type: tables
contractId: clients-actifs-fr # ← the ODCS contract followed in this article
version: 0.1.0 # version of the referenced contractHomonym to avoid: the Open Data Product Specification (opendataproducts.org)[13] shares the ODPS acronym, but it’s a different lineage: here we talk about the Bitol standard.
Finally, the six DAMA-UK quality dimensions give the grid for “measuring the quality” of a dataset and feed the SLOs; we’ll see them in action in scene 5, where they power the product’s quality dashboard.
Where these roles come from
Domain ownership, that is, the ownership of data by domain, is the first principle of the data mesh, formulated by Zhamak Dehghani, the engineer (then at Thoughtworks) who invented the concept in 2019[7]. The idea: the domain that produces the data owns and serves it, instead of dumping it into a central data lake managed by a third-party team that doesn’t know its meaning.
The role breakdown relies on Team Topologies, a book by Matthew Skelton & Manuel Pais on organizing software teams[14]. Its thesis: four broad team types are enough: stream-aligned (aligned on a value stream), platform (which equips the others), enabling (which builds up the others’ skills) and complicated-subsystem (a specialized craft). We find them almost exactly mapped in the mesh: domain team, platform team, support, specialties (ML, real-time). Dehghani cites Team Topologies, but this detailed mapping is largely the community’s work[15].
The real lever of Team Topologies, here, is the cognitive load(canon Skelton & Pais, picked up by Dehghani)[14]: decentralization transfers cognitive load to the domain: the owner must now think about quality, contract, support, not just their business. Without lightening it (self-serve platform + enabling team), ownership becomes a naked burden: that’s the mechanism of the first breaking point detailed in the synthesis (“Ownership without incentive”).
The federated governance (4th principle) is also “computational”[1]: rules are decided globally, executed locally, and enforced automatically by the platform (policy-as-code). “Federated” isn’t “decentralized”: we keep common standards and a few central dependencies.
Finally, the mesh doesn’t remove data governance. The reference corpus for the field, the DAMA-DMBOK (the “body of knowledge” published by the DAMA association, which structures data management into domains: quality, security, metadata, reference data…)[16], remains valid. The mesh doesn’t change its what; it changes its operating model: responsibility shifts from a single central team to a federatedstewardship, embedded within the domains.
2 · Technical construction
How do you turn the contract’s promise into an executable product?The declaration triggers the provisioning of a suitable environment. We build in it, and we don’t touch production until after two gates; each approval is traced: it’s a signature.
The core idea: the domain builds, but the platform standardizes the construction path. The data engineer (or a data engineer agent, an emerging role as tooling automates) works in an environment provided by the platform, not on infrastructure they’d have to piece together themselves. The platform tailors the environment to what was declared: real-time, operational, sensitive classification, chosen technology.
A word on essential vocabulary, because it describes a central lifecycle step: environment promotion. We never develop directly in production. The work traverses successive environments: dev (the data engineer’s sandbox), preprod / staging (where we test on realistic data), then production (what users consume). “Promoting” means passing the product from one tier to the next, and each pass is guarded by a gate.
The technical gate is automatic: it replays the tests and verifies that the contract is respected; a breaking change causes a fail. The governance gate is human: a reviewer approves, and their approval is traced in an audit log: it’s a signature that commits.
Detailed flow and roles
- Request the environment (per the declaration)
- Env provisioned: dev → preprod, templates, access
- Build: ingestion, transformation (medallion), data tests↺ loop
- Open the PR to promote to prod
- Replay tests + enforce the contract (fail if breaking)gate↺ loop
- Green build → request the review
- Approve & sign: reviewer ≠ author (audit log)gate
- Merge to main → deploy to production
Development materializes the data contract in code: it’s this code that produces all the data product artifacts. Pre-production serves to test them before promotion: it keeps development flexibility while letting external actors verify that the product meets their needs. If needed, we iterate (return to scene 1) until convergence.
Production promotion, meanwhile, should be driven by the platform, at minimum via enablers (CI/CD code, Terraform modules…). You also need to declare the job to the orchestration tool, which will execute the product according to the contract, declaratively (by parsing the contract) or more explicitly. Details vary by organization; the key is standardizing and automating as much as possible to avoid human errors.
For further reading — medallion layer development
Bring in the data (ingestion / iPaaS). Three families, not a single tool: the iPaaS (low-code application integration: MuleSoft, Boomi…), the ELT (load raw then transform: Fivetran, Airbyte, dlt) and the CDC (capture near-real-time changes: Debezium). At the mesh boundary, an Anti-Corruption Layer translates the source model into the domain’s model. The concrete implementation of this off-mesh integration is often a pain point, because it must adapt to operational sources and security constraints. Generally, we choose to duplicate raw data into the mesh, to keep control over auditing and be able to replay the data in case of issues.
The internal architecture can be a medallion DAG: Bronze (raw), Silver (cleaned, conformed), Gold (business aggregates). But beware: maturity isn’t purely technical. Data becomes a source-aligned data product when it gains the triad owner + contract + governed output port; that’s the Gold → product transition.
For further reading — dev → prod promotion
Promoting dev → preprod → prod. Concretely, usually, promoting = merging to main (git is the mechanism, there’s no “promote” button). In this case the gate is a PR review by another role.
A contract without CI is decorative.
On the tooling side: the technical gate applies the contract at build time (datacontract-cli, Schema Registry): the build fails on a breaking change; the governance gate is a merge approved by a reviewer different from the author (the engineering control seen in scene 1) and traced in the audit log.
In the data contract
It’s here, at construction time, that the contract file (ODCS) lives in the repo, next to the transformation code. At merge, the CI/CD formats it for the publication in the platform (catalog, discovery port) and uses it to provision automatically the other blocks, for example declaring the job in the orchestration tool, creating the tables/SLO, applying the policies for the column marked PII. The contract thus becomes the executable source of truth, not a doc that drifts.
We therefore complete the scene 1 skeleton with the executable contract: the documented transformations, the quality, the SLOs, and the output servers (the output ports):
# … (continuation of the clients-actifs-fr contract)
# annotated excerpt for pedagogical purposes; do not validate as-is
schema: # enrich the schema from scene 1
- name: clients_actifs
properties:
- name: nb_commandes_30j # derived column (30-day aggregate)
logicalType: integer
transformDescription: Number of orders in the last 30 rolling days
transformLogic: >- # native ODCS: documents how the column
count(*) FROM commandes # is derived from its sources (lineage
WHERE client_id = c.id # at column level)
AND date >= CURRENT_DATE - INTERVAL '30' DAY
- name: segment_churn # derived column (business rule)
logicalType: string
transformDescription: Churn risk segment
transformLogic: "CASE WHEN nb_commandes_30j = 0 THEN 'at_risk' WHEN nb_commandes_30j <= 2 THEN 'fragile' ELSE 'loyal' END"
quality: # native ODCS: quality at the schema object level
- name: client_id_unique
type: library # text | library | sql | custom (native ODCS)
dimension: uniqueness # native ODCS (accuracy | completeness |
# conformity | consistency | coverage |
# timeliness | uniqueness)
metric: duplicateValues # native ODCS (nullValues | missingValues |
# invalidValues | duplicateValues | rowCount)
rule: must_be_zero # threshold: implemented by Soda / dbt tests
- name: email_present
type: sql # native escape hatch: free query
dimension: completeness
query: SELECT count(*) FROM clients_actifs WHERE email IS NULL
# must equal 0 (the engine interprets it)
slaProperties:
- property: freshness # freshness SLO
value: 24
unit: h
element: clients_actifs.derniere_commande
- property: completeness # expected completeness
value: 99
unit: percent
servers: # where the product is materialized
- server: prod
type: snowflake
database: MARKETING
schema: PUBLICOne product, one (or several) serving location(s). The block servers says where this data product is materialized. But nothing requires all data products to live in the same place: one domain publishes to Snowflake, another to an S3 bucket, another to a Kafka broker, depending on what best serves its consumers. That’s precisely why we catalog the available data: the catalog is the directory that makes every product addressable, wherever it’s served. The contract declares what the product is and where it’s served; the catalog aggregates these declarations so you can search, compare and request access (we’ll return to this in scene 3).
The CI gate compares two things. On one side, the contract written in code; on the other, what’s declared / published in the Catalog & discovery block. If the repo YAML diverges from the catalog version (a field added without bumping version, a classification removed), the build fails: that’s what prevents the contract from becoming “decorative”.
Documenting transformations. Derived columns carry their transformDescription (what they compute, in plain language) and their transformLogic (the SQL or formula): that’s native ODCS, at column level. The interest isn’t cosmetic: it’s what powers lineage at the column level and tells the consumer how a metric is obtained, not just what it contains. “nb_commandes_30j” isn’t a raw column, it’s an aggregate, and the contract says so. We’ll find these two fields again in scene 4, as a reference to avoid confusing them with masking.
The quality rules illustrate ODCS’s two native regimes: type: library for predefined rules, where you pick a standard metric (duplicateValues, nullValues, missingValues, rowCount…) that maps directly onto Soda or dbt tests; and type: sql, the escape hatch for the special cases no standard metric covers (here, a COUNT on missing emails). Each rule carries its dimension quality (uniqueness, completeness…), and quality attaches to the schema object, not the top-level contract: that’s its place in ODCS 3.1.0. The element of an SLO, meanwhile, ties the guarantee to one specific column (here, freshness is measured on the last order date).
3 · Discovery, access, and who pays for what
How do you find the product, and get the right to use it?The product is published; now you need to find it and access it. The catalog makes products addressable; the output ports serve them; access is requested (a gate); and someone pays: that’s where the incentive plays out.
We discover a product at the catalog (its discovery port: schema, semantics, owner, contract guarantees). We consume it via polyglot output ports: the same product can expose itself as an SQL table, an API, a Kafka stream, a file. Access is requested by declaring a purpose: that’s the PBAC (purpose-based access control): you grant access to a use, not to a person. This aligns with GDPR (purpose limitation, minimization).
Dark & shadow data: the data you can’t see
“Discovering” has a flip side: you can neither own, nor govern what you can’t see. Two concepts often conflated, worth keeping distinct:
Dark data
Data collected and stored but never used, known, within the governed patrimony. The problem is the non-value. Origin: Gartner, BI world[17].
Shadow data
Copies outside visibility and outside governance: snapshots, backups, SaaS/AI exports. Unknown, ungoverned: the problem is the invisibility (security, compliance). Origin: security (DSPM)[18].
Same treatment mechanism, opposite valence. Mesh × dark = opportunity: data-as-a-product, catalog and lineage activate dark data; a well-maintained mesh reduces it. Mesh × shadow = risk: in theory federated governance eliminates it; in practice, decentralization and the proliferation of products can manufacture it if maturity doesn’t keep up. The hinge is the anti-pattern of the “ghost product”: a product abandoned, both dark (unused) and drifting outside governance.
The GDPR angle is the one that resonates in France: personal data in shadow isn’t inventoried, so the right to erasure (Article 17)[19] cannot reach it, and the records of processing (Article 30)[19] become unreliable. Catalog and lineage are the antidote.
Detailed flow and roles
Here’s the end-to-end flow:
- Search (with purpose in mind)
- Product sheet: schema, semantics, guarantees (contract), owner
- Request access (purpose declared)
- Evaluate the purpose (PBAC)
- Approvers vote (multi-level)gate↺ loop
- Verdictgate
- if approvedAccess granted: PII mask lifted per purpose (audited)
- if refusedMotivated refusal (audit log)
- Consume via the output port (SQL / API / stream)
- Cost allocated (chargeback / showback)
In the data contract
On the contract side, discovery and access play out in two sections: the servers (seen in scene 2) which describe where and how the product exposes itself, one per output port, and the section roles, which encodes who can request what, for what purpose:
# … (continuation of the clients-actifs-fr contract)
# annotated excerpt for pedagogical purposes; do not validate as-is
servers: # one per output port
- server: prod-sql
type: snowflake # table / SQL port
database: MARKETING
schema: PUBLIC
- server: prod-api
type: api # API port (ODCS enum)
location: https://api.intra/dp/clients-actifs/v1
- server: prod-stream
type: kafka # stream port
topic: marketing.clients_actifs.v1
roles:
- role: analyste-marketing
access: read
firstLevelApprovers: data-steward # native ODCS: 1st level
customProperties:
- property: purpose # purpose (PBAC), non-native:
value: Campaign targeting # carried in role customProperties
- role: data-scientist
access: read
firstLevelApprovers: data-steward
secondLevelApprovers: dpo # native ODCS: PII → 2nd level
customProperties:
- property: purpose
value: Model training
- role: tableau-bord-ventes_dp_svc # service account of a downstream product
access: read # downstream, not a person.
firstLevelApprovers: data-steward # _dp_svc: house naming convention.
secondLevelApprovers: dpo # PII read by an app → the DPO
# evaluates its AUDIENCE
customProperties:
- property: purpose
value: Sales management (sales managers)Why this is structuring. Multiplying the servers lets one same data product be polyglot (table, API, stream) without duplicating it. In roles, the purpose is the heart of the PBAC introduced above. A non-native field, it lives under the role’s customProperties, as the YAML above shows. Multi-level approval, on the other hand, is native in ODCS: firstLevelApprovers / secondLevelApprovers[8]. Special case: as soon as a column is classified PII, we fill in the dpo as secondLevelApprovers, which triggers the multi-approver gate from scene 4. Each request on these roles follows the flow seen above: the decision, approval or refusal, is traced in the audit log, whether the arbitration is automatic (PBAC) or comes back to a human approver. This audit log is used to answer retroactively three questions: who had access to what, when, and on what basis (declared purpose, approver). This is what makes it possible to investigate an incident, justify a revocation, or prove compliance before a regulator.
Access isn’t reserved for humans. The third role in the example is a technical identity: a data product downstream, a published dashboard (Power BI), a service or an agent consumes via its own service account, dedicated to the product, not to a person (the suffix _dp_svc is a house naming convention; the standard prescribes nothing). Three practical consequences. First, vigilance: granting access to an application is granting it to all its users: the declared purpose must cover the audience of the app, not the intent of its developer; for regulated data, it’s what the DPO arbitration evaluates. Second, chaining: if the app re-exposes the data, it becomes itself a product, with its own contract and its own gates: the gates chain, they aren’t passed through. Finally, the signature: whoever builds the downstream product subscribes in the name of the product they’re building: the person carries the request and signs at the gate, but the subscribed identity (the one that consumes, appears in lineage and gets revoked) is the product. The field contractId of ODPS[12] (seen in scene 1) equips both sides: under inputPorts, the downstream product declares the contracts it consumes.
Who pays for what: chargeback and usage patterns
The scene title promises “who pays for what”: it’s time to honor that. Without chargeback, the producer pays for everyone, and it’s the public goods problem that kills data-as-a-product by default. The first thing the mesh renders is the visibility: the observability port exposes the request volume and who consumes what, in the form of a finops view per product. You can alert on unexpected consumption patterns, not just on quality or latency.
Anticipating then monitoring usage patterns is essential to avoid cost surprises. The same product serves rarely a single rhythm: one consumer pulls small queries every 5 minutes, another makes big occasional batch pulls. These aren’t the same cost, nor the same server silhouette. The first case calls for a small server running permanently (stable latency, low fixed cost); the second a big scale-to-zero server with a well-configured timeout (you pay for the peak, not for waiting). Recognizing the “streaming” and “batch” patterns lets you redistribute usage toward the optimized server corresponding, rather than serving everything on a single overdimensioned silhouette.
The chargeback itself, on the other hand, isn’t simple. Its implementation varies strongly by organization. And attributing a server’s uptime to a specific consumer, when that’s what we actually pay for when serving a model, isn’t always feasible. So we resort to proxies: the data volume transited, the query count, measured per consumer, then we redistribute the actual costs in proportion to these proxies. If a product-specific chargeback exists, it’s good to document it in the data contract: cost transparency lets consumers anticipate their budget, and the producer avoid staying behind to absorb the bill. The general framework (FinOps Foundation, FOCUS) is detailed on the finops insert of scene 5.
For further reading
Cross-domain interoperability: reference & master data
Decentralization complicates consistency across domains (“multiple versions of the same customer”). The reference data (nomenclatures, codes) and the master data (core entities: customer, product) make products joinable. The established pattern: a dedicated MDM domain publishes this data as data products (owner, contract, API).
But it’s a community consensus, not the canon: Dehghani addresses consistency through global identifiers and links, not a central “golden record.” And identity resolution (match/merge/survivorship) remains, itself, centralizing: the MDM-as-domain pattern is therefore an honest tension with decentralization, a golden record that we productize rather than distribute.
Global addressing. The catalog, in the mesh sense (the mesh experience plane of Dehghani’s three planes, detailed in the annexes), gives each product in each domain an address (an identifier, a semantics, guarantees) discoverable from any other domain. That’s the condition for a product from the sales domain to join a product from the marketing domain without central mediation.
Polysemes and shared identifiers. Dehghani introduces the polyseme (canon): the same concept (“customer”, “product”) designates different realities depending on the domain. The “customer” of marketing (a prospect) isn’t the “customer” of support (an open ticket). Cross-domain joining therefore requires a identity resolution: linking these versions by a shared identifier (a global key) or by links declared in the contract, rather than imposing a single model on all domains.
Table sharing vs query federation. Once identity is resolved, two ways to join without pulling everything in: sharing tables in open formats and interoperable (Apache Iceberg, Delta Lake), where the domain publishes a snapshot or a stream that another domain reads in place; or federating the query, i.e. addressing the products in place, without moving the data (Trino, cross-source views). The first moves data but serves repeated reads at low cost; the second moves nothing but puts the cost on each read.
And the semantic layer, beyond identity. The reference and master data solve the who (which entity we join), not the what (what “revenue” or “active customer” means, with what formula). Hence the semantic layer:“define once, use everywhere”, unifying metrics and the glossary so the same number has the same definition everywhere, for BI and AI agents alike. This is the least mature piece of the mesh: the initiative OSI (Open Semantic Interchange, launched late 2025 by Snowflake, dbt Labs, Salesforce, BlackRock et al.)[21] normalizes a vendor-neutral semantic model, but it’s still in v0.1.x. To present it as a trend, not an established standard, one that overlaps with ODCS semantic fields in a way that remains unclear. It’s also the same anti-hallucination anchoring that scene 4 ascribes to AI: without this layer, an agent misses the difference between “revenue” and “projected_revenue”.
4 · Consume: BI, AI, sensitive data
how to use the product without losing its guarantees?At the end of the flow, the product gets consumed: a dashboard, a model, an AI agent. Trust comes from the contract + lineage + QA; for sensitive data, protection is applied at several levels and must be proven.
Let’s remind ourselves, because this is the whole spirit of the data as a product: a “product” isn’t just a table. A data product is just as much a BI dashboard, a machine learning model, as today an agentic application (an AI agent plugged into the governed data). All are consumed, and all deserve a contract.
The same mesh therefore also serves datasets, a semantic layer, data APIs, and increasingly knowledge products for RAG. For a reliable AI, the contract and semantics are an anti-hallucination anchor, a necessary but not sufficient condition: knowing that “revenue” isn’t “projected_revenue” is exactly the kind of distinction an agent misses without a contract.
Detailed flow
The most sensitive moment is access to personal data. Here’s the PII / DPO gate in detail, from the submission of the request to the mask lift for the sole granted purpose:
- Request PII access (purpose + justification)
- Notification to the required approvers
- Each approver votes (approve / reject), multi-levelgate↺ loop
- Aggregated verdict
- if all approveAssign the purpose to the consumer
- PII mask lifted for this purpose only, audited act
- if one refusalMotivated refusal, recorded in the audit log
- Consume via the output port (PII visible per the purpose)
For further reading — masking, anonymization: at which level, what robustness
The mechanism, without deception. Raw data arrives in Bronze, not masked at storage. Dynamic data masking is an access control applied at read time, on the column, in the engine, a policy-as-code driven by tag/classification (Snowflake masking policy, BigQuery policy tags, Databricks ABAC). By default the policy returns the masked value; the DPO gate grants the consumer a purpose attribute, and the policy reveals the plaintext for that purpose only, audited act.
Highly regulated cases (GxP / pharma). In a GxP framework, compliance is proven by documented evidence:ALCOA+ principles, audit trail (21 CFR Part 11), risk-based validation (GAMP 5). The rhetorical bridge with the mesh is real (audit trail ↔ immutable log, lineage, policy-as-code), but no regulator endorses “data mesh”, and decentralization adds validation burden.
In the data contract
Consuming sensitive data relies on two places in the contract: the classification at column level (seen in scene 1), and the declarative declaration of masking: the effective policy is applied by the engine, but the contract says what to protect and for whom to lift it:
# … (excerpt from the clients-actifs-fr contract)
# annotated excerpt for pedagogical purposes; do not validate as-is
schema:
- name: clients_actifs
properties:
- name: email
logicalType: string
classification: PII # sensitive column (native ODCS); the
# masking policy is declared below, under
# customProperties: transform and unlockFor
# are two extensions, non-native ODCS
customProperties:
- property: retention # retention period (ISO 8601)
value: P3Y
- property: legalBasis # legal basis of processing
value: consent
- property: transform # default masking at read time (extension)
value:
column: email # plaintext rendered only for a purpose
type: mask # mask | hash | tokenize | nullify
default: redacted # what an unauthorized role sees
- property: unlockFor # purposes that lift the mask (extension)
value:
- column: email
purpose: Customer support
approvers: [dpo] # vocabulary of our extension;
# native ODCS is first/secondLevelApproversWhy these fields matter. The classification marks sensitivity; the masking policy (transform + unlockFor) makes it contract-driven: adding a PII column, means inheriting the protection automatically (“define once, new data picks up protection”). unlockFor ties the mask lift to a purpose, not to an identity: it’s the contractual implementation of scene 4’s gate. Honest provenance: classification is native ODCS, but transform and unlockFor aren’t: they live in customProperties, alongside retention or the legal basis. Beware the false friend: the native fields transformDescription / transformLogic describe how a column is derived from its sources (lineage at column level), not a masking at read time: we saw them in scene 2 documenting derived columns, and access control isn’t their purpose. The closest native field is encryptedName, at column level, which points to its encrypted variant (not dynamic masking). For purpose-based masking, we therefore declare our policy under customProperties, alongside retention, the GDPR legal basis or the GxP context. And we keep the legal distinction: a mask or a tokenize reversible remains personal data: it isn’t an anonymization.
5 · Incident & operations
How do you know what breaks, where, and how much it costs?An incident is something you detect (quality), track (lineage) and price (finops). The observability port of the product concentrates logs, traces, SLOs, quality metrics and audit.
The goal is simple: when something breaks, we should know and understand almost immediately where the problem comes from. This requires having monitored every step: from the sources through to exposition, passing through each transformation. The lineage[24] (captured at build) then traces upstream: failing source? faulty transformation? failed execution? And to price, we assign the cost at execution level (the query, the job), not just the warehouse.
But before the alert, there’s the measurement. The quality rules declared in the contract (seen in scene 2) don’t just serve CI: the Quality & observability block replays them in production, at every run, and draws metrics from the grid of the six DAMA-UK dimensions[37]: completeness, uniqueness, timeliness, validity, accuracy, consistency. The alert is just the extreme case: a measurement crossing a threshold.
Aggregated, these measurements form the DQ dashboard, the product’s quality dashboard, fed by the observability port: score per dimension, trend, incident history. It speaks to two audiences. The domain team pilots its quality debt there: which rules fail most often, which dimension degrades, where to invest before the on-call rings. The consumers, meanwhile, read a trust signal there: published in the catalog alongside the contract, the quality score is consulted before subscribing, and verified afterward. Without this dashboard, quality exists only in the negative: you only hear about it when it breaks.
Detailed flow
Here’s the tracking, step by step:
- Alert (deviated SLO, quality, failed run)
- Read the signals (logs, traces, metrics)
- Trace upstream (where is the failure?)
- Faulty link: source / transformation / run / consumer
- Price the incident cost (per step)
- Attribution: chargeback / showback per data product
- Fix the link; observability confirms the return to normal↺ loop
A price that spirals is itself an incident. Finops doesn’t just serve to count after the storm: a monitored cost that goes outside its expected range (a server’s load rising, a consumer’s request volume tripling, an output port’s billing exploding) is treated like any SLO deviation. We declare an SLO budget just like a freshness SLO, the observability port follows it, and the drift opens an incident before the bill, not after. That’s the cost version of the principle seen above (“a measurement crossing a threshold”): a finops signal isn’t a dashboard you glance at once a month, it’s an alarm that sounds in time, and the natural extension of the usage patterns anticipated in scene 3.
In the data contract
Operations anchor in the slaProperties (seen in scene 2): these are what trigger the alerts. The contract says the target and where to route the incident: that’s the link between the guarantee published at the discovery port and the monitoring of the observability port:
# … (excerpt from the clients-actifs-fr contract)
slaProperties:
- property: freshness
value: 24
unit: h
element: clients_actifs.derniere_commande
- property: completeness
value: 99
unit: percent
- property: latency # real-time case (see the synthesis)
value: 500
unit: ms
customProperties: # severity is not native on slaProperties
- property: slaSeverity # (ServiceLevelAgreementProperty is closed):
value: # alert criticality per SLO, in extension
freshness: critical # drives alert criticality
completeness: warning
support: # where the alert goes (native ODCS)
- channel: "#marketing-data-oncall"
tool: slack
scope: incident
- url: https://intra/runbooks/clients-actifs
scope: runbookWhy this is useful. Without numerical values in the contract, the monitoring has no threshold: a “deviation” is only detectable if the norm is declared. The severity (carried in customProperties, non-native on slaProperties) avoids waking an on-call for a minor delay; support guarantees the alert reaches the right role (the owning domain), not a central team. Special case: the latency only makes sense for a product served in near real-time; for a batch product, we stick to freshness and completeness.
For further reading
Alerting well: from threshold to action plan
Declaring thresholds isn’t enough; alerting well rests on four design choices.
1 · Alert where you look. The “In the data contract” section showed it: the support block routes the alert to the owning domain. Still, choose the channel, and it’s a design decision, not a configuration detail: the one the team sees in real time, often a Teams or Slack on-call channel (#marketing-data-oncall), rather than a mailbox read the next day.
2 · Don’t flood. An alert must be actionable; every ignored alert trains the team to ignore the next one (the alert fatigue)[27]. Beyond the severity already seen, two levers: grouping (an upstream incident should produce one alert, not fifty downstream, and lineage is what enables deduplication) and thresholds that translate the SLO, not the slightest variation.
3 · An action plan per alert. The best practice: every possible alert has its action plan documented in advance; the product’s risk analysis anticipated it: which failure modes? what severity? who decides what? The protocol to follow is systematized in the documentation: that’s the runbook[27], already referenced by the contract (the scope: runbook entry in the supportblock). An alert without an action plan is an invitation to improvise at 3 a.m.
4 · Automate investigation, up to agentic. The flow of this scene (alert → signals → lineage → cause) is a repeatable protocol, therefore automatable: first scripted diagnostics, and increasingly an agentic investigation: an agent reads the alert, traces lineage, tests the runbook hypotheses and proposes, or even applies, the remediation for known cases. It’s an ecosystem trajectory in 2026, not a standard; and the responsibility for resolution remains with the domain: the agent investigates, the human answers for what goes to production, except in explicitly delegated and bounded cases.
6 · Evolve, then retire
How do you change or retire the product without breaking its consumers?A data product lives. Changing its schema risks breaking consumers in production, hence contract versioning. And retiring it requires preparation.
A product exposes one version of its contract. A change non-breaking (adding an optional column) is managed painlessly: consumers simply ignore the new field. A breaking change (removing a column, modifying a type) requires instead the coexistence of versions and an accompanied migration: we publish v2, give everyone time to migrate, then retire v1. In streaming, this discipline is enforced at runtime by a Schema Registry: before writing a message, the producer registers its schema, and the Registry blocks publication if this schema risks breaking consumers, according to a compatibility rule (BACKWARD, FORWARD or FULL) [9].
Then the key question remains: “who still consumes v1?”. That’s the role of the lineage, which traces downstream dependencies. Deprecation, finally, is announced (a sunset date), migrated consumer by consumer, then retired.
For further reading — BACKWARD, FORWARD, FULL compatibility: who protects whom?
In streaming, producers and consumers deploy independently and at different paces. The compatibility rule says which direction of time we protect: the old messages read by new consumers, the new messages read by old consumers, or both. It’s also instructive for batch products, so I detail here the three compatibility modes, which are the most common:
- BACKWARD: a new consumer knows how to read the old messages. You can add a field (with a default value) or remove a field; you can’t remove the default value from an existing field. It’s the default, and the most common: we guarantee that the consumers we deploy will understand the history already present in the topic.
- FORWARD: an old consumer knows how to read the new messages. You can remove a field (the old consumer ignores it) or add an optional one; we protect the consumers we haven’t redeployed yet.
- FULL: BACKWARD and FORWARD at the same time. You can only add an optional field with a default. The most restrictive, but the safest: you can deploy producer and consumers in any order without breaking anyone.
Concretely, the Registry compares the schema the producer is about to publish to the previous one, applies the configured rule, and refuses the registration in case of violation: the write then fails at runtime. It’s the streaming equivalent of a CI gate, but per message and continuously rather than at the time of the build.
Detailed flow
Above all, an evolution isn’t just a technical topic: changing the schema forces to revalidate the data contract, all the more so if it carries sensitive data. We need to verify that the classification tags are still valid, and the people responsible must “sign” again to recommit their responsibility. Depending on the scope of the change, the healthiest is often to see the update as replaying the creation cycle of the data product, gates included. The flow below shows it, through to retirement:
- Propose an evolution (breaking or not): contract version vN
- Check compatibility (BACKWARD / FORWARD / FULL in streaming)
- Revalidate the contract (classification tags, purpose)
- Re-signature of the responsible people: recommitmentgate↺ loop
- Evolution approved (audit log)gate
- Notify the subscribers (coexistence v1 + v2, accompanied migration)
- Migrate to vN (or stay on v1 during the coexistence)
- if retirementAnnounce the sunset then retire (Retired state)
- Deprecation banner in the catalog → retirement
In the data contract
Evolution reads in two fields seen from scene 1: version (SemVer) and status, to which we add a compatibility rule that CI enforces:
# Evolution: v1.2.0 → v2.0.0 (BREAKING change)
# annotated excerpt for pedagogical purposes; do not validate as-is
apiVersion: v3.1.0
id: clients-actifs-fr # the id NEVER changes
version: 2.0.0 # major++ = breaking (column removed)
status: active
schema:
- name: clients_actifs
properties:
- name: client_id
logicalType: string
# 'email' removed → this is what makes the change breaking
customProperties:
- property: contractCompatibility # extension, non-native ODCS
value: backward # CI checks vs the published version
# backward | forward | full | none
# see scene 6, compatibility DeepDive
---
# End of life: we deprecate, we don’t remove abruptly
version: 1.2.0
status: deprecated
customProperties:
- property: deprecation # extension, non-native ODCS
value:
sunsetDate: "2026-12-31" # announced retirement date
replacedBy: clients-actifs-fr:2.0.0
migrationGuide: https://intra/migrations/clients-actifs-v2Why this protects consumers. The SemVer (major.minor.patch) encodes the promise: a minor adds without breaking, a major signals a breaking change: contractCompatibility gives CI the rule to reject a breaking change disguised as a patch. Streaming special case: it’s the modes BACKWARD / FORWARD / FULL of the Schema Registry that play this role, at runtime. Finally, the deprecation policy makes end of life piloted (date, replacement, guide) rather than endured: it’s the antidote to the “ghost products”. Honest provenance: contractCompatibility and deprecation aren’t native ODCS: we declare them under customProperties, like any house rule. And any evolution touching a PII column relaunches the re-signature of the affected roles.
Closing · Synthesis
Limitations and who it’s for
The closing movement, and the most practical questions: where does the mesh break, how far does it serve, and where to plug in a real-time ML use case?
The three places where it breaks
This is the anti-FOMO section of this article. The data mesh isn’t a magic potion: it’s a response to real pain points, and it has a cost. Here’s where projects go off the rails most often: expand each point for detail.
Ownership without incentiveorganization / politics
This is typically the real blocker; it isn’t technical. Decentralization is a transfer of work and risk that nobody asked for: a domain is handed the quality, the documentation and the support of “its” data, without necessarily having the means or the desire.
- Public goods problem (free-rider): the domain that produces pays, the others profit. Without incentive, it under-invests, rationally.
- Turf wars: territory wars between domains (“is the customer a marketing or sales concept?”)[28].
- Organizational theater[29]: renaming “the ERP team” into an “ERP domain” without changing anything about the responsibilities or the budget.
The executive sponsorship is, in the literature, the first predictor of success[29].
The self-service platformtechnical / architecture
The hardest pillar. It’s a genuine platform-engineering effort, never a turnkey product.
- No turnkey: catalog, orchestration, contracts, observability, access, lineage: each is a tool, and the real burden is their integration.
- Brownfield: durable coexistence with the central warehouse and the legacy is the norm, not the exception.
- Without a mature platform, each domain pieces together its own infrastructure and recreates silos; we lose the economies of scale.
Governance that seizes upmake-or-break principle
Federated governance is an unstable equilibrium.
- Too loose → chaos, duplication, proliferation of ghost products[30].
- Too tight → disguised re-centralization, and the bottleneck comes back through the window[30].
- Reference data (MDM / reference data) is under-specified by pure mesh: cross-domain coherence doesn’t fall from the sky (the point is explored scene 3 : global addressing, polysemes, table sharing vs federation).
At bottom, the data mesh brings problems closer to those who understand them, at the cost of an additional coordination overhead. My sense: the trade-off tends to pay off at large scale, and more rarely for a small organization. The main risk isn’t, by the way, the choice of a tool: it’s to underestimate the platform effort and the prerequisites, technical and organizational. A threshold often cited in the literature: at least five domain teams already in production[15], modern data engineering (DDD, CI/CD) and a multi-year commitment[29][31]. Below that, it’s often better to start small.
Is it for you? (and for an SME?)
The complete mesh is often oversized; but its principles are cheap and worth stealing. The good maxim for many organizations: “one central team, but one that delivers data products, not just tables”. The decision tree rests on a few questions: do you have several autonomous domains at scale? is the central team a bottleneck? do you have the capacity to change the operating model? If yes, an incremental mesh; if not, a clean centralized base with light products and contracts.
Even at small scale, two principles are almost always worth adopting early: governance and the contract. A data contract isn’t just a technical artifact: it’s an explicit commitment between parties, with guarantees (quality, freshness, purpose) that you can measure and enforce. It’s often what moves an organization from “the data exists somewhere” to “we know what we can count on”.
For further reading — does agentic AI change the game for the SME?
Partly. Agentic AI and radically simplified platforms genuinely erode the technical barrier of the mesh (infra, pipelines, cataloging, automated governance). True consequence: data-as-a-product + contracts + self-service become cheap enough to be a best practice at any scale.
But what justifies the complete mesh is a organizational complexity (several autonomous domains, a central bottleneck), not the technical[32]; tooling doesn’t create this precondition. An SME with one or two domains has nothing to decentralize[33]. Clean formulation: agentic AI democratizes the principles at any scale, not the operating model (the federation), which remains an answer to scale. The SME consensus is refined, not overturned.
Operational vs analytic: where real-time ML plugs in, and AI?
“If I want a real-time ML use case, do I rely on the platform, or is it necessarily off-mesh?” Short answer: no, not necessarily off-mesh; and yes, it would be a shame to deprive yourself of it. But we must distinguish the canon from the extension.
The canon (Dehghani) separates two planes[7]: the operational plane (the transactional systems that run the business) and the analytic plane (the aggregated view for deciding and training models). The data mesh is scoped to the analytic plane. Often-misunderstood key point: the boundary is operational vs analytic, not batch vs real-time. Real-time analytics (serving fresh features at low latency) is in scope.
Analytic plane: data mesh (canon)
Operational plane (OLTP): runs the business
Concretely, for fraud scoring or recommendations: the features come from governed data products (a feature store is an aggregate-type data product), served by streaming ports. The domain event is the bridge between the two planes[34]: the time-stamped business fact feeds both analytics and, in stream, real-time features. What remains truly of the operational plane is the live serving (the app that calls the model at the millisecond): that’s the extension.
It’s also the horizon of the whole edifice: data products governed (contracts and semantics) form the base of a reliable agentic AI. The “agentic mesh” (the term is Eric Broda’s[35]) remains emergent, and no vendor owns its definition. But the direction is clear: what makes an agent trustworthy is the same contract that makes a dashboard trustworthy.
Annexes · Reference
The roles, the tools
The main thread introduces each role and each block at the moment the film brings them on stage. Here, for the complete map, are the reference tables.
Roles and responsibilities
A business domain (ownership)
| Role | Responsible for | Does not |
|---|---|---|
| Domain owner | The data of their business domain; what becomes (or not) a data product, and its quality. | Doesn’t build the technical implementation themselves (unless in a small organization). |
| Data product owner | A specific product: its uses, its consumers, its contract. Often the same person as the domain owner in a small organization. | Doesn’t manage the infrastructure. |
| Domain data engineer (or data engineer agent) | The technical implementation: code, tests, executions. The merge to production after the gates. | Doesn’t decide on the business contract; doesn’t set global rules. |
| Platform team | The self-service platform: equipped blocks, developer experience, the reliability of the “factory”. | Owns no business data; isn’t a validation bottleneck. |
| Federated governance (reviewer, steward, DPO, compliance) | Global rules (security, compliance, interoperability) encoded in policy-as-code; the verdicts at gates. | Doesn’t write business code; doesn’t own the domain’s data. |
| Consumer (another domain, analyst, AI) | Their use of the data; respecting the purpose granted to them. | Doesn’t modify the product; doesn’t re-publish it without a contract. |
| Enabling team | Ramping up domain competence: workshops, coaching. | Eventually owns neither data nor platform. |
In a large organization, these roles are held by distinct people; in an SME, one person wears several hats, including that of DPO (data protection officer), subject to the incompatibilities of regulation: GDPR only permits this combining of roles if it doesn’t create a conflict of interest (Article 38)[19].
Tools and vendors
| Block | Plane | What we expect from it | Examples |
|---|---|---|---|
| Catalog & discovery | plane: mesh | Make the products of all domains globally addressable (schema, owner, lineage, guarantees); search and correlate across domains. | DataHub, OpenMetadata, Collibra, Purview, Zeenea |
| Data contracts | plane: product | Express the contract (schema, SLO, quality, purpose) and enforce it, not just document it. It feeds the discovery port: the product’s identity card. | ODCS / Bitol, dbt model contracts, Schema Registry |
| Ingestion / iPaaS | plane: infra | Bring in data from sources, including off-mesh (legacy, SaaS, partner APIs). | MuleSoft, Boomi · Airbyte, dlt, Fivetran · Debezium (CDC) |
| Orchestration | plane: infra | Schedule and chain the steps (ingest → transform → test → publish). | Airflow, Dagster, Prefect, Azure Data Factory |
| Transformation | plane: infra | The product’s “code”: cleaning, enrichment, aggregation. | dbt, SQLMesh, Spark, Flink SQL, Polars |
| Storage & serving | plane: infra | Materialize and serve, in batch and real-time. | Snowflake, BigQuery, Databricks · open formats Iceberg / Delta / Hudi |
| Streaming | plane: infra | Real-time products, where the stream IS the product. | Kafka / Confluent, Redpanda, Flink, Materialize |
| Quality & observability | plane: product | Measure and alert on quality (6 DAMA-UK dimensions) and freshness; feeds the product’s observability port. | Soda, Great Expectations, Monte Carlo, Elementary |
| Lineage | plane: infra | Trace origin (upstream → product → downstream). Capture, at build, is the execution stack’s business; cross-domain exploitation traces back via the catalog. | OpenLineage + native lineage of the catalogs |
| Policy-as-code | plane: mesh | Encode AND automatically enforce the global rules. | OPA / Rego, AWS Lake Formation, Immuta |
| Market & access gates | plane: mesh | Discover, request, approve or refuse access. | Data Mesh Manager, DataHub workflows, Lake Formation |
| Finops data | plane: infra | Assign cost per data product; chargeback / showback. | FinOps Foundation, FOCUS · Snowflake / Databricks attribution |
| CI-CD & env promotion | plane: infra | Pipelines dev → preprod → prod with gates; contract enforced in CI. | GitHub/GitLab CI · dbt Slim CI · Dagster code locations |
| Semantic layer | plane: product | “Define once, use everywhere”: governed metrics; the product’s semantic self-description. | dbt Semantic Layer, Cube, AtScale |
It’s this stacking of blocks that materializesthe data product and its five ports seen in scene 1: transformation is the “code”, the contract feeds the discovery port (which the catalog aggregates for cross-domain search), observability and lineage feed the observability port, policy-as-code feeds the control port, and storage/streaming serves the output ports. The data product doesn’t exist “in the air”: it’s placed on this platform.
Self-service platform: assembled, not turnkey
Sources · Bibliography
The cited sources
The sources cited in the article, in order of appearance. The marker [n] in the text points to the corresponding entry below.
- [1]Zhamak Dehghani, Data Mesh Principles and Logical Architecture (2020). martinfowler.com.
- [2]INNOQ (J. Christ, L. Visengeriyeva et al.), Data Product Canvas. datamesh-architecture.com.
- [3]Alberto Brandolini ; INNOQ, Event Storming ; « Data Mesh to Go: How to Get the Data Product » (2021). innoq.com.
- [4]Martin Fowler, BoundedContext (2014). martinfowler.com.
- [5]Eric Evans, Domain-Driven Design: Tackling Complexity in the Heart of Software (2003). Addison-Wesley.
- [6]Agile Lab / Witboost, How to identify Data Products? Welcome « Data Product Flow » (2022). witboost.com.
- [7]Zhamak Dehghani, How to Move Beyond a Monolithic Data Lake to a Distributed Data Mesh (2019). martinfowler.com.
- [8]Bitol (Linux Foundation AI & Data), ODCS : Open Data Contract Standard, v3.x. bitol.io.
- [9]Adam Bellemare (Confluent), Data Products, Data Contracts, and Change Data Capture. confluent.io.
- [10]Neal Ford, Rebecca Parsons, Patrick Kua, Building Evolutionary Architectures (2017). O’Reilly.
- [11]Open Data Mesh Initiative, Data Product Descriptor Specification (DPDS). dpds.opendatamesh.org.
- [12]Bitol (Linux Foundation AI & Data), ODPS : Open Data Product Standard, v1.0.0 (2025). bitol.io.
- [13]Jarkko Moilanen et al., Open Data Product Specification. opendataproducts.org.
- [14]Matthew Skelton & Manuel Pais, Team Topologies: Organizing Business and Technology Teams for Fast Flow (2019). IT Revolution.
- [15]INNOQ, Data Mesh Architecture. datamesh-architecture.com.
- [16]DAMA International, DAMA-DMBOK: Data Management Body of Knowledge (2nd ed.) (2017). Technics Publications.
- [17]Gartner, Dark Data (IT Glossary). gartner.com.
- [18]Wiz, What is Shadow Data?. wiz.io.
- [19]Parlement européen & Conseil, Règlement (UE) 2016/679 (RGPD), art. 17 & 30 (2016). EUR-Lex.
- [20]IBM Security / Ponemon Institute, Cost of a Data Breach Report 2024 (2024). IBM.
- [21]Open Semantic Interchange (Snowflake, dbt Labs, Salesforce, BlackRock et al.), Open Semantic Interchange (OSI), v0.1.x (2025). open-semantic-interchange.org.
- [22]EDPB, Guidelines 01/2025 on Pseudonymisation (2025). edpb.europa.eu.
- [23]CNIL, L’anonymisation de données personnelles. cnil.fr.
- [24]OpenLineage (LF AI & Data), OpenLineage : An Open Standard for Lineage Metadata. openlineage.io.
- [25]FinOps Foundation (Linux Foundation), FinOps Framework. finops.org.
- [26]FinOps Foundation, FOCUS : FinOps Open Cost & Usage Specification, v1.4 (2026). focus.finops.org.
- [27]Betsy Beyer et al. (Google), Site Reliability Engineering, chap. « Monitoring Distributed Systems » et « Being On-Call » (2016). O’Reilly.
- [28]Starburst, Data Mesh: What Happened?. starburst.io.
- [29]Thoughtworks, The State of Data Mesh in 2026: From Hype to Hard-Won Maturity (2026). thoughtworks.com.
- [30]Barr Moses (Monte Carlo), When a Data Mesh Doesn’t Make Sense for Your Organization. montecarlodata.com.
- [31]Paulo Caroli, Data Mesh Readiness Assessment. caroli.org.
- [32]Thoughtworks, Data Mesh: It’s Not Just About Tech, It’s About Ownership and Communication. thoughtworks.com.
- [33]Thoughtworks, Decoder: Data Mesh. thoughtworks.com.
- [34]Adam Bellemare, Building an Event-Driven Data Mesh (2023). O’Reilly.
- [35]Eric Broda & Davis Broda, Agentic Mesh (2026). O’Reilly.
- [36]Zhamak Dehghani, Data Mesh: Delivering Data-Driven Value at Scale (2022). O’Reilly.
- [37]DAMA UK Working Group, The Six Primary Dimensions for Data Quality Assessment (2013). DAMA UK / GOV.UK.
From the film to the playground
This article is the access ramp; the playground is the film we act out. You’ll take the roles one at a time (owner, data engineer, reviewer, DPO) to create and run data products in a virtual company.
Need to see clearly for your case? I can help you audit your use cases and your architecture to decide, then draw a minimalist version that carries only the principles actually useful for you.
Playground — An interactive simulation to understand how data mesh can be implemented in an enterprise. Take on several roles by following the scenarios, or explore freely to create and maintain data products in a virtual company.