Why use code to create software architecture models and diagrams?

One of the frequently asked questions we get is about using code to create software architecture diagrams vs using static text or a UI.

Static text

There has been a trend over the past few years towards text-based tooling, with the most popular examples including PlantUML and WebSequenceDiagrams. With these tools, the diagram source is provided as text using a special domain-specific language, which the tool then visualises, typically with an automatic layout algorithm.

These tools generally have a low barrier to entry, and the source text is easily version controlled. Also, it's relatively straightforward to automate the use of these tools in order to generate diagrams and documentation during your build process.

However, each diagram needs to be defined separately, typically in a separate text file. If you have the same element on two diagrams, and you want to change the name of that element, you need to make sure that you change the name everywhere it's used. The global search and replace features in most developer tooling does make this less of a problem, but it's just one way that a collection of diagrams can easily become inconsistent if not managed properly.

Architecture description languages

The idea of using text isn't new, and attempts have been made in the past to create a more formalised approach using something called an architecture description language. Some, like Darwin for example, offer a text-based language that you can use to describe the architecture of a software system. Diagrams are then created using supporting or additional tooling such as Graphviz. Using a text-based language is certainly appealing for us as software developers, but architecture description languages have never really seen mainstream adoption.

Code - an executable architecture description language

When you think about it, code is just text, and another type of domain-specific language. For this reason, Structurizr provides the ability to create software architecture diagrams and models using code. For example, with Java, you could write a short standalone program like this:

public static void main(String[] args) throws Exception {
    Workspace workspace = new Workspace("Getting Started", "This is a model of my software system.");
    Model model = workspace.getModel();

    Person user = model.addPerson("User", "A user of my software system.");
    SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system.");
    user.uses(softwareSystem, "Uses");

    ViewSet viewSet = workspace.getViews();
    SystemContextView contextView = viewSet.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram.");
    contextView.addAllSoftwareSystems();
    contextView.addAllPeople();

    Styles styles = viewSet.getConfiguration().getStyles();
    styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd").color("#ffffff");
    styles.addElementStyle(Tags.PERSON).background("#08427b").color("#ffffff").shape(Shape.Person);

    StructurizrClient structurizrClient = new StructurizrClient("key", "secret");
    structurizrClient.putWorkspace(25441, workspace);
}

This program creates a model containing elements and relationships, creates a single view, adds some styling, and uploads it to the Structurizr cloud service via the JSON-based web API. Here's the resulting diagram when you open it in Structurizr, where the layout of the diagrams can be modified.

A simple diagram
A simple diagram
A diagram key is automatically generated for you, based upon the styles and shapes defined in the model.

This code, which was used to create the software architecture model, can be thought of as an executable domain specific language, or an executable architecture description language.

Although using code to create simple diagrams might seem verbose when compared to static text-based approaches, the real power becomes evident when you start to think in terms of creating a model of your software system, rather than a disconnected collection of static diagrams. Also, using code provides a number of unique opportunities over using a static textual description.

For example, rather than manually specifying which elements should be shown in a view, it's easy to do things like create a system context diagram for a software system, where only the immediately related elements are added. For example (with Java):

SystemContextView view = views.createSystemContextView(softwareSystem, "SystemContext", "...");
view.addNearestNeighbours(softwareSystem);

And imagine you're building a web application based upon the web-MVC pattern. Given that even a relatively small web application might consist of tens or hundreds of components, a single component diagram is likely to be too cluttered to be useful. Instead, using code, you can create one component diagram per slice through the web application. For example (with Java):

// find all controller components
Set<Component> controllers = webApplication.getComponents().stream()
    .filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_MVC_CONTROLLER))
    .collect(Collectors.toSet());

// create one component view for every controller component
for (Component controller : controllers) {
    ComponentView view = views.createComponentView(webApplication, controller.getName(), "...");
    view.addAllElements();
    view.removeElementsThatCantBeReachedFrom(controller);
}

Another benefit of using code as an architecture description language is that you can write algorithms to help you automatically identify components in an existing codebase, using techniques like reflection or static analysis. This is exactly what the ComponentFinder class in the Java and .NET client libraries is designed to do. It means that just a few lines of code are needed to create a model of the components that reside in a given codebase. For example (with Java):

ComponentFinder componentFinder = new ComponentFinder(
    webApplication,
    "org.springframework.samples.petclinic",
    new SpringComponentFinderStrategy(
            new ReferencedTypesSupportingTypesStrategy(false)
    ),
    new SourceCodeComponentFinderStrategy(new File(sourceRoot, "/src/main/java/"), 150));
componentFinder.findComponents();

Code like this will scan a codebase to identify components (based upon the set of rules that you provide) and the relationships between those components.

Software architecture diagrams as code

With static text-based approaches (e.g. textual DSLs, JSON, YAML, etc), you would likely need to manually create each and every element, relationship, and diagram. With code, you can programmatically manipulate the model and views using the power of your programming language.

Benefits of using code to create software architecture models

In summary, some benefits of using code to create software architecture models are:

  • Code is familiar: Code is familiar to us as software developers, so let's take advantage of this rather than creating another language with which to represent a software architecture model.
  • Flexibility for creating models: In addition to manually writing code to create a software architecture model, we can also write code to extract architectural concepts (e.g. components) from our production codebase using techniques such as reflection, introspection and static analysis.
  • Flexibility for visualising models: Writing code to create the views of a software architecture model provides you with the ability to slice and dice the model as needed. For example, showing all components for a large system will result in a very cluttered diagram. Instead, you can simply write some code to programmatically create a number of smaller, simpler diagrams; perhaps one per vertical slice, web controller, user story, etc. You can also opt to include or exclude any elements as necessary.
  • Versionable: Since the models are code, they are also versionable alongside your codebase.
  • Living documentation: The code to generate the model can be integrated with your automated build system to keep your models up to date; providing accurate, up-to-date, living software architecture diagrams that actually reflect the code.