Visualizing Software Architecture With the C4 Model and Structurizr
Explore the C4 model and Structurizr and see how they can be used to visualize and manage software architecture effectively with practical examples and insights.
Join the DZone community and get the full member experience.
Join For FreeWhat Is the C4 Model?
The C4 model is a hierarchical framework designed to help software architects and developers visualize and communicate the essential aspects of software architecture in a clear and structured way. Unlike traditional diagramming approaches that often result in cluttered and overly complex diagrams, the C4 model focuses on simplicity and abstraction to convey architectural concepts effectively.
The next question is which tool you use to create said 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.
What Is 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 article, 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 this article, you will explore how you can use Structurizr to maintain not only the software architecture of one system but your entire system landscape as code.
Sources used in this blog can be found at GitHub.
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
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, log in with the default user structurizr and password password, and the Structurizr webpage is shown.
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 can you 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. 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 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 goes well, the DSL is pushed successfully. The System Context and Container diagram are now added to the workspace.
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.
Create System Landscape Using DSL Only
The above examples consist of diagrams for a single software system. Often, multiple software systems are used in an organization. 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 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
In the same way, create 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 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 on GitHub and used the answer to create a solution that can be found in the following paragraphs.
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 to achieve the desired functionality. I used the source code from the example in the Structurizr repository and added it to the directory: workspaces/5-system-landscape.
The pom file contains the necessary dependencies to run the code, and the maven-assembly-plugin is added to create a fat jar.
The code executes the following steps:
- Create a workspace for the system landscape.
- Create workspaces for each service.
- 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
To use the Java library, you need to use an API key. This API key is disabled by default. 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 there are some caveats to mention:
- 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.
- The source code contains a hardcoded API key in plain text. You should not do this in a production environment.
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.
Conclusion
Structurizr offers many features to get a grip on 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.
Published at DZone with permission of Gunter Rotsaert, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments