C4 Component Diagram: Complete Guide with Examples
The component diagram is level 3 of the C4 model -- and it's the level with the worst reputation. Level 1 (System Context) is easy to draw and rarely changes. Level 2 (Container) maps cleanly to what you deploy. But level 3? It zooms into the internals of a single container, which means it changes every time a developer restructures a package. Most teams either skip it entirely or draw it once and let it rot.
That's a shame, because a well-maintained component diagram is the single most useful artifact for a developer joining a codebase. It answers the question every newcomer asks: "where does the logic for X actually live?"
This guide covers what a C4 component diagram is, how it differs from a UML component diagram, when level 3 is worth the maintenance cost (and when it isn't), a complete worked example, the mistakes that make component diagrams useless, and how to keep them accurate without doing it all by hand.
If you're new to the C4 model itself, start with our complete guide to the C4 model, then come back here.
What Is a C4 Component Diagram?
A component diagram opens up one container -- one deployable unit from your container diagram -- and reveals the components inside it.
In C4 terms, a component is a cohesive grouping of related functionality sitting behind a well-defined interface. Think of the major building blocks inside an application:
- An HTTP controller or handler group
- A domain service that owns a slice of business logic
- A repository or data access layer
- A client wrapping an external API
- A middleware, interceptor, or background worker
The key word is grouping. A component is an abstraction over code, not a single class or file. The OrderService component might span a dozen files, but conceptually it's one thing: the part of the container responsible for order business logic. As the glossary definition puts it, a C4 component is "a higher-level grouping of code with a single responsibility" -- not a 1:1 mapping to a programming-language class.
What the Component Diagram Answers
- How is this container structured internally?
- Which component owns which responsibility?
- How do components collaborate to handle a request?
- Where do calls to databases and external systems originate?
What It Deliberately Leaves Out
- Individual classes, interfaces, and functions (that's level 4, the code diagram)
- Other containers' internals
- Deployment and infrastructure details
One container per diagram, components only. If you find yourself drawing classes, you've zoomed too far.
C4 Component Diagram vs. UML Component Diagram
This trips people up constantly, because UML also has something called a "component diagram" -- and it's not the same thing.
A UML component diagram models software components as units with provided and required interfaces (the famous lollipop-and-socket notation), often emphasizing physical packaging, artifacts, and deployment relationships. It comes with formal notation rules and a precise metamodel.
A C4 component diagram is looser and more pragmatic. It's simply level 3 of a zoom hierarchy: boxes for components, arrows for relationships, a short text description on each. There's no special notation to learn. The value comes from the hierarchy -- every component box lives inside a specific container, which lives inside a specific system -- not from the notation itself.
In practice: if someone asks for a "component diagram" in a C4 context, they want a structural map of one container's internals that a new developer can read in two minutes. If they want lollipop interfaces and <<artifact>> stereotypes, they're asking for UML.
When Is Level 3 Worth Maintaining?
Here's the honest answer most guides skip: level 3 is the level with the worst effort-to-stability ratio in the entire C4 model.
Context diagrams change a few times a year. Container diagrams change when you add or remove a service -- maybe monthly. Component diagrams change every time someone refactors a package, extracts a service, or renames a module. If you maintain them by hand, you're signing up for constant gardening, and the moment you stop, the diagram starts lying.
So be deliberate about where you spend that effort.
Create a component diagram when:
- The container is genuinely complex. A backend API with 15 packages, multiple layers, and non-obvious boundaries deserves a map. A three-endpoint CRUD service does not.
- The structure embodies a deliberate design. If you use hexagonal architecture, CQRS, or a clean layering scheme, the component diagram makes the intended structure explicit -- and gives you something to point at when a pull request violates it.
- Multiple teams touch the same container. Shared code needs a shared mental model.
- Onboarding is a bottleneck. If new developers take weeks to find their way around a container, a component diagram pays for itself with the first hire.
Skip it when:
- The container is small and its folder structure is self-explanatory.
- The container is third-party (you don't draw the internals of Redis).
- Nobody will commit to keeping it updated. A stale component diagram is worse than none -- it confidently sends developers to code that no longer exists.
This is exactly why level 3 is the level most teams either skip or automate. Generating components from the actual code structure -- and verifying them against it -- removes the maintenance tax that kills hand-drawn diagrams. More on that below.
A Worked Example: Inside an API Container
Let's make this concrete. Take the e-commerce platform from our C4 model guide and zoom into a single container: the Order API (Go). At level 2, it's one box. At level 3, it opens up into components.
The Components
| Component | Responsibility |
|---|---|
| Auth Middleware | Validates JWT tokens on incoming requests, injects user identity into the request context |
| Order Controller | REST endpoints for creating, reading, and cancelling orders; request validation and response serialization |
| Admin Controller | REST endpoints for back-office order management (refunds, manual status changes) |
| Order Service | Core business logic: order lifecycle, pricing rules, stock checks, payment orchestration |
| Order Repository | Data access layer; persists and queries orders via SQL |
| Payment Client | Wraps the Stripe API; handles authorization, capture, and refunds |
| Email Adapter | Sends transactional emails (order confirmation, shipping updates) via SendGrid |
| Event Publisher | Publishes domain events (OrderPlaced, OrderCancelled) to Kafka |
The Relationships
[Auth Middleware] --> [Order Controller] : Passes authenticated requests
[Auth Middleware] --> [Admin Controller] : Passes authenticated requests (admin role)
[Order Controller] --> [Order Service] : Delegates business operations
[Admin Controller] --> [Order Service] : Delegates back-office operations
[Order Service] --> [Order Repository] : Reads/writes orders
[Order Service] --> [Payment Client] : Authorizes and captures payments
[Order Service] --> [Email Adapter] : Triggers transactional emails
[Order Service] --> [Event Publisher] : Emits domain events
[Order Repository] --> [Order Database (PostgreSQL)] : SQL
[Payment Client] --> [Payment Gateway (Stripe)] : HTTPS/REST
[Email Adapter] --> [Email Service (SendGrid)] : HTTPS/REST
[Event Publisher] --> [Message Queue (Kafka)] : Publishes events
Note that the database, Stripe, SendGrid, and Kafka appear at the edges. They aren't components of this container -- they're neighboring containers and external systems -- but showing where the outbound calls originate is exactly what makes the diagram useful.
What This Diagram Tells a New Developer
In thirty seconds, a developer joining this team learns:
- The request path: middleware → controller → service → repository. There's exactly one place business logic lives, and it isn't the controller.
- The boundaries: all external calls go through dedicated adapters (
Payment Client,Email Adapter). If you need to talk to Stripe, you extend the client; you don't import the SDK in a controller. - The side effects: orders produce events on Kafka and emails via SendGrid. If an email isn't sent, you know which two components to check.
- Where to add code: a new "gift card" feature clearly needs changes in the controller, the service, and possibly a new client -- and the diagram shows the pattern to follow.
That last point is underrated. A component diagram doesn't just describe structure -- it prescribes it. It tells contributors what "consistent with this codebase" looks like.
Common Mistakes with Component Diagrams
Mapping One Class to One Component
The most frequent mistake. If your container has 80 classes and your component diagram has 80 boxes, you've drawn a class diagram with extra steps. Components are groupings: OrderController (one component) might cover five handler classes. Aim for 5-15 components per container. If you're past 20, your abstractions are too fine-grained -- or your container is doing too much.
Documenting Every Container at Level 3
Symmetry is tempting: "we have 12 containers, so we need 12 component diagrams." Resist it. Most of those diagrams will never be read and never be updated. Document the two or three containers where complexity actually hurts, and let folder structure speak for the rest.
Letting the Diagram Rot
Level 3 drifts faster than any other level. A component diagram drawn in January describes a container that probably doesn't exist by June. Every refactor, every extracted package, every renamed module widens the gap. And a confidently wrong diagram is worse than no diagram: it sends developers hunting for components that were deleted two quarters ago. If you can't automate the upkeep, at least put "update the component diagram" in the pull request checklist for that container.
Drawing Components Without Responsibilities
A box labeled OrderManager with no description is noise. Every component should carry a one-line responsibility statement ("Validates JWT tokens and injects user identity"). If you can't write that sentence, the component boundary is probably wrong.
Showing Implementation Detail Instead of Structure
Generics, design patterns, helper utilities -- none of that belongs at level 3. If the interesting part of a component is how it's implemented, that's a job for the code diagram (or, more often, for the code itself).
How Archyl Keeps Component Diagrams Accurate
Everything above points to the same conclusion: component diagrams are valuable, but hand-maintaining them is a losing game. This is precisely the problem Archyl was built to solve.
AI Discovery Extracts Components from Code
Instead of drawing components by hand, you connect your repository and run AI discovery. Archyl analyzes your code structure -- packages, modules, layers, naming conventions -- and proposes a C4 model, including the components inside each container and the relationships between them. A Go service with handlers/, service/, and repository/ packages comes back as exactly the kind of component diagram shown in the example above. You review the suggestions, accept or adjust them, and you have a level 3 model in minutes.
Drift Detection Flags Divergence
This is the part that keeps level 3 alive. Because Archyl links components to actual files and directories in your repository, it can continuously check whether the documented components still exist in the code. When someone deletes a package, renames a module, or restructures a layer, the drift score drops and the affected components are flagged. Your component diagram stops being a snapshot and becomes a monitored contract.
Architecture as Code
If you prefer explicit definitions over discovery, Archyl supports defining your C4 model -- systems, containers, components, and relationships -- in YAML that lives in your repository. Your component diagram is versioned, reviewed in pull requests, and rendered automatically. Combined with drift detection, this gives you level 3 documentation with the maintenance characteristics of code.
FAQ
What is the difference between a C4 component diagram and a UML component diagram?
A UML component diagram is a formal notation with provided/required interfaces, artifacts, and stereotypes, focused on modeling components as packaged units. A C4 component diagram is level 3 of the C4 zoom hierarchy: a pragmatic boxes-and-arrows view of the components inside one specific container. C4 has no formal notation requirements -- its value comes from the consistent hierarchy (system → container → component → code), not the symbols.
Is a component the same as a class?
No. A C4 component is a cohesive grouping of code behind a well-defined interface -- it may be implemented as one class, a dozen classes, a package, or a module. If your component diagram has one box per class, you've gone one zoom level too deep.
Does every container need a component diagram?
No, and trying to document every container at level 3 is one of the fastest ways to abandon C4. Create component diagrams only for containers that are complex, shared across teams, or central to onboarding. For the rest, the container diagram plus a readable folder structure is enough.
How many components should a component diagram show?
Roughly 5 to 15. Fewer than 5 and the diagram probably isn't telling you anything the container diagram didn't. More than 20 and either your component boundaries are too fine-grained or the container itself should be split.
Want component diagrams that stay accurate? Try Archyl free and generate a C4 model -- components included -- from your codebase in minutes. Or keep reading: What is the C4 Model? A Complete Guide | C4 Container Diagram Guide | C4 Code Diagram Guide | Component Diagram in the glossary.