Architecture documentation as code with Structurizr and Asciidoctor. Part 1: Workflow and tooling

No Comments

In this article series, we learn how to generate and publish HTML architecture documentation from code with Structurizr and Asciidoctor.

The goal of this approach is to reduce the efforts for maintaining long-living architecture documentation, keep it up to date, and ensure consistency.

In this first part of the series we will learn about the workflow behind this approach, the main tools used, and their core concepts.

Motivation

Teams with good documentation deliver software faster and more reliably
than those with poor documentation 
Accelerate State of DevOps Report, Google 2021

As a consultant, I work with self-organized development teams where architecture documentation is one of the many team responsibilities. Maintaining a consistent and up-to-date architecture documentation can be a challenging and time-consuming task, especially when things change fast and the tooling isn’t appropriate. Disruptive workflows, missing automation and versioning as well as storing the documentation separately from the code are problems often encountered. As a result, architecture documents can become inconsistent and outdated.

This article series addresses these challenges, aiming to enable development teams to create their architecture documentation using the tool they know and like, their IDE. We will combine open-source tools to create a workflow in order to generate architecture documentation from code. The resulting documents will be published to GitHub Pages and Confluence automatically via GitHub Actions.

Let’s start with a high-level view on this workflow.

The workflow

At first glance, the workflow shown in the following figure might look complex, but most of the steps are automated (gear icon). This automation has two benefits. First, we can easily adopt and republish the documentation if something changes. Second, we can focus on creating our architecture diagrams and writing the textual description of our system.

architecture documentation workflow

The workflow we will implement in this series.

We create our architecture diagrams using Structurizr. These diagrams and the textual documentation will be combined in an AsciiDoc document, which will then be converted to HTML using the Asciidoctor tool chain.

The resulting document can then be published in company wikis or hosted on any website. As an example, we will publish them to GitHub Pages as well as to Atlassian Confluence, allowing non-technical colleagues to access the documentation.

The tools Structurizr and Asciidoctor as well as the markdown language AsciiDoc are at the heart of our workflow. Therefore, let’s have a closer look at them and their underlying concepts.

Structurizr

Structurizr is a collection of tools allowing us to create architecture diagrams programmatically. The approach of creating diagrams using code is not new. Tools like PlantUML or Mermaid have been around for some years, allowing for writing diagrams in a markup or programming language that can be processed and converted to images. As a result, this enables us to treat our diagrams as code. Thus, we can store them in our version control system together with our code, edit them in our IDE, and use our build pipelines to generate them.

Diagrams as code 2.0

Taking this approach one step further, Structurizr introduces a single, central model representing the architecture of our system. For this model, we can create multiple views in order to visualize different aspects of our software on different abstraction levels. This separation between model and views has a big benefit. For example, let’s say we would use a diagram as code or drawing tool of our choice to create multiple architecture diagrams. Consequently, if an important aspect of our architecture changes, we would need to align all affected diagrams. This can be time-consuming and error-prone. With Structurizr, we only need to change the central model (DRY principle). All views based on the model will be updated automatically, making sure they are consistent. To emphasize this advantage compared to other diagrams as code tools, the approach is referred to as diagrams as code 2.0 by the creator of Structurizr, Simon Brown.

docs as code 2.0

Diagrams as code 2.0: Generating multiple diagrams from a central model, keeping them in sync automatically. Credits: Simon Brown

Diagramming approach and the C4 model

The creator of Structurizr is also the creator of the diagramming approach behind Structurizr, the C4 model. The goal of the C4 model is to provide an easy-to-learn approach to software architecture diagramming. At its core, the C4 model defines four hierarchical abstraction (or zoom) levels to describe the static structure of software. These abstraction levels are systems, containers, components, and code elements.

architecture documentation: c4 model

The abstraction levels of the C4 model.

For those abstraction levels, the C4 model defines diagram types showing the software system in scope at different levels of detail. These diagram types are the system context, container, component, and code diagram, while code level diagrams are not defined in detail by the C4 model. Instead, UML, ERM or similar diagrams should be used. As the following figure shows, each lower abstraction level diagram can be seen as a zoom-in view of the next higher abstraction level.

The 4 core diagram types defined by the C4 model. Copyright Simon Brown. www.c4model.com

Structurizr allows for the definition of views for each of these diagram types except the code-level diagrams. It’s recommended to automatically generate the latter from source code (e.g. using our IDE).

Apart from the core diagrams, the C4 model defines supplementary diagram types. These are deployment, dynamic (interaction) and system landscape diagrams, which are also supported by Structurizr. Describing all these diagram types and the related Structurizr model elements would go beyond the scope of this article. Instead, we will get to know some of them during part two of this series.

APIs, output formats, and rendering tools

In order to create the model and views, Structurizr supports multiple languages and APIs. A textual domain-specific language (DSL) as well as APIs for multiple programming languages can be used. In this article series, we will use the Java API. Compared to the DSL, it gives us more freedom regarding the choice of development tooling. Furthermore, it allows for automating the model creation, for example, via code analysis using reflection or parsing configuration files.

Structurizr supports multiple output formats like PlantUML, C4-PlantUML or Mermaid as well as a web-based rendering and editing tool. We will use C4-PlantUML as output format, since it fits well with the C4 model of Structurizr, it is open-source, and it allows for the creation of diagrams in a high visual quality which can easily be integrated in AsciiDoc documents.

However, there is a gap between the features provided by Structurizr and the capabilities of C4-PlantUML. In order to bridge this gap, I wrote a Kotlin-based library on top of the Java API which extends the Structurizr model and provides extended C4-PlantUML support. Furthermore, the library provides a more Kotlin-like syntax and allows us to modify the layout of the resulting C4-PlantUML diagram, which is crucial when diagrams get more complex. Therefore, we will use this library in this series. The following figure compares the rendering tools mentioned in this chapter.

rendering tools

Example component diagram rendered with the web rendering tool (top left), C4-PlantUML (top centre), PlantUML (top right), Mermaid (bottom left) and the extension we will use (bottom right)

The tool ecosystem around Structurizr is constantly growing in terms of official tools, but also third-party libraries. At the time of this writing, a search on GitHub for “Structurizr” results in 170 repositories. So, if the combination of tools used in this article doesn’t suit your needs, it’s likely that there is an adaptation out there that fits for you.

AsciiDoc

AsciiDoc is a plain text markup language built for writing technical documents. The AsciiDoc syntax is lightweight and human-readable. It provides all the basic formatting features (text formatting, lists, tables, …) you would expect from a markup language. Furthermore, AsciiDoc provides features for writing technical documents like syntax highlighting for code blocks, document and image includes, cross references and many more out of the box. The figure below shows an example AsciiDoc file and the generated HTML.

asciidoc file with HTML

AsciiDoc document and the generated HTML output

If additional features are needed, AsciiDoc can be extended. This extension mechanism is a core aspect of AsciiDoc and allows for adding new syntax elements, macros, and document post-processors.

Asciidoctor

AsciiDoc itself is not coupled to any specific output format. Instead, AsciiDoc documents are parsed and then converted to HTML, man-pages, PDF, or any other output format. This is done by an AsciiDoc processor. The reference AsciiDoc processor implementation is called Asciidoctor.  Asciidoctor is open-source and was originally written in Ruby. Now there is a JVM adaptation called AsciidoctorJ and a JavaScript adaptation called Asciidoctor.js. In this article series, we will use AsciidoctorJ.

AsciidoctorJ provides, among other tools, the Gradle plugin we will use to generate HTML documents from our AsciiDoc files. Furthermore, we will use an extension to integrate the C4-PlantUML diagrams generated with Structurizr and convert them to SVG images. Besides Asciidoctor, there is a lot more of tooling around AsciiDoc. For example, WYSIWYG (What You See Is What You Get) editor plugins for the most common IDEs like VS Code, Eclipse, or IntelliJ.

In summary, the plain text syntax of AsciiDoc together with the automation provided by Asciidoctor and the IDE plugins allow us to define our documents as code. As for our diagrams, this means we can store AsciiDoc files in our version control system together with our code, edit them in our IDE, and use our build tools to generate and publish them automatically.

Summary and outlook

Maintaining a consistent and up-to-date architecture documentation can be a challenging and time-consuming task. Disruptive workflows, missing automation and versioning as well as storing the documentation separately from the code can lead to inconsistent and outdated documents. In this article, a workflow based on open-source tools was introduced, aiming to address these challenges.

The core of this workflow are Structurizr and Asciidoctor. Both follow the “as code” approach, allowing developers to maintain their architecture documentation with the tools they are already familiar with. Combining them in an automated tool chain enables development teams to write maintainable, long-living architecture documents.

In part two, we will learn hands-on how to create the architecture documentation. We will use Structurizr to create architecture diagrams and AsciiDoc to write our documentation. Next, we will automate the creation of the resulting HTML documents using Asciidoctor and Gradle. Finally, we will publish our architecture documentation with GitHub Actions.

Christoph works as consultant and coach at codecentric in Karlsruhe, supporting clients regarding agile software development, software architecture and software quality.

Comment

Your email address will not be published.