C4 model diagrams in Mermaid for system architecture documentation
System architecture diagrams often suffer from the same problem: they're either too high-level to be useful, or so detailed that only the author understands them. The C4 model solves this by defining four levels of abstraction, each meant for a different audience and question. Context (the system and its users), Container (the major pieces), Component (the internals of a container), and Code (the classes and functions) — and Mermaid supports all four natively.
The C4 model hierarchy
The C4 model is a way of thinking about architecture, not a strict notation. Each level answers a different question:
- Context (C1): "What is the system, and who uses it?" Audience: non-technical stakeholders, new team members.
- Container (C2): "What are the major building blocks?" Audience: architects, senior engineers.
- Component (C3): "What are the parts inside a container?" Audience: engineers working on that container.
- Code (C4): "What classes and functions make up a component?" Audience: the engineer writing the code (usually not a diagram at all, but the code itself).
Each level zooms in and adds more detail. Teams typically maintain C1, C2, and C3; C4 is usually the code itself, not a diagram.
C1: System context diagram
A context diagram shows your system as a single box, surrounded by external systems, users, and actors. It's the 30,000-foot view.
graph TB
User["👤 User"]
Admin["👤 Admin"]
System["🖥️ E-Commerce Platform"]
PaymentGateway["💳 Payment Gateway<br/>(Stripe)"]
EmailService["✉️ Email Service<br/>(SendGrid)"]
InventorySystem["📦 Inventory System<br/>(Legacy)"]
User -->|Browse & buy| System
Admin -->|Manage inventory & orders| System
System -->|Process payments| PaymentGateway
System -->|Send order confirmations| EmailService
System -->|Sync stock levels| InventorySystem
The context diagram is your highest-level abstraction. Use it in architecture docs, README files, and presentations to newcomers. It answers: "What does this system do, and what does it talk to?"
C2: Container diagram
Now zoom in. Each major service, database, or infrastructure piece gets its own box. A container is something that can be deployed or scaled independently.
graph TB
User["👤 User"]
Browser["🌐 Web Browser"]
API["🔌 API Server<br/>(Node.js)"]
Frontend["⚛️ Frontend<br/>(React)"]
Auth["🔐 Auth Service<br/>(Node.js)"]
Cache["⚡ Redis Cache"]
OrderDB["🗄️ Orders Database<br/>(PostgreSQL)"]
UserDB["🗄️ Users Database<br/>(PostgreSQL)"]
Queue["📬 Message Queue<br/>(RabbitMQ)"]
Payment["💳 Payment Service<br/>(Microservice)"]
User -->|Uses| Browser
Browser -->|HTTP/REST| Frontend
Browser -->|GraphQL| API
API -->|Calls| Auth
API -->|Reads/writes| OrderDB
API -->|Caches with| Cache
API -->|Publishes to| Queue
Queue -->|Consumes| Payment
Auth -->|Reads| UserDB
Payment -->|Syncs orders to| OrderDB
The container diagram shows deployment boundaries. Each box is usually a separate service, database, or infrastructure component. Use this to discuss scaling decisions, team ownership, and deployment architecture.
C3: Component diagram
Pick a container and zoom in further. A component diagram shows the major pieces inside a container — classes, modules, or logical groupings.
For the API Server container above, a component diagram might look like:
graph TB
Request["📨 HTTP Request"]
Auth["🔐 Auth Middleware"]
Router["📍 Router"]
OrderService["📦 Order Service"]
PaymentService["💳 Payment Service"]
UserService["👤 User Service"]
OrderDB["🗄️ Database<br/>Orders"]
UserDB["🗄️ Database<br/>Users"]
Cache["⚡ Cache"]
Response["✅ Response"]
Request -->|Authenticate| Auth
Auth -->|Route| Router
Router -->|GET /orders| OrderService
Router -->|POST /payments| PaymentService
Router -->|GET /users| UserService
OrderService -->|Query| OrderDB
OrderService -->|Cache check| Cache
UserService -->|Query| UserDB
PaymentService -->|Verify user| UserService
OrderService -->|Response| Response
PaymentService -->|Response| Response
UserService -->|Response| Response
A component diagram is most useful when you're onboarding a new engineer to a specific service, or when you're redesigning a large subsystem.
From hierarchy to practice
Most teams document three levels:
- C1 in the main README — everyone needs to understand what the system is
- C2 in architecture docs — engineers and architects use this to understand deployment and team boundaries
- C3 in service-specific docs — linked from the service's own README for engineers working on that service
Use the MermaidCreator editor to draft each diagram. Store the source in your repository alongside the docs, so it travels with the code and stays in sync when you refactor.
C4 model best practices
C1: Keep it simple. External systems should be labeled by role or name, not implementation. "Payment Gateway" beats "Stripe API endpoint," because if you switch providers, the context diagram doesn't change.
C2: Align with team structure. Each container should map roughly to a team or service owner. If the diagram shows 15 containers but you have 3 teams, the diagram is probably too detailed for C2 — some of those should be internal components, not separate containers.
C3: One service at a time. Don't try to show all three layers in one giant diagram. Pick the service that's hardest to understand and document its internals; leave the others at C2.
Avoid the "one diagram to rule them all" trap. If a single diagram needs more than 10–12 boxes, split it. A human can scan and understand about a dozen elements; beyond that, the diagram becomes a maze.
Example: A real microservice architecture
C1: System context
graph TB
User["👤 User"]
System["🖥️ SaaS Product"]
StripeAPI["💳 Stripe"]
SlackAPI["💬 Slack"]
User -->|Interact with| System
System -->|Charge| StripeAPI
System -->|Notify| SlackAPI
C2: Containers
graph TB
Web["⚛️ Web App<br/>(Next.js)"]
API["🔌 API<br/>(Node.js)"]
Auth["🔐 Auth<br/>(Auth0)"]
Analytics["📊 Analytics<br/>(TimescaleDB)"]
UserDB["🗄️ Users<br/>(PostgreSQL)"]
Jobs["⚙️ Jobs<br/>(Node.js worker)"]
StripeAPI["💳 Stripe API"]
Web -->|HTTP| API
API -->|Verify| Auth
API -->|Read/Write| UserDB
API -->|Log events| Analytics
API -->|Trigger| Jobs
Jobs -->|Charge| StripeAPI
C3: API components
graph TB
Request["📨 Request"]
AuthMW["🔐 Auth Middleware"]
Router["📍 Router"]
UserController["👤 User Controller"]
SubscriptionController["💳 Subscription Controller"]
UserRepo["🗄️ User Repo"]
SubRepo["🗄️ Subscription Repo"]
Cache["⚡ Cache"]
DB["🗄️ PostgreSQL"]
Request -->|Check token| AuthMW
AuthMW -->|Route| Router
Router -->|/users| UserController
Router -->|/subscriptions| SubscriptionController
UserController -->|Query| UserRepo
SubscriptionController -->|Query| SubRepo
UserRepo -->|Cache| Cache
SubRepo -->|Cache| Cache
Cache -->|Miss| DB
C4 in the Mermaid ecosystem
Mermaid doesn't have a special "C4" notation, but you can build C4 diagrams using:
- Graph/flowchart: The most flexible; use node shapes to distinguish systems (rectangles), databases (cylinders), users (circles)
- Sequence diagrams: When the C4 diagram focuses on message flow between containers
- Class diagrams: For C3 diagrams that show classes and relationships
The key is consistency. If a container is always a rectangle with a database icon, readers will learn that convention instantly.
When to use C4 diagrams
Use C4 when:
- You're onboarding new engineers to the system
- You're proposing an architecture change and need to show before/after
- You need to explain the system to non-engineers (C1) or architects (C2)
- You're documenting a microservice (C3) for the team that owns it
Don't use C4 if:
- The system is simple enough to fit in a single sentence (e.g., "a CRUD API on top of a database")
- You're in the early prototype phase and the architecture changes weekly
- You need real-time status (C4 diagrams are static; use dashboards for that)
FAQ
Q: Should we maintain all four levels? A: Most teams maintain C1, C2, and C3. C4 is usually the code itself, not a diagram.
Q: How often should we update C4 diagrams? A: Whenever the architecture changes. Keep the source in the repository, near the code it describes, so it's easy to update in the same PR as the refactor.
Q: Can I use C4 for a legacy system I'm trying to understand? A: Absolutely. Reverse-engineer a C1 and C2 diagram by reading deployment docs and the code, then use them to onboard the next person. You'll spot inconsistencies and outdated pieces as you build the diagrams.
Sketch a C1 diagram of your current system in the MermaidCreator editor — it's the fastest way to check that your mental model of the architecture matches everyone else's.
Related posts
Drawing system architecture diagrams with Mermaid
Use Mermaid to map cloud infrastructure, microservices, and data flows. Show components, connections, and scaling without leaving your codebase.
Mermaid Sankey diagrams: visualize data flow and dependencies
Sankey diagrams show how data, energy, or resources flow through systems — perfect for dependency mapping, cost allocation, and process tracing.