Simon Brown’s C4 Model for architecting your software solutions provides a clean and structured way of modeling software. Structurizr is not a new tool, but with some new "Solution Architecture" responsibilities flowing my way I was looking for a way to create maintainable models. I found Structurizr extremely useful for creating easy to maintain models to use and change(!) during discussions with architects and developers alike.

A brief introduction to C4

Simon Brown proposed a way of describing Software Systems in 4 layers. He often draws a parallel to "maps". Maps are created on different levels of detail, highlighting different aspects of the world. When I want to know where the Netherlands are in the world, a map of the world, or a map of Europe is very helpful, but to find the central station of Utrecht these maps are not useful at all. I would like to zoom in on Utrecht and add detail to actually find my way. Likewise, in the C4 model there is an emphasis on creating useful models of the application for each of the layers/levels of detail, and Simon Brown also puts great emphasis on your models being self-explanatory. To quote George Box: “All models are wrong, some are useful”.

The 4 layers of the C4 Architecture model act as a map which you can zoom in, where every level also adds a level of detail.

C1: System Context

On this level the System is modeled as a single box. Around the box are all the other entities your system interacts with.

C2: Container

When you zoom in on the box representing the System, the container diagram shows the different "deployable" containers which make up the System.

C3: Component

When you zoom in on the box representing a Container, the component diagram shows which components make up the Container.

C4: Code

When you zoom in on the box representing a Component, the Code diagram shows UML (or other Code modeling diagrams) which describe the actual software.

There is no obligation to use all the 4 levels. Especially the Code level is only used when modeling the Software on that level provides value.

What is Structurizr

Structurizr is a tool which can create diagrams for the 4 levels of C4 based purely on a DSL (Domain Specific Language).

As a developer I found structuring my thoughts through a DSL much easier than fiddling around with graphical tools like draw.io, but I found that with little extra effort I could generate helpful diagrams to use in my conversations with developers as well as less technical colleagues.

How to run

Structurizr offers a Lite version which can be run locally. It’s available as a docker image, and by mounting a volume containing your files everything will work out of the box. Saving your DSL files and refreshing the page provide instant feedback on the changes you made.

The command to run Structurizr is docker run -it --rm -p 8080:8080 -v <LOCAL_DIR>:/usr/local/structurizr structurizr/lite

You can change the port on the localhost using normal docker run parameters, and I’ve created a simple shell script which uses pwd to mount the working directory.

Listing 1. run.sh
#!/bin/sh
docker run -it --rm -p 8080:8080 -v $(pwd):/usr/local/structurizr structurizr/lite

Creating the model

The root container of the model is called a "workspace". The workspace contains both the model, and the views. The workspace is created in a file called workspace.dsl

If you haven’t run the container yet, try it now. You’ll notice Structurizr creates a demo workspace with some elements inside.

When you navigate to http://localhost:8080, you’ll see the first models being rendered by Structurizr.

Structurizr SystemContext1

For this use case we’ll design an application for a car parking garage.

  • The system will monitor incoming cars and register their license plate.

  • The system will monitor outgoing cars and allow them to leave if they are a member, or have already paid at the terminals.

  • When someone has not paid at a terminal before leaving they can pay at the beam. The payment terminal at the beam will then directly open the gate.

Structurizr SystemContext2

This image, describing the system, is actually the result of structuring the use case in the Structurizr DSL.

In the workspace section I’ve first specified !identifiers hierarchical which causes relationships to be "transitive". This means members.memberService → database implicitly means members → database. Relationships are explained after the entities.

In the model section you can specify the "entities" that make up your system. The nesting describes the different layers of the C4 model, although in this blog we won’t touch level 4.

The general syntax is <id> = <structurizr type> "<display name". You can add {} to drill down into the entity and describe the sub structure. By specifying <id1> → <id2> "<optional display text>" you can describe the relations between entities.

In the model you’ll find some tags which will be described later.

After the model you’ll find a views section which we haven’t discussed yet. For now I’ve included just the System Context diagram.

Listing 2. workspace.dsl
workspace "P" "Car Park Software System" {

    !identifiers hierarchical

    model {
        user = person "Driver"
        paymentTerminal = softwareSystem "Payment Terminal"
        gateControlSystem = softwareSystem "Gate Control System"
        licensePlateScanner = softwareSystem "License plate Scanner"
        carParkSystem = softwareSystem "Car Park System" {
            members = container "Members" {
                memberService = component "Member Service"
            }
            inventory = container "Carpark Inventory" {
                inventoryService = component "Inventory Service"
                gateControlSystemClientLib = component "Gate Control System Client" {
                    tag Library
                }
                inventoryService -> members.memberService "check membership"
                inventoryService -> gateControlSystemClientLib
            }
            database = container "Database" {
                tag Database
            }
            members.memberService -> database
            inventory.inventoryService -> database
        }
        user -> carParkSystem.members.memberService "take membership"
        paymentTerminal -> carParkSystem.inventory.inventoryService "get parking duration & update payment status"
        licensePlateScanner -> carParkSystem.inventory.inventoryService "register incoming/outgoing vehicles"
        carParkSystem.inventory.gateControlSystemClientLib -> gateControlSystem "open gate"
        paymentTerminal -> gateControlSystem "open gate"
        user -> paymentTerminal "pay fee"
    }

    views {
        systemContext carParkSystem {
            include *
            autolayout lr
        }
    }
}

When I first started using Structurizr I focused a lot on the diagrams the tool was creating, but the views offer a lot of features to change the diagrams. I would suggest to keep the model as close to reality as possible, and tweak the views.

Creating the views

In the System Context diagram we can now see our Car Park System and all the systems it interacts with.

Let’s "open the box" and create a Container diagram. All we have to do is add a container section to our view. Now, when we run structurizr, the extra view is added in the left menu. You can also double click on the component in the System Context view to "zoom in" on the specific Container. This zoom behavior for any entity (Containers and Components) is indicated by the "magnifying glass" icon in the bottom of the entity.

Listing 3. workspace.dsl
workspace "P" "Car Park Software System" {
    ...

    views {
        ...

        container carParkSystem {
            include *
            autolayout lr
        }
    }
}
Structurizr Container1

This is really easy, but the results seems cluttered. In the System Context diagram you could already see interactions being drawn outside our own system (The user paying at terminal, and the terminal having a direct "open gate" interaction). Whether you find these interactions useful in the diagram is of course up to you, but in the Container diagram I find these interactions messy.

Luckily we can easily remove them by excluding those relations.

Elements can also be excluded, as well as elements and relations described by Expressions
Listing 4. workspace.dsl
workspace "P" "Car Park Software System" {
    ...

    views {
        ...

        container carParkSystem {
            include *
            autolayout lr
            exclude user->paymentTerminal
            exclude paymentTerminal->gateControlSystem
        }
    }
}
Structurizr Container2

Style matters

The very first screenshot gave it away, you can style the models to be more clear. These grey square boxes aren’t very nice to look at and, more importantly, don’t really distinguish the elements from each other.

Inside the views section we can add styles to the elements Structurizr has by default, or to elements you gave a certain tag.

Let’s add a Component view (including exclusion of unwanted relations) and style the diagram:

Listing 5. workspace.dsl
workspace "P" "Car Park Software System" {
    ...

    views {
        ...

        component carParkSystem.inventory {
            include *
            exclude carParkSystem.members->carParkSystem.database
            exclude paymentTerminal->gateControlSystem
            autolayout lr
        }

        styles {
            element "Element" {
                color #ffffff
            }
            element "Person" {
                background #2dcd4d
                shape person
            }
            element "Software System" {
                background #2dcd4d
            }
            element "Container" {
                background #00A555
            }
            element "Component" {
                background #97AFB9
            }
            element "Library" {
                background #A1AFA0
                shape component
            }
            element "Database" {
                shape cylinder
                background #00D0FF
            }
        }
    }
}
Structurizr Component1

Both the background color and the shape of entities can be modified. The docs can help you with the supported shpes, and also other attributes you can change in the style section.

Where to go from here

I found C4 and Structurizr very helpful, both for structuring my own thoughts and structuring conversations with developers and architects, but also product owners the business experts.

Diagrams are easy to maintain, modify, and can be versioned using git.

Because a simple docker container is used to create a live and interactive set of diagrams, the master version of the architecture can easily be hosted and shared with anyone.

For more information:

Langauage reference (including all the features not in this blog)

shadow-left