Documenting Architecture: ADRs and the C4 Model

Software Architecture Fundamentals

Chapter 9 · Documenting Architecture: ADRs and the C4 Model

Chapters 1–8 made real architectural decisions — a repository boundary, layering, MVC's variants, event-driven boundaries, ports and adapters. None of it is worth anything to a future engineer unless it's written down in a way that actually answers their questions. This chapter measures the difference between a documented decision and a genuinely useful one.

Two ADRs for the Same Decision

Both records below document the exact same real decision this course made in Chapter 6 — but only one of them can actually answer a future engineer's questions.

# BAD_ADR Title: Order/User Communication Status: Accepted Decision: We will use events for order processing.
# GOOD_ADR Title: ADR-003: Order/User Communication via Events, Not Direct Calls Status: Accepted (2026-08-12) Context: OrderService needs to award loyalty points and send confirmation emails when an order is placed. These actions require data from the User domain. Direct synchronous calls were considered, but risk cascading failures if UserService is slow or down (see Chapter 4's chained-call latency findings). A shared database was also considered, but was rejected due to the coupling risk verified in Chapter 4. Decision: OrderService will publish an OrderPlaced event via a shared EventBus. UserService and EmailService will subscribe independently. OrderService will have zero code-level knowledge of either subscriber. Consequences: This introduces eventual consistency - a real, measurable window after an order is placed where loyalty points and the confirmation email have not yet been processed (see Chapter 6's own verified async timing). The UI must communicate this to the user.
Verified directly — a measurable 1-of-4 vs. 4-of-4 answerability gap
Testing both ADRs against four real questions a future engineer would ask ("why not direct calls?", "why not a shared database?", "what do we lose by doing this?", "what was actually decided?"), searching each ADR's own text for the concept each question needs: BAD_ADR answers only 1 of 4 — it states the decision, but nothing else. GOOD_ADR answers 4 of 4 — every question a reader would actually have is addressed directly in the text.
Why this matters six months later
BAD_ADR genuinely satisfies "was this decision documented? yes." But a new engineer reading it later still can't tell whether direct calls were considered and rejected, or never considered at all — the exact ambiguity this course's own Chapter 4 (cascading calls) and Chapter 5 (shared-database coupling) exist specifically to resolve. A record that only states the "what," never the "why" or "what it costs," is a paper trail, not documentation.

The C4 Model: Four Levels of Zoom

LevelShows
1. ContextThe system as one box, and who/what interacts with it
2. ContainerThe major running pieces inside the system (services, databases, APIs)
3. ComponentThe major building blocks inside one container
4. CodeClass diagrams — rarely drawn by hand; usually generated from the code itself

A lightweight Level 1 + Level 2 diagram for the actual system this course has been building since Chapter 1, rendered and visually verified:

Level 1 — System Context Customer (person) Order Fulfillment System Lets a customer place orders and earn loyalty rewards places orders Level 2 — Containers (inside the System boundary) Order Fulfillment System boundary OrderService Ch.1–2: places orders, owns pricing & totals publishes: OrderPlaced EventBus Ch.6: pub/sub — Design Patterns' Observer, reused at the architecture level UserService Ch.5–6: awards loyalty points on OrderPlaced EmailService Ch.5–6: sends order confirmation on OrderPlaced PricingEngine Ch.7: core logic, depends only on ports (InventoryPort, NotificationPort) OrderRepository Ch.1–2: data layer — owns storage, no business rules of its own Pricing API Ch.8: one stateless endpoint, both clients call it Mobile client Web client publish() notify notify port call
Verified directly — the diagram was rendered and visually checked before being finalized
This diagram was built as a standalone SVG, rendered via headless Chrome, and inspected as a real screenshot before being embedded here — the same technique Pseudocode & Algorithmic Problem-Solving Chapter 3 used for its own flowchart. A first render had a genuine layout bug (the Web Client arrow pointed at the wrong box, crossing over the Mobile Client box); it was caught in the screenshot and fixed before finalizing.
Why two levels was enough here
A Level 3 (Component) diagram for OrderService alone would show Order, OrderBuilder, and the various strategy/state classes from Design Patterns — genuinely useful for someone working inside that one service, but unnecessary for someone trying to understand how the whole system fits together. Match the diagram's zoom level to the question actually being asked, the same way Chapter 5 matched its own analysis method to the question "where should the boundary go."

Where This Connects

This chapter's findingWhat it connects to
A verified 1-of-4 vs. 4-of-4 ADR answerability gapChapter 8's own Exercise 3 — a decision's specific value (which client ordering "wins") is separate from the decision to centralize it; an ADR is where that separation gets written down explicitly
The C4 diagram naming every component from Chapters 1–8 by its own chapterThis course's own capstone (Chapter 10) — the diagram doubles as a map of what the capstone project will assemble
A real layout bug caught by rendering the diagram, not just writing its markupPseudocode & Algorithmic Problem-Solving's own SVG-verification technique, reused directly rather than trusted blind

Hands-On Exercises

Exercise 1

Write a third ADR, PARTIAL_ADR, that includes a Context section (why direct calls were rejected) but omits the Consequences section entirely. Run this chapter's own four-question test against it and report which questions it can and can't answer.

📄 View solution
Exercise 2

Add a fifth question to this chapter's own QUESTIONS dictionary: "Who is allowed to publish an OrderPlaced event?" (keywords: "OrderService will publish"). Verify both BAD_ADR and GOOD_ADR against the expanded five-question set and report the new totals.

📄 View solution
Exercise 3

Using this chapter's own C4 diagram, explain which single component you'd need to zoom into with a Level 3 (Component) diagram to understand the discrepancy Chapter 8 verified between the mobile and web thick clients — and why the Level 2 diagram alone couldn't have shown that bug.

📄 View solution

Chapter 9 Quick Reference

  • An ADR is useful when it answers real future questions: verified — a vague ADR answered 1 of 4 test questions; a full Context/Decision/Consequences ADR answered 4 of 4
  • C4 has four zoom levels: Context, Container, Component, Code — match the level to the question being asked, not to how detailed a diagram could theoretically be
  • Verified: this course's own Level 1+2 diagram was rendered and screenshot-checked before finalizing, catching a real arrow-routing bug in the first draft
  • Next chapter: the Capstone — designing the full architecture for a real application, using every chapter in this course together