Structurizr Authoring Workflows¶
Step-by-step procedures for creating and maintaining C4 models.
Workflow Overview¶
flowchart TD
subgraph Create
C1[Gather Requirements]
C2[Define System Context]
C3[Define Containers]
C4[Define Components]
C5[Create Views]
C6[Apply Styles]
end
subgraph Update
U1[Identify Change]
U2[Update Model]
U3[Update Views]
U4[Validate]
end
subgraph Review
R1[Completeness Check]
R2[Accuracy Check]
R3[Style Check]
end
C1 --> C2 --> C3 --> C4 --> C5 --> C6
U1 --> U2 --> U3 --> U4
Workflow 1: Create New Workspace¶
Step 1: Gather Requirements¶
Before modeling, understand:
questions:
- What is the system name and purpose?
- Who are the users (people/roles)?
- What external systems does it interact with?
- What are the major technical components?
- What technologies are used?
Step 2: Create Workspace Structure¶
workspace "{SystemName}" "{Brief description}" {
!identifiers hierarchical
model {
# Elements will go here
}
views {
# Diagrams will go here
}
}
Step 3: Define People and External Systems¶
Identify actors first:
model {
# People who use the system
customer = person "Customer" "Places orders and tracks deliveries"
admin = person "Admin" "Manages products and orders"
# External systems
paymentProvider = softwareSystem "Payment Gateway" "Processes payments" "External"
emailService = softwareSystem "Email Service" "Sends notifications" "External"
}
Step 4: Define the Main System¶
Create the software system boundary:
model {
# ... people and external systems ...
system = softwareSystem "{SystemName}" "{Description}" {
# Containers will go here
}
}
Step 5: Add Containers¶
Break down into deployable units:
system = softwareSystem "E-Commerce Platform" "Online shopping" {
# User-facing
webApp = container "Web Application" "Customer storefront" "React" "webapp"
mobileApp = container "Mobile App" "iOS/Android app" "React Native" "mobile"
# Backend
api = container "API" "Backend services" "Node.js/Express"
# Data
database = container "Database" "Product and order data" "PostgreSQL" "database"
cache = container "Cache" "Session and product cache" "Redis" "cache"
# Async
queue = container "Message Queue" "Order processing" "RabbitMQ" "queue"
worker = container "Worker" "Background jobs" "Node.js"
}
Step 6: Add Relationships¶
Connect elements with meaningful descriptions:
model {
# People to system
customer -> webApp "Browses and orders via" "HTTPS"
customer -> mobileApp "Browses and orders via" "HTTPS"
admin -> webApp "Manages via" "HTTPS"
# Internal
webApp -> api "Makes API calls to" "HTTPS/JSON"
mobileApp -> api "Makes API calls to" "HTTPS/JSON"
api -> database "Reads/writes" "SQL/TCP"
api -> cache "Caches data in" "Redis Protocol"
api -> queue "Publishes orders to" "AMQP"
worker -> queue "Consumes from" "AMQP"
worker -> database "Updates" "SQL/TCP"
# External
api -> paymentProvider "Processes payments via" "HTTPS/REST"
worker -> emailService "Sends emails via" "SMTP"
}
Step 7: Create Views¶
Define diagrams for each level:
views {
# Level 1: System Context
systemContext system "SystemContext" {
include *
autoLayout
description "System context showing users and external systems"
}
# Level 2: Containers
container system "Containers" {
include *
autoLayout
description "Container diagram showing technical building blocks"
}
# Default styles
styles {
element "Software System" {
background #1168bd
color #ffffff
}
element "Container" {
background #438dd5
color #ffffff
}
element "Person" {
shape Person
background #08427b
color #ffffff
}
element "External" {
background #999999
}
element "database" {
shape Cylinder
}
element "queue" {
shape Pipe
}
element "webapp" {
shape WebBrowser
}
element "mobile" {
shape MobileDeviceLandscape
}
}
}
Step 8: Validate and Refine¶
# Validate syntax
structurizr-cli validate -workspace workspace.dsl
# Preview locally
docker run -it --rm -p 8080:8080 \
-v $(pwd):/usr/local/structurizr \
structurizr/lite
Check: - [ ] All elements have descriptions - [ ] All relationships have descriptions - [ ] No orphan elements (unconnected) - [ ] Views show all relevant elements - [ ] Styles applied consistently
Workflow 2: Add Components¶
When you need to detail a container's internals.
Step 1: Identify Container¶
Determine which container needs component breakdown:
criteria:
- Complex enough to warrant decomposition
- Multiple distinct responsibilities
- Team needs to understand internals
Step 2: List Components¶
Identify logical components:
api_components:
- name: "AuthController"
responsibility: "Handle authentication"
technology: "Express Router"
- name: "OrderController"
responsibility: "Handle order operations"
technology: "Express Router"
- name: "OrderService"
responsibility: "Order business logic"
technology: "TypeScript"
- name: "OrderRepository"
responsibility: "Order data access"
technology: "Prisma"
Step 3: Add Component Block¶
api = container "API" "Backend services" "Node.js/Express" {
# Controllers (presentation)
authController = component "Auth Controller" "Authentication endpoints" "Express"
orderController = component "Order Controller" "Order endpoints" "Express"
productController = component "Product Controller" "Product endpoints" "Express"
# Services (business logic)
authService = component "Auth Service" "Authentication logic" "TypeScript"
orderService = component "Order Service" "Order processing" "TypeScript"
productService = component "Product Service" "Product management" "TypeScript"
# Repositories (data access)
userRepository = component "User Repository" "User data access" "Prisma"
orderRepository = component "Order Repository" "Order data access" "Prisma"
productRepository = component "Product Repository" "Product data access" "Prisma"
}
Step 4: Add Component Relationships¶
# Controller -> Service
authController -> authService "Uses"
orderController -> orderService "Uses"
productController -> productService "Uses"
# Service -> Repository
authService -> userRepository "Uses"
orderService -> orderRepository "Uses"
orderService -> productService "Validates products with"
productService -> productRepository "Uses"
# Repository -> Database
userRepository -> database "Queries"
orderRepository -> database "Queries"
productRepository -> database "Queries"
Step 5: Create Component View¶
views {
component api "API-Components" {
include *
autoLayout
description "Component diagram for the API container"
}
}
Workflow 3: Update Existing Model¶
When the system changes.
Step 1: Identify the Change¶
change_types:
- new_container: "Adding a new service"
- new_relationship: "New integration"
- technology_change: "Migrating database"
- remove_element: "Deprecating service"
- rename: "Rebranding component"
Step 2: Locate Affected Elements¶
Find in workspace.dsl: - Element definitions - Relationships involving the element - Views that include the element
Step 3: Make Changes¶
Adding New Container¶
# Add to model
notificationService = container "Notification Service" "Push notifications" "Go"
# Add relationships
api -> notificationService "Sends notifications via" "gRPC"
notificationService -> database "Reads user preferences" "SQL"
# Update views (if not using include *)
container system "Containers" {
include * # Automatically includes new container
}
Removing Container¶
# 1. Remove all relationships involving the container
# 2. Remove the container definition
# 3. Check views for explicit includes
Changing Technology¶
# Before
database = container "Database" "Data storage" "MySQL" "database"
# After
database = container "Database" "Data storage" "PostgreSQL" "database"
# Update relationship descriptions if protocol changed
api -> database "Reads/writes" "SQL/TCP" # May need update
Step 4: Validate¶
Check: - [ ] No broken references - [ ] Relationships still make sense - [ ] Views include new elements - [ ] Descriptions are accurate
Workflow 4: Add Dynamic View¶
Show runtime behavior/sequences.
Step 1: Identify the Flow¶
flow:
name: "User Login"
actors: [user, webApp, api, database]
trigger: "User submits credentials"
outcome: "User receives JWT token"
Step 2: List Steps¶
steps:
1: User -> webApp: "Enters credentials"
2: webApp -> api: "POST /auth/login"
3: api -> database: "Validate credentials"
4: database -> api: "User record"
5: api -> api: "Generate JWT"
6: api -> webApp: "Return token"
7: webApp -> user: "Redirect to dashboard"
Step 3: Create Dynamic View¶
views {
dynamic system "UserLogin" "User authentication flow" {
user -> webApp "1. Enters email and password"
webApp -> api "2. POST /auth/login"
api -> database "3. SELECT user WHERE email = ?"
database -> api "4. User record or null"
api -> webApp "5. JWT token or error"
webApp -> user "6. Redirect to dashboard or show error"
autoLayout
}
}
Step 4: Refine¶
- Use numbered steps for clarity
- Keep descriptions concise
- Show error paths in separate dynamic view if complex
Workflow 5: Add Deployment View¶
Show infrastructure mapping.
Step 1: Identify Environment¶
Step 2: Map Infrastructure¶
production:
cloud: AWS
nodes:
- name: "ECS Cluster"
containers: [api, worker]
- name: "RDS"
containers: [database]
- name: "ElastiCache"
containers: [cache]
- name: "CloudFront"
containers: [webApp]
Step 3: Create Deployment Model¶
model {
# ... existing model ...
production = deploymentEnvironment "Production" {
deploymentNode "AWS" "Amazon Web Services" "Cloud" {
deploymentNode "us-east-1" "N. Virginia" "Region" {
deploymentNode "ECS Cluster" "Container orchestration" "AWS ECS" {
containerInstance api
containerInstance worker
}
deploymentNode "RDS" "Managed database" "AWS RDS" {
containerInstance database
}
deploymentNode "ElastiCache" "Managed cache" "AWS ElastiCache" {
containerInstance cache
}
}
deploymentNode "CloudFront" "CDN" "AWS CloudFront" {
containerInstance webApp
}
}
}
}
Step 4: Create Deployment View¶
views {
deployment system production "Production" {
include *
autoLayout
description "Production deployment on AWS"
}
}
Workflow 6: Review Model¶
Ensure model quality and accuracy.
Step 1: Completeness Check¶
checklist:
elements:
- All elements have names: true
- All elements have descriptions: true
- No orphan elements: true
- Technologies specified: true
relationships:
- All have descriptions: true
- Technologies/protocols specified: true
- Direction is correct: true
views:
- System Context exists: true
- Container view exists: true
- All elements visible: true
Step 2: Accuracy Check¶
Compare model to reality:
verify:
- Does the model match current architecture?
- Are deprecated elements removed?
- Are new services included?
- Are relationships still valid?
Step 3: Style Check¶
style_review:
- Consistent naming conventions
- Appropriate shapes for element types
- Colors distinguish element types
- Layout is readable
Step 4: Document Gaps¶
gaps:
- "Payment Service added but not in model"
- "Legacy API deprecated but still shown"
- "Missing deployment view for staging"
Quick Reference: Common Tasks¶
| Task | Key Steps |
|---|---|
| Add person | Define in model, add relationships, check views |
| Add container | Define in system, add relationships, views update |
| Add component | Define in container block, add internal relationships |
| Add relationship | Source -> target with description and technology |
| Add view | Define in views block, include elements, autoLayout |
| Change technology | Update container/component definition, check descriptions |
| Remove element | Remove relationships first, then definition, check views |