Introduction
The C4 model provides a simple way to think about and visualize software architecture. Created by Simon Brown, it breaks down the complexity of software systems into four hierarchical levels:
- System Context - The big picture view
-
Container - The major technical building
blocks
- Component - Internal structure of containers
- Code - Implementation details (not covered by c4r)
This vignette walks you through creating each type of diagram using c4r, from basic concepts to practical examples.
Understanding the C4 Hierarchy
When to Use Each Diagram Type
- System Context: Use when communicating with stakeholders, showing how your system fits into the broader environment
- Container: Use when planning deployment, showing major technical decisions and boundaries
- Component: Use when designing or documenting the internal structure of a specific container
Key Concepts
Before diving into examples, let’s understand the core building blocks:
- Person: A human user of your system
- System: Your software system (the thing you’re building)
- External System: Another system that your system interacts with
- Container: A deployable/runnable unit (web app, database, etc.)
- Component: A grouping of related functionality within a container
- Relationship: How elements interact with each other
System Context Diagrams
A System Context diagram shows your system as a black box, focusing on the people who use it and the other systems it interacts with.
Basic Example
Let’s start with a simple e-commerce system:
# Create the system context diagram
context_diagram <- c4_context(
title = "E-Commerce System - System Context",
# Define the people who interact with our system
person = list(
c4_person("customer", "Customer", "A person who buys products online"),
c4_person("admin", "Administrator", "Manages products and orders")
),
# Define our main system
system = list(
c4_system(
"ecommerce", "E-Commerce System",
"Allows customers to buy products and admins to manage inventory"
)
),
# Define external systems we depend on
external_system = list(
c4_external_system("payment", "Payment Gateway", "Processes credit card payments"),
c4_external_system("email", "Email Service", "Sends transactional emails")
),
# Define the relationships between elements
relationships = list(
c4_rel("customer", "ecommerce", "Browses products and places orders", "HTTPS"),
c4_rel("admin", "ecommerce", "Manages products and orders", "HTTPS"),
c4_rel("ecommerce", "payment", "Processes payments", "REST API"),
c4_rel("ecommerce", "email", "Sends order confirmations", "SMTP")
)
)
# Display the diagram
context_diagram
Container Diagrams
A Container diagram zooms into your system to show the major technical building blocks. Each container is something that can be deployed independently.
E-Commerce Container Example
container_diagram <- c4_container_diagram(
title = "E-Commerce System - Container View",
# Users of the system
person = list(
c4_person("customer", "Customer", "Shops for products online"),
c4_person("admin", "Administrator", "Manages the e-commerce platform")
),
# The containers that make up our system
container = list(
c4_container(
"web_app", "Web Application",
"Provides e-commerce functionality", "React SPA"
),
c4_container(
"api", "API Gateway",
"Handles all business logic and API requests", "Node.js/Express"
),
c4_container(
"database", "Database",
"Stores product, order, and user data", "PostgreSQL"
),
c4_container(
"cache", "Cache",
"Caches frequently accessed data", "Redis"
)
),
# External systems (same as context level)
external_system = list(
c4_external_system("payment", "Payment Gateway", "Stripe payment processing"),
c4_external_system("email", "Email Service", "SendGrid email delivery")
),
# Relationships between containers
relationships = list(
# User interactions
c4_rel("customer", "web_app", "Uses", "HTTPS"),
c4_rel("admin", "web_app", "Administers", "HTTPS"),
# Internal container relationships
c4_rel("web_app", "api", "Makes API calls", "JSON/HTTPS"),
c4_rel("api", "database", "Reads from and writes to", "SQL/TCP"),
c4_rel("api", "cache", "Reads from and writes to", "Redis Protocol"),
# External integrations
c4_rel("api", "payment", "Processes payments", "REST API/HTTPS"),
c4_rel("api", "email", "Sends emails", "REST API/HTTPS")
)
)
container_diagram
Component Diagrams
A Component diagram shows the internal structure of a single container, breaking it down into components and their interactions.
API Gateway Component Example
Let’s zoom into the API Gateway container:
component_diagram <- c4_component_diagram(
title = "API Gateway - Component View",
# Components within the API Gateway container
component = list(
c4_component(
"auth_controller", "Authentication Controller",
"Handles user authentication and authorization", "Express.js"
),
c4_component(
"product_controller", "Product Controller",
"Handles product-related requests", "Express.js"
),
c4_component(
"order_controller", "Order Controller",
"Handles order processing requests", "Express.js"
),
c4_component(
"product_service", "Product Service",
"Business logic for product management", "Node.js"
),
c4_component(
"order_service", "Order Service",
"Business logic for order processing", "Node.js"
),
c4_component(
"database_client", "Database Client",
"Handles database connections and queries", "Sequelize ORM"
),
c4_component(
"cache_client", "Cache Client",
"Manages cache operations", "Redis Client"
)
),
# External systems this container interacts with
external_system = list(
c4_external_system("database", "PostgreSQL Database", "Main data store"),
c4_external_system("cache", "Redis Cache", "Session and data cache"),
c4_external_system("payment", "Payment Gateway", "Stripe API"),
c4_external_system("email", "Email Service", "SendGrid API")
),
# Component relationships
relationships = list(
# Controller to service relationships
c4_rel("product_controller", "product_service", "Uses", "Method calls"),
c4_rel("order_controller", "order_service", "Uses", "Method calls"),
c4_rel("order_controller", "auth_controller", "Validates auth", "Method calls"),
# Service to data access relationships
c4_rel("product_service", "database_client", "Reads/writes product data", "SQL"),
c4_rel("order_service", "database_client", "Reads/writes order data", "SQL"),
c4_rel("product_service", "cache_client", "Caches product data", "Redis"),
# External integrations
c4_rel("database_client", "database", "Connects to", "SQL/TCP"),
c4_rel("cache_client", "cache", "Connects to", "Redis Protocol"),
c4_rel("order_service", "payment", "Processes payments", "REST API"),
c4_rel("order_service", "email", "Sends confirmations", "REST API")
)
)
component_diagram
Customizing Your Diagrams
Using Different Themes
c4r includes several built-in themes:
# Default theme (blue tones)
default_diagram <- c4_context(
title = "Default Theme",
person = list(c4_person("user", "User", "System user")),
system = list(c4_system("app", "Application", "Main system")),
relationships = list(c4_rel("user", "app", "Uses")),
theme = "default"
)
# Dark theme
dark_diagram <- c4_context(
title = "Dark Theme",
person = list(c4_person("user", "User", "System user")),
system = list(c4_system("app", "Application", "Main system")),
relationships = list(c4_rel("user", "app", "Uses")),
theme = "dark"
)
# Blue theme
blue_diagram <- c4_context(
title = "Blue Theme",
person = list(c4_person("user", "User", "System user")),
system = list(c4_system("app", "Application", "Main system")),
relationships = list(c4_rel("user", "app", "Uses")),
theme = "blue"
)
Best Practices
- Start High-Level: Begin with System Context, then drill down
- Keep It Simple: Don’t try to show everything in one diagram
- Use Consistent Naming: Keep element names consistent across diagram levels
- Focus on Communication: Choose the right level of detail for your audience
- Iterate: Diagrams evolve with your understanding and system changes
Integration with R Workflows
In R Markdown Documents
c4r diagrams work seamlessly in R Markdown:
``` r
my_diagram <- c4_context(
title = "My System Context",
# ... diagram definition
)
my_diagram
```
```{=html}
<div class="grViz html-widget html-fill-item" id="htmlwidget-febe03efa1a2d8d52a86" style="width:700px;height:432.632880098888px;"></div>
<script type="application/json" data-for="htmlwidget-febe03efa1a2d8d52a86">{"x":{"diagram":"digraph {\n graph [rankdir=TB, bgcolor=\"white\", fontname=\"Arial\"];\n node [fontname=\"Arial\", fontsize=10];\n edge [fontname=\"Arial\", fontsize=8];\n\n label=\"My System Context\";\n labelloc=t;\n fontsize=16;\n\n\n\n\n}","config":{"engine":"dot","options":null}},"evals":[],"jsHooks":[]}</script>
```
In Shiny Applications
library(shiny)
library(c4r)
ui <- fluidPage(
titlePanel("Architecture Diagrams"),
DiagrammeR::grVizOutput("diagram")
)
server <- function(input, output) {
output$diagram <- DiagrammeR::renderGrViz({
c4_context(
title = "Dynamic Architecture View",
# ... your diagram definition
)
})
}
shinyApp(ui = ui, server = server)
Advanced Usage
Next Steps
- Explore the function reference for detailed API documentation
- Check out the c4_demo() function for more examples
- Learn more about the C4 model at c4model.com
- Consider integrating c4r diagrams into your documentation workflow