Are you struggling to create a system landscape of all your applications? Is your system landscape architecture difficult to maintain? Then Structurizr might be a good option for you to consider. With Structurizr, you can create and maintain your diagrams as code. Let’s take a closer look at it in this blog!

1. Introduction

The C4 model helps you with visualizing software architecture. We all know the whiteboard diagrams cluttered with boxes and connectors. The C4 model approach helps you with visualizing software architecture in a more structured way. A good explanation is given on the C4 model website, so if you do not know what it is, it is worth reading first.

Next question is which tool you use to create the diagrams. You can use Visio, draw.io, PlantUML, even PowerPoint, or whatever tool you normally use for creating diagrams. However, these tools do not check whether naming, relations, etc. are consistently used in the different diagrams. Besides that, it might be difficult to review new versions of diagrams because it is not clear which changes are made.

In order to solve these problems, Simon Brown, the author of the C4 model, created Structurizr. Structurizr allows you to create diagrams as code. Based on the code, Structurizr visualizes the diagrams for you and you can interact with the visualization. Because the diagrams are maintained in code, you can add them to your version control system (git) and changes in the diagrams are tracked and can be easily reviewed.

In a previous blog, some features of Structurizr are explored. Structurizr Lite was used which supports only one workspace. However, if you have a more diverse system landscape, Structurizr Lite is not sufficient anymore. You will have multiple workspaces, one for every software system. You also probably want an overview of your entire system landscape.

In the remaining part of this blog, you will explore how you can use Structurizr in order to maintain not only the software architecture of one system, but how to maintain your entire system landscape as code.

Sources used in this blog can be found at GitHub.

2. Prerequisites

Prerequisites for this blog are:

  • Basic knowledge of the C4 model;
  • Basic knowledge of Docker;
  • Basic knowledge of Structurizr;
  • Linux is used, if you are using a different Operating System, you will need to adjust the commands accordingly.

3. Installation

As mentioned before, Structurizr Lite cannot be used for this scenario. Instead, you need to install Structurizr on-premises.

Create in the root of the repository a data directory. This directory will be mapped as a volume in the docker container. If you have executed the previous blog, ensure that you clean the data directory first. With Structurizr Lite, it is intended that you can edit files in this data directory, with Structurizr on-premises it is advised not to alter the files in the data directory. Structurizr on-premises should be run on a separate server and a normal user should not have access to the data directory anyway.

Execute the following command from within the root of the repository:

$ docker run -it --rm -p 8080:8080 -v ./data:/usr/local/structurizr structurizr/onpremises

Navigate in your browser to http://localhost:8080, login with the default user structurizr and password password and the Structurizr webpage is shown.

4. Single Workspace

First, let’s see how you can create a single workspace with Structurizr on-premises.

Click New workspace and an empty workspace is created.

It is not possible anymore to edit files on your host machine, just like with Structurizr Lite. So, how to upload your DSL files to the workspace? In order to do so, you need Structurizr CLI. At the moment of writing, v2024.02.22 is the latest version, which can be downloaded as a zip from GitHub. Unpack the zip file and add the directory to your path.

You will upload the latest version of the software system from the previous blog. The DSL is located in the workspaces/3-basic-styles directory. Navigate to this directory. In order to push the DSL to Structurizr, you will make use of the push command. The push command needs some parameters which can be found in the settings of the Structurizr workspace. You need the information as shown under API details. Below this information, the parameters can easily be copied.

Execute the following command, replacing the parameters as for your situation:

$ structurizr.sh push -url http://localhost:8080/api -id 1 -key 2607de22-7ce0-4eb1-9f28-1e7e9979121a -secret 09528dfd-0c0a-4380-85cb-766b8da5e1dc -workspace workspace.dsl
Pushing workspace 1 to http://localhost:8080/api
 - creating new workspace
 - parsing model and views from /<path to project directory>/MyStructurizrPlanet/workspaces/3-basic-styles/workspace.dsl
 - merge layout from remote: true
 - storing previous version of workspace in null
 - pushing workspace
Getting workspace with ID 1
Putting workspace with ID 1
{"success":true,"message":"OK","revision":2}
 - finished

If everything went well, the DSL is pushed successfully. The System Context and Container diagram are now added to the workspace.

5. Workspace Features

In this section, some interesting features of Structurizr on-premises are shown.

5.1 Version Control

Every upload automatically creates a new version. It is also possible to retrieve an older version.

5.2 Error Checking

The Inspections in the left menu, gives you an overview of errors in your DSL.

5.3 Reviews

When you open a diagram, you can create a review.

When creating the review, you can choose which diagrams need to be reviewed, what kind of review you are requesting and whether unauthenticated access is allowed or not.

The reviewer can add comments of course. Next to the Public review text, a link to a checklist is present which can help you executing the review.

6. Create System Landscape Using DSL Only

The above examples consist out of diagrams for a single software system. Often, multiple software systems are used in an organisation. These software systems interact with each other and thus form together a system landscape. Each team will be responsible for its own software system diagrams, but it is also necessary to have a diagram containing the larger picture. Let’s explore in this section whether this is possible using Structurizr. You will be using an example based on the enterprise example provided at the Structurizr GitHub repository. The files can be found in workspaces/4-system-landscape.

Create a new workspace via the UI, navigate to the 4-system-landscape directory and push the customer-service DSL to this workspace.

$ structurizr.sh push -url http://localhost:8080/api -id 2 -key f24fe705-a508-4f8d-9cf7-3fc7b323f293 -secret 02c6597f-c750-47e0-9b88-f6e26fccdf38 -workspace customer-service/workspace.dsl

Create in the same way a workspace for the invoice-service and the order-service. Push the corresponding DSL to each workspace.

A separate system-landscape DSL is present which uses a plugin in order to create the relationships between the software systems. Create a workspace for this DSL and push it.

$ structurizr.sh push -url http://localhost:8080/api -id 5 -key cb18cabb-61c7-4c3a-a58e-2e97ff0fa285 -secret a638aa99-73cd-427d-8188-3788e678129f -workspace system-landscape/workspace.dsl 

This creates the system landscape overview.

However, two issues are encountered with this view:

  • It is not possible to click on the Order Service f.e. in order that it opens the software system diagram for the Order Service.
  • The DSL of the Customer Service does not define the relationship with Order Service and Invoice Service as can be seen in the diagram below. It would be nice if this inconsistency was reported one way or the other.

I asked a question about this at GitHub and used the answer to create a solution which can be found in the following paragraphs.

7. Create System Landscape Using Java

The solution to the problem with the absence of links to the different services is to make use of the Java Structurizr library. With this library, you have much more control in order to achieve desired functionality. I used the source code from the example in the Structurizr repository and added it to directory workspaces/5-system-landscape.

The pom file contains the necessary dependencies in order to run the code and also the maven-assembly-plugin is added in order to create a fat jar.

The code executes the following steps:

  1. Create a workspace for the system landscape.
  2. Create workspaces for each service.
  3. Generate the system landscape by parsing the workspaces metadata, create the necessary relationships, add a link to the services and create a view for the system landscape.

Execute the following command from within the workspaces/5-system-landscape directory in order to build the fat jar.

$ mvn clean package

Run the code and an error occurs.

$ java -jar target/mystructurizrplanet-1.0-SNAPSHOT-jar-with-dependencies.jar 
Mar 02, 2024 11:41:12 AM com.structurizr.api.AdminApiClient createWorkspace
SEVERE: com.structurizr.api.StructurizrClientException: The API key is not configured for this installation - please refer to the documentation
Exception in thread "main" com.structurizr.api.StructurizrClientException: com.structurizr.api.StructurizrClientException: The API key is not configured for this installation - please refer to the documentation
        at com.structurizr.api.AdminApiClient.createWorkspace(AdminApiClient.java:109)
        at com.mydeveloperplanet.mystructurizrplanet.CreateSystemLandscape.main(CreateSystemLandscape.java:30)
Caused by: com.structurizr.api.StructurizrClientException: The API key is not configured for this installation - please refer to the documentation
        at com.structurizr.api.AdminApiClient.createWorkspace(AdminApiClient.java:105)
        ... 1 more

In order to use the Java library, you need to use an API key. This API key is disabled by default. In order to enable it, you need to add a file structurizr.properties to your data directory. In the properties file, you set the API key to its bcrypt encoded value.

structurizr.apiKey=$2a$10$ekjju1h3fC1y2YAln7wqxuJ.q0gBjQoFPX/Wvmzr.L5aIdoqvUIwa

Add read permissions to the file.

$ chmod o+r data/structurizr.properties

Restart the docker container and execute the jar file again.

$ java -jar target/mystructurizrplanet-1.0-SNAPSHOT-jar-with-dependencies.jar 
Mar 02, 2024 11:50:03 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 7
Mar 02, 2024 11:50:04 AM com.structurizr.api.WorkspaceApiClient putWorkspace
INFO: Putting workspace with ID 7
Mar 02, 2024 11:50:04 AM com.structurizr.api.WorkspaceApiClient putWorkspace
INFO: {"success":true,"message":"OK","revision":2}
Mar 02, 2024 11:50:04 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 8
Mar 02, 2024 11:50:04 AM com.structurizr.api.WorkspaceApiClient putWorkspace
INFO: Putting workspace with ID 8
Mar 02, 2024 11:50:04 AM com.structurizr.api.WorkspaceApiClient putWorkspace
INFO: {"success":true,"message":"OK","revision":2}
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 9
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient putWorkspace
INFO: Putting workspace with ID 9
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient putWorkspace
INFO: {"success":true,"message":"OK","revision":2}
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 1
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 2
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 3
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 4
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 5
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 6
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 7
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 8
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 9
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient getWorkspace
INFO: Getting workspace with ID 6
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient putWorkspace
INFO: Putting workspace with ID 6
Mar 02, 2024 11:50:05 AM com.structurizr.api.WorkspaceApiClient putWorkspace
INFO: {"success":true,"message":"OK","revision":2}

If you open the system landscape workspace, it is now possible to double-click one of the services and you will be navigated to the corresponding service.

Great, but some caveats to mention:

  1. This source code always creates new workspaces every time you run it. This is just an example of what is possible using the Java library. If you want to update existing workspaces, you will need to alter the source code for this purpose.
  2. The source code contains hardcoded the API key in plain text. You should not do this in a production environment.

8. Validate Relationships

Is it possible to validate the relationships using the Java library? Yes, it is. An example of the source code can be found in directory workspaces/6-validate-relationships. This code will validate offline whether the DSL contains the correct relationships. It is only intended to prove that the validation can be done. For using this in production, the source code needs to be made more robust.

Build the code and run it.

$ mvn clean package
$ java -jar target/validaterelationships-1.0-SNAPSHOT-jar-with-dependencies.jar 
missing relation in CustomerService {2 | Order Service | } ---[Manages customer data using]---> {4 | Customer Service | }
missing relation in CustomerService {3 | Invoice Service | } ---[Gets customer data from]---> {4 | Customer Service | }

The validation finds the two errors in the Customer Service.

Add the relationships to the Customer Service DSL.

model {
    !extend customerService {
        api = container "Customer API"
        database = container "Customer Database"

        api -> database "Reads from and writes to"
        orderService -> customerService "Gets customer data from"
        invoiceService -> customerService "Gets customer data from"
    }
}

Build the code and run it. The errors are gone and the relationships are visible in the Customer Service if you run the code from the previous paragraph.

9. Conclusion

Structurizr offers many features in order to get a grip onto your software architecture. It also allows you to generate a system landscape and to implement several customizations, e.g. custom validation checks. You need to learn the Java Structurizr library, but the learning curve is not very steep.