Your DDD Context Map Is Already in Your Foreign Keys
The Problem with Hand-Drawn Context Maps
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 - 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 - 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 Schemity's Context Map Works
How do I see the dependencies between bounded contexts in my database? Derive the picture from the schema instead of drawing it.
In Schemity, an offline ERD tool built for software engineers, that is what the 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.
In my own multi-tenant auth schema - 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 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 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: 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.
Arrow Shapes and Cycle Detection
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.
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 gives you two ways to walk the graph:
- The manual way turns click-to-highlight into a traversal: click the context you are checking, follow its highlighted arrows to each target, and re-highlight round by round - if a round ever lights an arrow back to where you started, you have found an indirect cycle. One click is one round of that walk: the selected context keeps its arrows and their foreign key counts in full color while every other dependency drops back, so you are reading four edges instead of twenty.
- Or ask the built-in AI chat, which analyzes the Context Map and names the cycles directly - the faster route when the map has many contexts. It will also re-arrange the map's layout on request, so tidying the architecture picture is one sentence, not a drag session.
Drill-Down on Every Arrow
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: double-click it, and a panel lists each entity relation behind the dependency - source field, target entity, and the real constraint name.
The 5-key arrow from Auth View into Account View expands into:
member_roles.member_id->membersotps.member_id->membersotps.tenant_id->tenantsroles.tenant_id->tenantssso_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. 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 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. 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 double-click away the whole time.
Comparison: Drawn vs. Derived
| 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) | Double-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 flag every cycle at a glance |
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, 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.
Coupling Between Contexts
I wrote before that 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 tells you an entity reaches outside the view - the Context Map is where that reach is finally analyzed rather than merely flagged.
This is the zoom ladder domain-driven design database schema work needs:
- a database context view to read each bounded context without the noise 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 a desktop ERD tool 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.
Export and Portability
The Context Map exports as JPG, PNG, or sharp-at-every-zoom SVG, 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.
Disclosure: I build Schemity, a desktop ERD tool - this post is from our blog and uses it for the examples.
Comments
No comments yet. Start the discussion.