Module 3: Communicating Architecture
3. Context and Container: the two maps you almost always need
Overview
By the end of this lesson you'll know how to draw the two C4 levels you'll use 80% of the time: the Context (level 1, the world map) and the Container (level 2, the city map). It's no coincidence they're the first two: between them they cover almost the entire real need to communicate an architecture. The Context tells anyone —including the business— what the system does and who it talks to; the Container tells any technical person what deployable pieces it's made of and how they connect. Levels 3 and 4 (Component, Code) are for concrete and bounded cases; levels 1 and 2 are the daily bread. If from this whole module you only took away the skill of drawing a good Context and a good Container of a system, you'd already be better at communicating architecture than most.
This matters because these two diagrams resolve the architect's two most frequent conversations. The first is with the business: the VP who wants to know where a new idea fits, the executive who approves a budget, the customer who evaluates an integration. That conversation is won with a Context —five boxes, zero jargon, the picture of the system in its world—. The second is with the developers: the new dev who needs to get oriented, the ops team who deploys, the engineer from another team who's going to integrate. That one is won with a Container —the real pieces, their technologies, their connections—. Mastering these two maps is mastering the two audiences an architect faces almost every day.
Connection with the module: in lesson 2 you built the C4 ladder —the four named levels—. Here you go down the first two rungs and draw them in depth over Mercado. This lesson is purely practical: you'll see Mercado's Context and Container in ```mermaid, understand what goes into each one and what doesn't, and measure —executed— how much detail each zoom carries. In lesson 4 you'll keep going down to Component and Code and learn when not to. And in lesson 5 you'll use exactly these two maps for the module's central exercise: choosing which one you give the VP and which one the dev.
Two maps of the same city: the tourist one and the metro one
Think of a city you're visiting for the first time. At the airport you grab two different maps of the same place. One is the tourist map: it shows the city as a blob with its points of interest —the historic center, the river, the three famous museums, the airport— and how they relate in broad strokes. With that map you understand what the city is and what surrounds it without drowning in detail. The other is the metro map: it shows the lines, the stations, the transfers —the pieces you actually move by and how they connect—. With that map you can already operate within the city: you know which station to get off at and where to change lines.
Both maps are of the same city, but they serve different moments and different people. The tourist who only spends a day and wants to "see the main things" uses the tourist one. The one who's going to live there and move daily needs the metro one. And no one confuses one with the other: the tourist one would be useless to know which station to change lines at, and the metro one would be overwhelming for someone who just wants to know what's interesting about the city.
The Context is the tourist map; the Container is the metro map. The Context shows the system as a blob with what surrounds it —who uses it, what it talks to— for whoever wants to understand what it is. The Container shows the internal lines and stations —the deployable pieces and their connections— for whoever's going to move around inside. Same "city" (Mercado), two maps, two audiences. Let's draw both.
Worked example: Mercado's two maps
Mercado's Context (the VP's map)
The Context answers: what does Mercado do, who uses it, and what external systems does it depend on? Mercado is a single box; around it, the people and the external systems. No technologies, no internal pieces.
C4Context
title Mercado - System Context (level 1, the VP's map)
Person(customer, "Customer", "Searches and buys products")
Person(seller, "Seller", "Publishes and sells products")
System(mercado, "Mercado", "Online marketplace: connects buyers and sellers")
System_Ext(payments, "Payment Gateway", "Processes card payments")
System_Ext(carrier, "Carrier API", "Generates labels and tracks shipments")
Rel(customer, mercado, "Searches, buys, tracks orders")
Rel(seller, mercado, "Publishes products, manages inventory")
Rel(mercado, payments, "Charges payments", "HTTPS/API")
Rel(mercado, carrier, "Requests shipments", "HTTPS/API")
Read it as the VP would read it: the customers search and buy; the sellers publish and sell; Mercado connects them; to charge it uses an external payment gateway; to ship it uses an external carrier. Five boxes, four arrows, and the complete business story in thirty seconds. PostgreSQL doesn't appear, nor the API, nor the search index —and that's fine, because the VP didn't come for that—. What appears is exactly what they need for their question ("where does opening Mercado to external vendors fit?"): they see there's already a relationship with sellers, they see the external dependencies they might have to scale, and they can decide without drowning.
Mercado's Container (the dev's map)
Now we zoom inside the Mercado box. The Container answers: what deployable pieces is Mercado made of and how do they communicate? The people and the external systems are still there, as decoration, but now the central box opens into its pieces, each one with its technology.
C4Container
title Mercado - Containers (level 2, the dev's map)
Person(customer, "Customer", "Buys")
Person(seller, "Seller", "Sells")
System_Boundary(mercado, "Mercado") {
Container(web, "Web App", "React", "Storefront in the browser")
Container(mobile, "Mobile App", "React Native", "Shopping app")
Container(api, "API", "FastAPI", "Business logic: catalog, orders, checkout")
ContainerDb(db, "Database", "PostgreSQL", "Products, orders, users")
Container(search, "Search Index", "Elasticsearch", "Catalog search")
}
System_Ext(payments, "Payment Gateway", "Stripe")
System_Ext(carrier, "Carrier API", "Ships")
Rel(customer, web, "Uses", "HTTPS")
Rel(customer, mobile, "Uses", "HTTPS")
Rel(seller, web, "Manages products", "HTTPS")
Rel(web, api, "Calls", "JSON/HTTPS")
Rel(mobile, api, "Calls", "JSON/HTTPS")
Rel(api, db, "Reads and writes", "SQL")
Rel(api, search, "Queries and indexes", "HTTPS")
Rel(api, payments, "Charges", "HTTPS/API")
Rel(api, carrier, "Requests shipments", "HTTPS/API")
Read it as the new dev would read it: the customer uses a web app in React or a mobile app in React Native; both call an API in FastAPI that has the business logic; the API reads and writes to a PostgreSQL database, queries an Elasticsearch search index, and talks to the two external systems —payments and shipping—. In a minute, the dev knows what the pieces are, what technology each one uses, and how a request flows from the browser to the database. With that they can already start reading the code with a map in their head, instead of getting lost. That's the difference between onboarding in two days and onboarding in two weeks.
How much detail each zoom carries, measured
The two maps are of the same system, but they carry different amounts of detail. Let's count it, because "less detail" doesn't mean "less honest" —it means the right detail for each audience's question—.
# How many elements each level carries for the TWO most common audiences:
# the VP (Context) and the technical dev (Container). Less detail is NOT less honest:
# it's the right detail for the question each one brings.
# Mercado Context diagram: people + external systems + Mercado as one box.
context_elements = [
("person", "Customer"),
("person", "Seller"),
("software_system", "Mercado"),
("external_system", "Payment Gateway"),
("external_system", "Carrier API"),
]
# Mercado Container diagram: the same boundary, opened into deployable pieces.
container_elements = [
("person", "Customer"),
("person", "Seller"),
("container", "Web App"),
("container", "Mobile App"),
("container", "API"),
("container", "Database"),
("container", "Search Index"),
("external_system", "Payment Gateway"),
("external_system", "Carrier API"),
]
def summarize(name, elements):
total = len(elements)
kinds = {}
for kind, _ in elements:
kinds[kind] = kinds.get(kind, 0) + 1
detail = ", ".join(f"{v} {k}" for k, v in kinds.items())
print(f"{name:<11} {total:>2} elements ({detail})")
print("The SAME system, two zooms:")
summarize("Context", context_elements)
summarize("Container", container_elements)
print()
print("The VP looks at Context: 5 boxes, zero jargon, understands what Mercado does and who it talks to.")
print("The dev looks at Container: 9 boxes, now with technologies, knows where each thing lives.")
print("Neither of the two sees the code. They don't need it yet.")
What to expect. Running it:
The SAME system, two zooms:
Context 5 elements (2 person, 1 software_system, 2 external_system)
Container 9 elements (2 person, 5 container, 2 external_system)
The VP looks at Context: 5 boxes, zero jargon, understands what Mercado does and who it talks to.
The dev looks at Container: 9 boxes, now with technologies, knows where each thing lives.
Neither of the two sees the code. They don't need it yet.
Notice what changed and what didn't. What didn't change: the people (2) and the external systems (2) are still there in both —the world around Mercado is the same in both zooms—. What changed: the single software_system box "Mercado" of the Context opened into 5 containers. That's exactly the move from zoom 1 to zoom 2: open the system's box and see its pieces, without touching the world around it. And the last line is key: neither of the two diagrams shows code. The VP doesn't need it, the new dev doesn't yet —they first get oriented with the city map, and only go down to the code when they're going to touch a specific piece—.
What goes in and what doesn't at each level
The discipline of C4 is in being strict about what belongs to each level. Here's the rule for the two we just drew.
In the Context goes: your system as a single box; the people who use it (by role, not by name: "Customer", not "Ana"); the external systems your system talks to; and the relationships among them, labeled in business language ("buys", "charges payments"). What doesn't go: any technology, any internal piece, any database. If the word "PostgreSQL" or "API" appears in your Context, you went down a level by accident. The test: a well-made Context is understood by someone who doesn't know how to program.
In the Container goes: the deployable pieces of your system (apps, services, databases, indexes, queues), each with its responsibility and its main technology; the people and external systems as decoration around; and the relationships labeled with how they communicate ("JSON/HTTPS", "SQL"). What doesn't go: the interior of each container —its classes, its functions, its internal components—. If the OrderController class appears in your Container, you went down two levels by accident. The test: a well-made Container is useful for a dev to know where each thing lives, without yet telling them how it's written inside.
This strictness is what keeps the diagrams legible. The constant temptation —which lesson 7 calls by its name— is to "take advantage" of the Container to also throw in some important internal component, or to "take advantage" of the Context to clarify that such-and-such database is used. Every time you give in to that temptation, you mix levels and the diagram loses the power to communicate to its audience. The system's completeness doesn't live in one loaded sheet; it lives in the set: Context + Container + (where needed) Component, each one clean.
The arrows also communicate: label the how, not just the what
It's easy to obsess over the boxes and neglect what really makes a diagram legible: the arrows and their labels. An unlabeled arrow —or one with a vague label like "uses" or "connects"— wastes half the diagram's communicative power, because the relationship between two pieces is usually as informative as the pieces themselves. The rule is that an arrow's label answers what happens through there, in the language of the level you're at.
In the Context, the labels go in business language: "searches and buys", "publishes products", "charges payments". The VP reads the arrow between Mercado and the payment gateway and understands "this is where the money comes in" with nothing more. A label like "HTTP POST /charge" in the Context would be a level leak: too technical for that audience.
In the Container, the labels gain a second piece of data —the technical how— without losing the what: "calls, JSON/HTTPS", "reads and writes, SQL", "charges, HTTPS/API". Notice that C4 lets you put the protocol or technology in the relationship's label (that's why we write Rel(web, api, "Calls", "JSON/HTTPS") with two fields): the first says what it does, the second how it travels. For the dev, that "how" is gold: it tells them whether the communication is synchronous (an HTTP call) or asynchronous (a queue), whether it crosses the network or is local, what format to expect. A Container with well-labeled arrows saves the dev from opening the code just to find out "is this REST or a queue?".
There's a useful asymmetry in the direction of the arrow: it points from whoever initiates toward whoever responds —the one making the request toward the one serving it—. In Mercado, the arrow goes from the Web App toward the API (the web calls the API, not the other way around), and from the API toward the database (the API queries the database). That direction communicates the flow of control at a glance: you follow the arrows from a person and see how an action propagates through the system until it touches the data. A diagram with arrows in the wrong direction, or bidirectional out of laziness ("better I put a double head just in case"), loses exactly that information. The discipline: each arrow, one direction, one label saying what happens through there in the language of the level.
Common mistakes
Putting technology in the Context (of a level leak). What happens: the Context, which should be legible for the business, ends up with labels like "REST API", "microservices", or "PostgreSQL" because the architect thought it was "useful information". Why it happens: for the architect, the technology is the interesting part, and it's hard to resist the urge to mention it. How to spot it: show your Context to someone non-technical; if they ask "what's an API?", you put level 2 into level 1. How to fix it: in the Context, everything is said in business language —"Mercado charges payments with an external provider", not "the API calls Stripe via REST"—; the technology is the Container's.
A Container that's really a Component (of too much zoom). What happens: the level 2 diagram shows "OrderService", "PaymentService", "TaxCalculator", "NotificationHandler" —but they all live inside the same API, they're not separately deployable pieces—. That's not a Container, it's a disguised Component. Why it happens: "logical piece of the code" is confused with "deployable piece". How to spot it: ask of each box "is this deployed and run separately?"; if the answer is "no, it's a class inside the API", it's a component, not a container. How to fix it: in the Container only things that deploy separately go (the whole API, the database, the index); the interior of the API is level 3.
A Context or Container without the external dependencies (of the island-system). What happens: the diagram shows only what the team built, and omits the payment gateway, the carrier, the identity provider —the external systems the system depends on—. Why it happens: you draw "your stuff" and forget the system doesn't live alone. How to spot it: if your diagram suggests Mercado charges and ships by magic, without showing whom it depends on, you lie by omission. How to fix it: the value of these levels is precisely in showing the boundaries —who your system talks to—, because that's where the risks, costs, and integration points that matter to the business and the dev are.
Exercises
Exercise 1 — Clean up the Context. An architect shows you this "Mercado Context": a box "Mercado (FastAPI + PostgreSQL)", a box "Customer", a box "PostgreSQL Database", a box "Stripe", and an arrow from Mercado to "AWS S3 (product images)". Three things are wrong for a Context. Find them and correct them.
See solution
Error 1: the technology in the system box. "Mercado (FastAPI + PostgreSQL)" puts level 2 into level 1. In the Context the box should say only "Mercado — Online marketplace", with no technologies. The VP doesn't need to know it runs on FastAPI.
Error 2: the database as a Context box. "PostgreSQL Database" is an internal piece of the system —a container—, not an actor of the world around it. It doesn't belong in the Context; it will appear when we zoom into the Container. In the Context, the database is inside the Mercado box, invisible.
Error 3 (subtler): mixing detail levels in the externals. Stripe (payment gateway) and AWS S3 (image storage) are both valid external systems in the Context, so having them isn't wrong in itself; what's wrong is the inconsistency with the rest —if we show S3 but omit the shipping carrier, we give a biased picture—. The underlying correction: in the Context, show the business-significant external dependencies evenly (payments, shipping, and yes, storage if relevant), all at the same abstraction level and in business language ("stores product images", not "AWS S3 bucket").
The corrected Context: Mercado (one box, no tech) ← Customer and Seller (people) → and Mercado talks to Payment Gateway, Carrier API, and image storage (externals, in business language). The database disappears from this level.
Exercise 2 — From the Context to the Container. You have the Context of a restaurant reservation system: a box "ReservaYa" used by "Diner" and "Restaurant", that talks to an external "SMS Provider". You're asked for the Container. The system inside has: a web app, a mobile app, an API, a database, and a worker that sends reminders by SMS. Draw (in text or mermaid) how the Container would look, and say what from the Context is kept and what is opened.
See solution
The world's actors are kept: Diner and Restaurant (people) and the SMS Provider (external) stay the same, as decoration. The "ReservaYa" box is opened into its deployable pieces.
C4Container
title ReservaYa - Containers
Person(diner, "Diner", "Books a table")
Person(resto, "Restaurant", "Manages availability")
System_Boundary(reservaya, "ReservaYa") {
Container(web, "Web App", "React", "Reservations from the browser")
Container(mobile, "Mobile App", "Flutter", "Reservations from mobile")
Container(api, "API", "Node.js", "Reservation logic")
ContainerDb(db, "Database", "PostgreSQL", "Reservations, tables, users")
Container(worker, "Reminder Worker", "Python", "Sends reminders")
}
System_Ext(sms, "SMS Provider", "Sends messages")
Rel(diner, web, "Uses", "HTTPS")
Rel(diner, mobile, "Uses", "HTTPS")
Rel(resto, web, "Manages", "HTTPS")
Rel(web, api, "Calls", "JSON/HTTPS")
Rel(mobile, api, "Calls", "JSON/HTTPS")
Rel(api, db, "Reads and writes", "SQL")
Rel(worker, db, "Reads upcoming reservations", "SQL")
Rel(worker, sms, "Sends reminders", "API")
The interesting thing about the exercise is the worker: it's a container even though it has no user interface, because it deploys and runs separately. And notice that the "SMS Provider" that in the Context talked to "ReservaYa" (the whole box), in the Container is seen to actually talk to one specific piece —the worker—. That's the value of going down a level: the Context's vague relationships become precise in the Container.
Exercise 3 — What map do I ask for? For each situation in Mercado, say whether you need to show the Context or the Container, and why: (a) the sales team wants a sheet for the pitch to an investor; (b) a dev from another team is going to build a service that consumes orders from Mercado and asks "what do I talk to and how?"; (c) the product lead wants to understand, before approving a project, what parts of the system adding "wishlists" would touch.
See solution
(a) The pitch to the investor → Context. The investor brings the world's question: what is this, who uses it, what does it depend on? Five boxes with no jargon tell the business story and fit on a slide. A Container would lose them in technologies they don't evaluate.
(b) The dev who's going to integrate → Container. They need to know which piece they talk to and how —is there an API? what protocol?—. The Container shows them the API as a container, with its technology and its connections, which is exactly what they need to integrate. The Context would be too little (they wouldn't see the API); the Component would be too much (they don't care how the API is split inside, only its external contract).
(c) The product lead evaluating "wishlists" → it depends, but probably starts in Context and maybe a look at the Container. If their question is purely business ("does this fit with what we do?"), the Context suffices. But since they specifically ask what parts it would touch, they're already asking for something from the city map: they'll want to see that the functionality probably lives near the catalog and the users, which is better appreciated in a simplified Container. The mature answer: use the Context for the frame ("here's the system") and point out on a bounded Container which pieces would be touched, without drowning them in all five. Show the minimum level that answers their question and not one more.
Summary and next step
In this lesson you drew the two maps you'll use 80% of the time: Mercado's Context (the tourist map: five boxes, zero jargon, for the VP and the business) and Mercado's Container (the metro map: nine boxes with technologies, for the dev and ops). You saw, executed, that going down from zoom 1 to zoom 2 is literally opening the system's box into its pieces without touching the world around it —the people and the externals are kept, the single Mercado box becomes five—. And you fixed the discipline of what goes in each level: zero technology in the Context, zero classes in the Container, and always the external dependencies visible because that's where the boundaries that matter are.
Before moving on you should be able to: draw a Context legible for the business (system as one box, people, externals, business language) and a Container legible for devs (deployable pieces with technology, external decoration); explain what is kept and what is opened when going from one to the other; and distinguish a real container (deploys separately) from a disguised component (a class inside a piece).
What follows is to keep going down —and to learn when not to—. In lesson 4 you'll draw the Component of the checkout container (the neighborhood map, for the dev working inside that piece) and see why the Code (the street map) is almost never drawn by hand. You'll measure, executed, how many diagrams are really worth maintaining in Mercado —spoiler: three, not thirty— and understand the zoom rule: you go down a level only where the detail helps a real person work.
Resources
- Simon Brown — System Context diagram (c4model.com) — the canonical definition of level 1, with what to include and what to omit. Contrast it with your Mercado Context.
- Simon Brown — Container diagram (c4model.com) — the canonical definition of level 2, including the clarification that "container" isn't Docker. The exact reference for this lesson.
- arc42 — building block view — how arc42 organizes the description of blocks by levels; it fits naturally with C4's Context/Container and shows how these diagrams live within a larger documentation.
- Gregor Hohpe — The Software Architect Elevator — for the part of "talking to the business with the Context and to the engineers with the Container": it's the skill of translating between floors of the building, applied to these two diagrams.