Architecture diagrams drawn by hand go stale the week after the review. Rename one service and you’re editing five boxes in three files, and one of them always gets missed.

Structurizr fixes that by separating the model from the views. You describe your system once in a text file, the Structurizr DSL, and every diagram is a view of that one model. Simon Brown, who created the C4 model, built it.

This guide walks through one example system, an online bookstore, and builds every C4 view for it. Every DSL example here passes structurizr validate (Structurizr 6.2.3), and every image was rendered from the code shown.

What You’ll Learn

  • How the Structurizr DSL separates the model from the views.
  • How to write system context, container, component, dynamic, and deployment views.
  • How to style diagrams and reuse one model across several views.
  • How to run Structurizr locally with Docker and export diagrams to Mermaid, PlantUML, or a static site.
  • How to validate a workspace in CI so broken diagrams fail the build.

Structurizr Basics

Primary Use Cases

  • Documenting a system at several zoom levels that stay consistent with each other.
  • Keeping architecture diagrams in version control, next to the code, where pull requests review them.
  • Publishing architecture docs and decision records from the same source.

Less Suitable Use Cases

  • One-off sketches for a meeting. A whiteboard is faster.
  • Diagrams that aren’t about software structure, such as org charts or marketing visuals.

When to Use Structurizr

Use Structurizr when you need more than one diagram of the same system. The first diagram costs about the same as any other tool. The payoff arrives with the second view and every rename after that.

How a Structurizr Workspace Works

A workspace file, workspace.dsl, has two blocks:

  • model defines the elements (people, software systems, containers, components) and the relationships between them.
  • views picks which elements each diagram shows.
flowchart TB A["workspace.dsl"] --> B["model
people, systems, containers,
components, relationships"] A --> C["views
which elements each diagram shows"] B --> D["System context view"] B --> E["Container view"] B --> F["Component view"] B --> G["Dynamic and deployment views"] C --> D C --> E C --> F C --> G

Structurizr Example 1: A System Context View

Start with the smallest useful workspace: one person, two systems, and the context view.

workspace "Bookstore" "An online bookstore used to demonstrate the Structurizr DSL." {

    model {
        customer = person "Customer" "Browses and buys books."
        payments = softwareSystem "Payment Provider" "Processes card payments."
        bookstore = softwareSystem "Bookstore" "Lets customers browse and buy books."

        customer -> bookstore "Browses and orders books" "HTTPS"
        bookstore -> payments "Charges cards" "HTTPS"
    }

    views {
        systemContext bookstore "Context" {
            include *
            autolayout tb
        }
    }
}

The pieces:

  • person and softwareSystem take a name, a description, and optional tags.
  • -> creates a relationship with a description and a technology.
  • include * shows the system and everything directly connected to it.
  • autolayout tb lays the diagram out top to bottom, so you never drag boxes by hand.

The full example below adds a second person and an email service. Its context view renders like this:

Structurizr system context diagram: Customer and Store Admin use the Bookstore, which calls the Payment Provider and Email Service

Structurizr Example 2: A Container View

Containers are the deployable pieces inside a system: apps, APIs, and databases. Nest them inside the software system, and turn on hierarchical identifiers so you can refer to them as bookstore.web and bookstore.api.

    !identifiers hierarchical

    model {
        bookstore = softwareSystem "Bookstore" "Lets customers browse and buy books." {
            web = container "Web App" "Serves the storefront." "TypeScript and React"
            api = container "API" "Handles the catalog and orders." "Go"
            db = container "Database" "Stores books, customers, and orders." "PostgreSQL" "Database"
        }

        customer -> bookstore.web "Browses and orders books" "HTTPS"
        bookstore.web -> bookstore.api "Calls" "JSON/HTTPS"
        bookstore.api -> bookstore.db "Reads and writes" "SQL"
    }

    views {
        container bookstore "Containers" {
            include *
            autolayout tb
        }
    }

The context view needs no changes. Structurizr implies customer -> bookstore from customer -> bookstore.web, so the system-level diagram stays correct as you add detail. The "Database" tag on the last container picks up a cylinder shape from the styles section later in this guide.

Structurizr container diagram: the Web App calls the API, which reads and writes the PostgreSQL database and calls the payment and email systems

Structurizr Example 3: A Component View

Components live inside a container. Add them to the API and draw relationships at the component level:

            api = container "API" "Handles the catalog and orders." "Go" {
                catalog = component "Catalog Handler" "Searches and lists books." "Go package"
                orders = component "Order Handler" "Places and tracks orders." "Go package"
                repo = component "Repository" "Reads and writes the database." "Go package"
            }

        bookstore.web -> bookstore.api.catalog "Searches books" "JSON/HTTPS"
        bookstore.web -> bookstore.api.orders "Places orders" "JSON/HTTPS"
        bookstore.api.catalog -> bookstore.api.repo "Reads books"
        bookstore.api.orders -> bookstore.api.repo "Saves orders"
        bookstore.api.repo -> bookstore.db "Reads and writes" "SQL"
        component bookstore.api "Components" {
            include *
            autolayout tb
        }

Once relationships exist at the component level, the container view derives its arrows from them. Define each relationship once, at the most detailed level you care about.

Structurizr component diagram of the API: Catalog Handler and Order Handler use the Repository, which reads and writes the database

Structurizr Example 4: A Dynamic View

Static views show what connects to what. A dynamic view shows one request flowing through the system, with numbered steps:

        dynamic bookstore "PlaceOrder" "A customer places an order." {
            customer -> bookstore.web "Submits the checkout form"
            bookstore.web -> bookstore.api "Sends POST /orders"
            bookstore.api -> bookstore.db "Saves the order"
            bookstore.api -> payments "Charges the card"
            bookstore.api -> email "Sends the confirmation"
            autolayout tb
        }

Each step must follow a relationship that already exists in the model, directly or implied. A typo in a step fails validation instead of drawing an arrow that the model doesn’t support.

Structurizr dynamic diagram showing five numbered steps of placing an order, from the checkout form through the API to the database, payments, and email

Structurizr Example 5: A Deployment View

Deployment views map containers onto infrastructure. Add a deployment environment to the model:

        production = deploymentEnvironment "Production" {
            deploymentNode "Amazon Web Services" {
                deploymentNode "us-east-1" {
                    deploymentNode "Amazon CloudFront and S3" {
                        containerInstance bookstore.web
                    }
                    deploymentNode "Amazon ECS on Fargate" {
                        containerInstance bookstore.api
                    }
                    deploymentNode "Amazon RDS for PostgreSQL" {
                        containerInstance bookstore.db
                    }
                }
            }
        }
        deployment bookstore production "Production" {
            include *
            autolayout tb
        }

Add a deploymentEnvironment block for staging and a second deployment view, and both environments share the same containers. That’s how you show where staging differs from production without drawing either one twice.

Structurizr deployment diagram: the Web App on CloudFront and S3, the API on ECS Fargate, and the database on RDS, inside AWS us-east-1

Style Structurizr Diagrams

Styles match on tags. Every element gets a default tag for its type (Person, Software System, Container, Component), and you can add your own, such as External and Database in this example:

        styles {
            element "Person" {
                shape person
                background #08427b
                color #ffffff
            }
            element "Software System" {
                background #1168bd
                color #ffffff
            }
            element "Container" {
                background #438dd5
                color #ffffff
            }
            element "Component" {
                background #85bbf0
                color #000000
            }
            element "Database" {
                shape cylinder
            }
            element "External" {
                background #999999
            }
        }

For a quick start, replace the whole styles block with one line inside views:

        theme default

Reuse One Model Across Views

Views pick elements from the model, and they can leave relationships out too. This workspace shows the same two elements twice, once for ordering and once for shipping, by excluding the relationship the other view needs:

workspace "Reuse Models" "One model, two views that show different relationships." {

    model {
        customer = person "Customer"
        website = softwareSystem "Website"

        orderPlaced = customer -> website "Places an order"
        shipmentSent = website -> customer "Emails shipping updates"
    }

    views {
        systemContext website "Ordering" {
            include customer website
            exclude shipmentSent
            autolayout tb
        }

        systemContext website "Shipping" {
            include customer website
            exclude orderPlaced
            autolayout tb
        }
    }
}

Naming a relationship (orderPlaced = ...) lets a view include or exclude it by identifier. The model stays single-sourced, and each audience sees only the relationships that matter to it.

Run Structurizr Locally With Docker

Structurizr local is the free, open source viewer and layout editor. It replaced Structurizr Lite, and it runs from the structurizr/structurizr Docker image. Put workspace.dsl in a folder and mount that folder:

mkdir -p ~/structurizr
docker pull structurizr/structurizr
docker run -it --rm -p 8080:8080 \
  -v ~/structurizr:/usr/local/structurizr \
  structurizr/structurizr local

Open http://localhost:8080. If the folder is empty, Structurizr creates a starter workspace.dsl. Edit the file, save it, and refresh the browser to see the change. Layout changes you make in the diagram editor save automatically.

To try the DSL without installing anything, paste a workspace into the Structurizr playground.

Validate and Export Structurizr Diagrams

The same image runs the command-line tools. Validate a workspace before you commit it:

docker run --rm -v "$PWD:/usr/local/structurizr" structurizr/structurizr \
  validate -workspace /usr/local/structurizr/workspace.dsl

validate prints nothing and exits 0 when the workspace is valid. On an error it exits 1 and names the line, which makes it a one-line CI check:

ERROR ... Unexpected tokens (expected: ... ->) at line 8 of /usr/local/structurizr/workspace.dsl

Export every view to another format with export:

# Mermaid, one .mmd file per view
docker run --rm -v "$PWD:/usr/local/structurizr" structurizr/structurizr \
  export -workspace /usr/local/structurizr/workspace.dsl -format mermaid -output /usr/local/structurizr/mermaid

# PlantUML, or plantuml/c4plantuml for C4-PlantUML
docker run --rm -v "$PWD:/usr/local/structurizr" structurizr/structurizr \
  export -workspace /usr/local/structurizr/workspace.dsl -format plantuml -output /usr/local/structurizr/plantuml

# A static HTML site you can host anywhere
docker run --rm -v "$PWD:/usr/local/structurizr" structurizr/structurizr \
  export -workspace /usr/local/structurizr/workspace.dsl -format static -output /usr/local/structurizr/site

The Docker image doesn’t export PNG or SVG. For images, open a view in Structurizr local and use its export button.

Learn Structurizr: Beyond the Basics