# Your DDD Context Map Is Already in Your Foreign Keys

> Hand-drawn context maps go stale because nothing ties them to the schema, and cross-context coupling stays invisible until an extraction fails. Schemity's Context Map derives the map from your foreign keys: each context view becomes one node, arrows show dependency direction with a foreign key count, and clicking an arrow lists the exact foreign keys behind it.

Source: https://schemity.com/blog/your-ddd-context-map-is-already-in-your-foreign-keys/

You do not need to draw your context map, because your schema already contains it: every foreign key that crosses a context boundary is one edge of the map. Schemity's Context Map renders that map for you - each context view collapses into a single node, and the arrows between nodes are derived from the actual foreign keys in the diagram, not from anyone's memory of them.

The reason this matters is what teams do today instead. The context map - the DDD picture of which bounded contexts exist and who depends on whom - is usually a drawing. As one practitioner report on Context Mapper puts it, DDD models "are typically developed on a whiteboard or with Post-it notes," and [few tools support maintaining them](https://ozimmer.ch/modeling/2022/11/23/ContextMapperInsights.html) - which is why a dedicated DSL for writing context maps by hand has existed since 2018. A whiteboard photo is stale the day after the workshop. A hand-written DSL file is better, but it is still a second artifact: someone has to notice that the schema changed and go edit the map to match. Nothing enforces that, so nobody does it, and the map quietly stops being true.

Meanwhile the coupling the map was supposed to expose keeps accumulating where nobody is looking. One backend engineering account describes how a single innocent `JOIN payments.transactions ON orders.payment_id = payments.id` "ends up becoming the coupling hell," and recounts teams [struggling for 3+ years to break core business logic out of a big database](https://backendengineeringadventures.substack.com/p/database-design-for-modular-monoliths) - because by the time anyone tried to extract a module, months of cross-module foreign keys had piled up unseen. The pain is not that the dependencies exist. It is that no view of the system showed them at the scale where they could be judged.

## How do I see the dependencies between bounded contexts in my database?

Derive the picture from the schema instead of drawing it. In Schemity, database design software built for software engineers, that is what the [Context Map](https://schemity.com/doc/context-map/) does.

A context view is a saved, focused view of your schema that shows only a subset of entities and the relationships between them - the natural home of a **bounded context ERD**. The Context Map is the level above: a view where each context view is rendered as a single node, and an arrow is drawn from context A to context B whenever entities in A hold foreign keys into entities in B. Each arrow carries a badge with the number of foreign keys flowing in that direction.

There is no separate modeling step to reach it. The contexts are the views you already saved while working on the schema, and the map is one click away from the list of them.

![The Context Views panel listing the saved context views in a schema, with the Context Map button in the panel header](https://schemity.com/images/blog/your-ddd-context-map-is-already-in-your-foreign-keys/context-map-list.webp)

In my own [multi-tenant auth schema](https://schemity.com/blog/design-data-model-for-multi-tenant-rbac/) - 56 entities, 71 relations on the main view - the Context Map collapses to three nodes: `Account View`, `Auth View`, `Segment View`. Between `Auth View` and `Account View` there are two arrows, one per direction: 1 foreign key flowing out of `Account View`, 5 flowing back into it. That asymmetry is the design in one glance - identity lives in `account`, and `auth` consumes it. On a whiteboard map I would have drawn one vague line labeled "uses." The derived map tells me the direction and the weight, and it can never disagree with the schema, because it is computed from it.

Each context view can carry its own color, and the Context Map inherits it: the node header and every outgoing dependency arrow take the context's color, the same way [entity colors propagate to relationship lines](https://schemity.com/blog/you-shouldnt-have-to-guess-which-line-goes-where/) one level down. Contexts stay recognizable at both zoom levels, and clicking a context highlights all of its dependency arrows, so inspecting one context on a busy map never means following lines by eye. Every arrow begins with a circle at its source end - the same discipline as line hops on the main canvas: where a line starts must never be a guess, even when it passes through another context on its way. On a map with many nodes, [fuzzy search](https://schemity.com/blog/finding-a-column-shouldnt-take-a-sql-query/) works here too - type a fragment of a context's name and the map focuses and highlights it - and the footer keeps the two numbers that describe the architecture in view: how many contexts, how many dependencies.

Three nodes is a small schema. Here is the same view on a larger one - a CMS whose footer reads 12 contexts and 20 dependencies between them:

![A Context Map of twelve contexts including account, Auth, catalog, learning, and targeting, with twenty dependency arrows between them, each carrying a badge with its foreign key count](https://schemity.com/images/blog/your-ddd-context-map-is-already-in-your-foreign-keys/context-map.svg)

At that size the whole architecture still fits on one screen, which is the point: twelve contexts and twenty dependencies is a paragraph of prose or an unreadable wall of 60 tables, but it is a single readable picture. Finding one context in it is a few keystrokes rather than a hunt.

![The context search panel open over the Context Map, listing each context with its entity count](https://schemity.com/images/blog/your-ddd-context-map-is-already-in-your-foreign-keys/context-map-search.webp)

The arrows carry one more signal: shape. A straight arrow is a one-way dependency. A curved arrow means the dependency runs both ways - a circular dependency between two contexts. That is exactly what the curved pair between `Auth View` and `Account View` admits about my schema: those two contexts lean on each other, and neither could be extracted without touching the other. Scanning a schema for context-level cycles used to be a query-writing exercise; on the Context Map it is a visual scan for curves. One level down, [the same cycle between two individual tables](https://schemity.com/blog/circular-foreign-keys-first-row-cannot-be-inserted/) is not an architecture smell but a hard failure - if every foreign key column in it is `NOT NULL`, neither table can take its first row.

Curves catch the two-context case. For cycles that hide across three or more contexts - A depends on C, C on B, B back on A - every individual arrow is straight, so no shape can flag them. That is a graph question, and the map answers it: a **Cycles** button lists every dependency loop between contexts, shortest first, and picking one lights only that loop's arrows. Selecting a context does the rest of the reading: it highlights what the context depends on and dims what depends on it while the other arrows step aside, so you are reading four edges instead of twenty. Whether each of those cross-context keys should stay is its own decision, worked through in [foreign keys between modules in a modular monolith](https://schemity.com/blog/foreign-keys-between-modules-modular-monolith/).

![The learning context selected on the Context Map, its outgoing arrows highlighted with their foreign key counts while every other dependency fades into the background](https://schemity.com/images/blog/your-ddd-context-map-is-already-in-your-foreign-keys/context-map-active-context.webp) Or ask your AI agent: over [MCP it reads the context dependencies](https://schemity.com/doc/ai-assisted-design/) and names the cycles directly - the faster route when the map has many contexts. And the map arranges itself the first time you open it, so the dependencies read at a glance rather than in name order.

## Every arrow is backed by the exact foreign keys that create it

A high-level map you cannot interrogate is just a prettier whiteboard. So every arrow on the Context Map answers for itself: click it, and a panel lists each entity relation behind the dependency - source field, target entity, and the real constraint name. Those named constraints are also the routes [a cascading delete travels between contexts](https://schemity.com/blog/on-delete-cascade-is-invisible-in-your-erd/), which makes the panel the fastest way to check the `ON DELETE` of everything crossing a boundary.

The 5-key arrow from `Auth View` into `Account View` expands into `member_roles.member_id -> members`, `otps.member_id -> members`, `otps.tenant_id -> tenants`, `roles.tenant_id -> tenants`, and `sso_configs.tenant_id -> tenants`. Five keys, two shapes: a member identity or a tenant identity flowing inward.

The badge is a promise the panel keeps: one row per foreign key, always. On the CMS map above, the `3` on `progress -> learning` opens into three relations - `trainee_badges.trainee_id`, `trainee_reward_points.trainee_id`, and `trainee_stage_points.trainee_id`, each pointing at `trainees`, each with its real constraint name attached.

![The dependency detail panel for the progress to learning arrow, listing three entity relations with their source fields, target entity, and constraint names](https://schemity.com/images/blog/your-ddd-context-map-is-already-in-your-foreign-keys/context-map-dependency-detail.webp)

That is the difference between "progress depends on learning" and a work list. Three columns, three constraints to drop or replace - and the shape of the coupling is legible too: all three point at the same table, so this dependency is one concept borrowed three times, not three separate entanglements.

The nodes answer for themselves the same way. A context that carries a markdown description shows a file icon on its Context Map node, opening the rendered description in place - so [the data dictionary that lives inside the ERD](https://schemity.com/blog/the-data-dictionary-should-live-in-the-erd/) surfaces at the architecture scale too: what a context *is for* sits one click from the arrows that show what it *depends on*. This is where a boundary stops being a shape and starts being a rule: which tables the context owns, which it merely borrows as a scope anchor, and what it deliberately excludes.

![The markdown description modal for the Auth context, opened from the file icon on its Context Map node, describing what the view covers and its boundary rules](https://schemity.com/images/blog/your-ddd-context-map-is-already-in-your-foreign-keys/context-map-context-description.webp) That is a coupling surface you can defend in a design review - or the itemized work list you hand to whoever extracts the module. The "3+ years of untangling" story above is what happens when that list is assembled *after* the extraction decision instead of being one click away the whole time.

## Why a derived context map beats a drawn one

| | Drawn map (whiteboard, Miro, DSL) | Schemity Context Map |
|---|---|---|
| Source of the edges | Someone's memory of the schema | The foreign keys themselves |
| When the schema changes | Someone must notice and redraw | The map is derived, so it follows |
| Dependency weight | Not shown, or guessed | Foreign key count per direction |
| Drill-down | None - ask the person who drew it | One click lists every FK and constraint name |
| Direction of dependency | Often a line with no arrowhead | Always directional, one arrow per direction |
| Circular dependencies | Discovered during extraction | Curved arrows and a Cycles list flag every loop |

The left column is not a strawman; it is the state of the art the Context Mapper authors describe. The right column is only possible because in Schemity the contexts and the schema live in the same file: the [main view is the single source of truth](https://schemity.com/doc/single-source-of-truth/), context views are perspectives on it, and the Context Map is derived from both. There is no moment where the map and the schema can drift, because the map is not stored - it is computed.

## The third scale: coupling between contexts

I wrote before that [you cannot judge coupling and cohesion from the same view](https://schemity.com/blog/you-cannot-judge-coupling-and-cohesion-from-the-same-view/) - the main view answers coupling, a context view answers cohesion. The Context Map completes that instrument set with a third scale. The main view shows you *which entities* couple across borders; the Context Map shows you *how much* and *in which direction*, with the entities abstracted away entirely. And inside a context view, [the orange boundary dot](https://schemity.com/blog/a-view-owes-you-back-the-context-it-removes/) tells you an entity reaches outside the view - the Context Map is where that reach is finally analyzed rather than merely flagged.

Counting the crossings is a reading you can do on any schema divided this way: in [a property management model split into five context views](https://schemity.com/examples/property-management-database-schema/), seven of the thirteen relationships stay inside a single view and four cross between them, and those four are most of the dependency structure - the last two belong to a link table deliberately filed under no context at all. This is the zoom ladder domain-driven design database schema work needs: a [database context view](https://schemity.com/doc/context-views/) to read each bounded context without the noise of [all 800 tables](https://schemity.com/blog/you-dont-need-a-diagram-of-all-800-tables/), the main view to see every entity and edge at once, and the Context Map to see the architecture - contexts and dependencies, nothing else. All three are ERD perspectives on one set of entities in one local JSON file, in database design software that works entirely offline - so the context map you show in the architecture review is not a hopeful drawing. It is the schema, zoomed out.

And it travels. The Context Map exports as JPG, PNG, or [sharp-at-every-zoom SVG](https://schemity.com/blog/why-your-erd-export-turns-blurry/), or as a Mermaid diagram that GitHub, GitLab, Notion, and Obsidian render natively - so the derived architecture picture can sit in the README of the very repo whose modules it describes, regenerated whenever the schema moves.
