An architecture that exists only in its author's head is a lost architecture. Documenting it is what allows the team to understand it, new members to onboard quickly, the business to trust what is being built, and decisions to survive personnel turnover. But documenting well is hard: diagrams tend to become outdated, mix levels of detail, or turn out to be incomprehensible to anyone who did not draw them. In this lesson you will learn two fundamental tools: the classic 4+1 view model by Philippe Kruchten and, above all, the modern C4 model by Simon Brown, a simple and pragmatic way to diagram architectures with four zoom levels: Context, Container, Component, and Code.
Contents
- Why document and for whom
- The 4+1 view model
- The C4 model: the zoom idea
- Level 1: Context diagram
- Level 2: Container diagram
- Level 3: Component diagram
- Level 4: Code
- Best practices and tools
- Why document and for whom
Documenting is not about producing hundreds of pages no one will read. It is about communicating effectively to different audiences. The main rule: document just enough, for a specific audience and with a clear purpose.
| Audience | What interests them | Level of detail |
|---|---|---|
| Business / management | What the system does and what it relates to | Very high (context) |
| Architects / team newcomers | Overall structure and technologies | Medium (containers) |
| Developers | How a module is organized internally | Detailed (components) |
| Whoever implements a class | The code itself | Maximum (code) |
A classic mistake is to use a single giant diagram for all audiences: it ends up being too detailed for the business and too superficial for the developer. The solution is to offer different views at different levels.
- The 4+1 view model
Proposed by Philippe Kruchten in 1995, the 4+1 model starts from the idea that no single view captures the entire architecture. It proposes four views plus one cross-cutting view.
| View | Describes | Main audience |
|---|---|---|
| Logical | Functionality: objects, classes, their organization | Developers, analysts |
| Process | Concurrency, processes, communication, performance | Integrators |
| Development (implementation) | Code organization: modules, packages, layers | Programmers, managers |
| Physical (deployment) | Mapping to hardware: servers, network | Systems engineers |
| +1 Scenarios (use cases) | Ties the four views together through key use cases | Everyone |
graph TD
E["+1 Scenarios<br/>(use cases)"]
E --- L["Logical View"]
E --- P["Process View"]
E --- D["Development View"]
E --- F["Physical View"]This Mermaid diagram places the scenarios at the center, connected (with ---, undirected lines) to the four views. The idea is that key use cases "bind" and validate the other views: every important scenario must be traceable through the logical, process, development, and physical views. The 4+1 model remains conceptually valuable, but in day-to-day practice many teams today prefer C4 for its simplicity and its direct mapping to how software is actually built.
- The C4 model: the zoom idea
The C4 model, created by Simon Brown, is based on an analogy with maps: just as you use different zoom levels (world, country, city, street), an architecture is best understood with different levels of detail. C4 defines four levels, each a "zoom" into the previous one:
| Level | Name | Answers | Audience |
|---|---|---|---|
| 1 | Context (System Context) | How does the system fit into its world? | Everyone, including business |
| 2 | Container | What deployable pieces is it made of? | Technical team |
| 3 | Component | How is a container organized internally? | Developers |
| 4 | Code | How is a component implemented? | Whoever programs that code |
Basic C4 concepts:
- Person: a human user of the system (customer, administrator...).
- Software system: what you build (or an external system you integrate with).
- Container: not necessarily a Docker container; it is something that runs and is deployed independently: a web application, an API, a database, a single-page app, a broker.
- Component: a logical grouping of functionality within a container.
A C4 golden rule: most teams routinely need only levels 1 and 2, and level 3 occasionally. Level 4 is almost never drawn by hand, because the IDE generates it.
- Level 1: Context diagram
This is the highest level. It shows your system as a single box, surrounded by its users and the external systems it relates to. Its purpose is for anyone, including the business, to understand the big picture.
graph TD
customer["Customer<br/>(Person)"]
system["Online Banking System<br/>(Our system)"]
email["Email System<br/>(External)"]
core["Banking Core<br/>(External)"]
customer -->|"checks balance and<br/>makes transfers"| system
system -->|"sends notifications via"| email
system -->|"reads and updates accounts in"| coreThis Context diagram shows a single box for "Online Banking System," surrounded by a person (the Customer) and two external systems (Email and Banking Core). The labeled arrows describe what each relationship does, not how. Note that no technology or internal detail appears: at this level all that matters is the system's scope and its interactions with the outside world. It is the ideal diagram to start any conversation, including with non-technical people.
- Level 2: Container diagram
We zoom inside our system. We show the deployable units that make it up and how they communicate, now indicating the main technologies.
graph TD
customer["Customer<br/>(Person)"]
spa["SPA Web Application<br/>(React)"]
api["Banking API<br/>(Java, Spring Boot)"]
db["Database<br/>(PostgreSQL)"]
core["Banking Core<br/>(External)"]
customer -->|"uses, via HTTPS"| spa
spa -->|"calls, JSON/HTTPS"| api
api -->|"reads/writes, SQL"| db
api -->|"queries accounts, via API"| coreHere, inside the online banking system, we see three of our own containers: a single-page web application in React, an API in Java with Spring Boot, and a PostgreSQL database; plus the external system (Banking Core). Each arrow indicates the communication protocol (HTTPS, JSON, SQL). This level is the most useful in day-to-day technical work: it shows at a glance the deployment architecture, the chosen technologies, and the communication flows. Remember: "container" here means deployable unit, not necessarily a Docker container.
- Level 3: Component diagram
Now we zoom inside a single container—for example, the Banking API—to see how it is organized internally into components.
graph TD
spa["SPA Web Application"]
subgraph API["Banking API (Spring Boot)"]
ctrl["Transfers Controller<br/>(REST Controller)"]
service["Transfers Service<br/>(business logic)"]
repo["Accounts Repository<br/>(data access)"]
end
db["Database"]
spa -->|"POST /transfers"| ctrl
ctrl -->|"uses"| service
service -->|"uses"| repo
repo -->|"SQL"| dbThis diagram uses a subgraph to represent the boundaries of the Banking API and shows three internal components: the REST controller that receives requests, the service that contains the business logic, and the repository that accesses the data. It is the classic separation into layers (presentation, business, data) within a container. This level is valuable for helping developers understand the internal structure, but it should be drawn only for non-trivial containers, not for all of them.
- Level 4: Code
The last level details how a specific component is implemented, usually with a UML class diagram. In practice, it is almost never drawn by hand: the IDE or documentation tools generate it automatically, because it changes constantly and maintaining it manually is wasted work.
// The Code level corresponds to the actual classes that implement a component.
// For example, the "Transfers Service" component from the previous level:
public class TransfersService {
private final AccountsRepository repository;
public TransfersService(AccountsRepository repository) {
this.repository = repository; // dependency injection of the repository
}
public void transfer(String source, String destination, BigDecimal amount) {
Account sourceAccount = repository.find(source);
Account destinationAccount = repository.find(destination);
sourceAccount.withdraw(amount); // business logic
destinationAccount.deposit(amount);
repository.save(sourceAccount);
repository.save(destinationAccount);
}
}This code shows how the "Transfers Service" component materializes into concrete classes. The C4 recommendation is not to draw this level manually: if you need to see it, let your IDE generate the class diagram from the actual code. Keeping code diagrams by hand is the number one cause of outdated documentation.
- Best practices and tools
Summary of the notations and practices recommended by C4:
- Every diagram self-explanatory: with a title, a legend, and clearly labeled elements.
- Label the relationships: describe what each arrow does and, where applicable, with what technology/protocol.
- One level per diagram: do not mix context with code.
- Do not overuse levels 3 and 4: draw only what adds value.
- Documentation as code (diagrams as code): define the diagrams in text so they can be versioned alongside the code and avoid becoming outdated.
Common tools:
| Tool | Approach |
|---|---|
| Mermaid | Diagrams as text, embedded in Markdown (as in this lesson) |
| PlantUML (+ C4-PlantUML extension) | Diagrams as text, very powerful for C4 |
| Structurizr | The official C4 model tool, "single model, multiple views" |
| draw.io / Excalidraw | Quick manual drawing, without versioning as code |
Example of the same context diagram in PlantUML with the C4 extension:
@startuml !include <C4/C4_Context> Person(customer, "Customer", "Online banking user") System(banking, "Online Banking System", "Allows checking balances and transferring") System_Ext(email, "Email System", "Sends notifications") System_Ext(core, "Banking Core", "Manages accounts") Rel(customer, banking, "Checks balance and transfers") Rel(banking, email, "Sends notifications via") Rel(banking, core, "Reads and updates accounts in") @enduml
This PlantUML snippet uses the official C4_Context library. The Person, System, and System_Ext (external system) macros create the elements with their name and description, and Rel defines the labeled relationships. The advantage over Mermaid is that PlantUML with C4 knows the model's semantics (it distinguishes people from systems, internal from external) and applies a consistent style automatically. Being text, it is versioned alongside the code and regenerated effortlessly.
Common Mistakes and Tips
- The "spaghetti-plate diagram." A single diagram with everything mixed together communicates nothing. Separate by levels and audiences.
- Diagrams without a legend or labels. If the boxes and arrows are not explained, each reader interprets whatever they want. Label everything.
- Documentation that ages. Hand-drawn diagrams become outdated quickly. Use diagrams as code and generate the code level automatically.
- Excess detail. Do not draw levels 3 and 4 for everything. Most teams live happily with levels 1 and 2.
- Tip: always start with the Context. In any architecture conversation, start with the context diagram; align everyone before descending into detail.
Exercises
Exercise 1. Match each element with its C4 level: (a) "a Redis database"; (b) "the customer and the external payment system"; (c) "the OrdersService class"; (d) "the controller, the service, and the repository within the API."
Exercise 2. Draw (in Mermaid or PlantUML) a Context diagram for a library management system, with at least one person and one external system.
Exercise 3. Explain why C4 recommends not drawing the Code level manually and what alternative it proposes.
Solutions
Solution 1. (a) Container (level 2): a database is a deployable unit. (b) Context (level 1): people and external systems. (c) Code (level 4): a specific class. (d) Component (level 3): logical groupings within a container.
Solution 2. Example in Mermaid:
graph TD
member["Member<br/>(Person)"]
librarian["Librarian<br/>(Person)"]
system["Library Management System<br/>(Our system)"]
email["Email System<br/>(External)"]
member -->|"searches for and reserves books"| system
librarian -->|"manages loans and catalog"| system
system -->|"sends return reminders via"| emailThis Context diagram shows two people (Member and Librarian), our own system, and an external Email system, with labeled relationships describing what each interaction does, without going into technologies or the internal structure.
Solution 3. Because the code changes continuously, and maintaining a class diagram by hand quickly turns it into outdated and unreliable documentation, which creates more confusion than help. The alternative C4 proposes is to generate this level automatically from the code itself (with the IDE or documentation tools) when it is genuinely needed, instead of drawing it manually.
Conclusion
You have learned why and for whom an architecture is documented, the classic 4+1 view model and, in depth, the C4 model with its four zoom levels (Context, Container, Component, and Code), plus the practices and tools for keeping documentation alive through diagrams as code. With this you close Module 1, Fundamentals of Application Architecture: you now know what architecture is and its domains, who the architect is, how quality attributes are specified, how decisions and their trade-offs are made and documented, and how to communicate all of it clearly. On these foundations, the following modules will delve into the concrete architectural styles and patterns with which to bring these ideas to life.
Application Architecture Course
Module 1: Fundamentals of Application Architecture
- What Is Application Architecture?
- The Role of the Software Architect
- Quality Attributes and Non-Functional Requirements
- Architectural Decisions and Trade-offs
- Architecture Documentation: Views and the C4 Model
Module 2: Design Principles and Tactics
- Coupling, Cohesion and Separation of Concerns
- SOLID Principles Applied to Architecture
- DRY, KISS, YAGNI and Other Design Principles
- Architectural Tactics for Quality Attributes
- Managing Technical Debt
Module 3: Architectural Styles and Patterns
- Monolithic Architecture
- Layered Architecture (N-Tier)
- Client-Server Architecture
- Hexagonal Architecture (Ports and Adapters)
- Clean and Onion Architecture
Module 4: Distributed Architectures and Microservices
- Introduction to Distributed Systems
- Microservices Architecture
- Service Decomposition and Bounded Contexts
- API Gateway, Service Discovery and Inter-Service Communication
- Resilience Patterns: Circuit Breaker, Retry and Bulkhead
- The CAP Theorem and Data Consistency
Module 5: Event-Driven Architectures and Messaging
- Fundamentals of Event-Driven Architecture
- Asynchronous Messaging: Queues and Brokers
- Event Patterns: Event Sourcing and CQRS
- Managing Distributed Transactions: The Saga Pattern
- Real-Time Data Streaming
Module 6: Domain-Driven Design (DDD)
- Core DDD Concepts
- Strategic Design: Bounded Contexts and Ubiquitous Language
- Tactical Design: Entities, Aggregates and Repositories
- Context Mapping
Module 7: Data and Persistence
- Persistence Strategies: SQL vs NoSQL
- Data Access Patterns: Repository, Unit of Work and DAO
- Database per Service and Distributed Data Management
- Caching and Invalidation Strategies
Module 8: Cloud Architecture and Deployment
- Cloud Computing Fundamentals (IaaS, PaaS, SaaS)
- Containers and Orchestration with Docker and Kubernetes
- Serverless Architecture
- Cloud-Native Design Patterns
- Infrastructure as Code (IaC)
Module 9: Quality, Security and Observability
- Scalability: Horizontal vs Vertical and Load Balancing
- High Availability and Fault Tolerance
- Security by Design and Authentication/Authorization
- Observability: Logging, Metrics and Tracing
- Performance and Load Testing
