See it as a diagram
Everything below, as a diagram you can edit. Describe yours and see it in seconds.
No account needed · Editable canvas, not a picture
Draw boundaries around business capabilities, not source systems
The reliable rule comes from domain-driven design: a domain boundary is a bounded context, the span inside which one word means one thing. If "order" means a different thing in checkout than it does in fulfilment, those are two contexts and two domains, no matter that both read the same Postgres instance.
The tempting alternative, one domain per source system, feels tidy and fails within a quarter. Source systems get replaced, merged, and split. A domain named after Salesforce becomes homeless the day the CRM changes. A domain named "customer acquisition" survives the migration because the business capability did.
Practical test for a proposed boundary: can you name the team that decides the roadmap for it, name the vocabulary it defends, and name at least one data product it would publish. If any of the three has no answer, the boundary is wrong.
Write the answer down where the code enforces it. A CODEOWNERS entry per domain path is the cheapest ownership record a repository has, it is checked by the host on every pull request, and it survives reorganisations better than any wiki page. If a path in the transformation project has no owning domain in CODEOWNERS, that path is the boundary you have not drawn yet.
The three shapes a domain data product takes
Source-aligned products mirror facts native to the owning domain: checkout publishing orders, catalog publishing products. They change when the business capability changes, which makes them the most stable of the three. They should be published close to raw, with the domain vocabulary, not pre-aggregated for one consumer.
Aggregate products compose several source-aligned products into something no single domain owns cleanly, for example a customer 360 built from checkout, support, and marketing. These are where ownership fights start, so assign a single owning domain before the first pipeline runs.
Consumer-aligned products are shaped for a specific use, a machine learning feature set or an executive reporting mart. They are the most volatile, and they belong to the consuming domain rather than the producing one. Putting a consumer-aligned product in a producer domain makes the producer responsible for a shape they cannot predict.
What stays central after decentralization
Storage and compute provisioning, orchestration, the catalog, access control, secrets, lineage collection, and observability tooling stay central and are offered as a platform. Domains consume those capabilities; they do not choose their own orchestrator.
Cross-domain semantics stay central as decisions, not as implementations. The definition of customer_id, the privacy classification vocabulary, the naming convention for output ports, and the minimum quality bar are set by the federated group and enforced by the platform, while each domain writes its own transformation code.
Everything else moves: transformation logic, schema evolution, the data contract, the SLA, documentation, and the on-call rotation for the products the domain publishes.
# CODEOWNERS in the transformation repo: the boundary, enforced models/checkout/** @acme/checkout-data contracts/checkout/** @acme/checkout-data models/catalog/** @acme/catalog-data contracts/catalog/** @acme/catalog-data models/marketing/** @acme/marketing-data # Central, and deliberately so: changing these needs the platform team macros/** @acme/data-platform packages.yml @acme/data-platform policy/** @acme/data-governance # Anything not matched above is a boundary nobody has drawn yet.
The handover, and how not to lose the map during it
The handover is the risky month. Pipelines change hands, repositories split, and the one accurate architecture diagram becomes wrong within days because the person who maintained it is no longer the person who owns the pipeline. This is the failure mode worth designing against explicitly.
Connect the repositories that survive the split (the dbt project, the Terraform that provisions each domain account) to Datadef with read-only access on GitHub, GitLab, or Azure DevOps, pick the branch each domain ships from, and the diagram plus its architecture.md regenerate on a daily sync rather than depending on whoever remembers. A node someone dragged more than twelve pixels from where the last sync left it is treated as hand-placed and restored after the redraw, so the picture updates instead of reshuffling.
What the sync reads is worth knowing before you judge the result. It walks the tree once and takes at most forty files, 250KB in total and 30KB per file, in a priority order. Under the architecture focus, infrastructure files get a ceiling of sixteen while dbt models get three, because sixteen model files would tell you which columns exist and nothing about which domain owns what. If your mesh boundary lives in the model layer rather than in separate accounts or repositories, connect the domain repositories separately instead of expecting one sync of a monorepo to find it. See how repository sync works.
Put the resulting picture where the argument happens: one markdown line renders it as a live embed in each domain README, and viewers need no account to open it.
FAQ
How do you decide where a data mesh domain starts and stops?
What does a domain team actually own in a data mesh?
Who owns a data product that spans several domains?
Does every domain need its own data engineer?
How many domains should a data mesh start with?