Diagram as Code Using Structurizr
We all remember the times when we get onboard in one company and we try to understand how the whole system is designed, how the services communicate, how the dependency between those design boxes are settled and etc .. , and as most of the time there is not enough resources to help us get that vision or use them as a support in a daily manner. The worst is when we are gather in meetings and have nothing to say as we dont have a clear vision, just listen and catch the information and try to enrich our high level vision based on those informations. Being blind is not always what i would prefer.
Being blind is not preferable and worst is being a blind between blinds
Get rid of blindness
To being seer and have a vision, first we need to define what we need to be able better understand , better communicate and better make the decisions. We should define the building blocks of a system that helps us to better understand the complexity that the whole system presents, a landscape.
Building blocks
An Enterprise digital platform can be represented by a map of block all relating to one or more other blocks and to make that map comprehensive defining the different level of abstractions is the first step.
Before looking at the abstraction levels and what those abstractions are defining what an enterprise map or landscape should look like is important.
A Landscape represent a highest level of abstraction for visualizing a digital platform, but, is the highest level of visualization sufficient? for sure non, we need a simple way of looking at different level and different dimensions of a system. Think about google map, at the highest level we look at Town, zooming in to the Zone, Road, Alleys and .... , a landscape must give us the same possibilities at enterprise level.
Digital platform abstraction levels:
Lets jump to the point and define the concret building blocks.
A sub system: defines a contextualized business responsability that can contain smaller blocks.
A website, Api, Batch or a Bi dashboard: theses are the next level of abstraction that provides more detailed representation of a business functionality.
A Database, an Application, A search engine: This lower level represents more detailed context and help understand how this small number of blocks works together.
A Code function , module, class or namespace: This level represents how the specific details are collaborating and what are the lowest level of process.
C4 model:
I'm not going to explain the whole presentations and documentation that exists already for c4 model but here we just jump to the advantages that each level had in my side and why some levels was not adopted.
The c4 model represents 4 level of abstractions ( as the name stands for)
Context: Software Context abstraction represents my first need that i presented under the name subsystem, this represent a single block that can contain an isolated complexity inside and locally, a rich or moderate process.
Container: Containers helps to look at the isolated complexity inside a Software Context and see how that complexity looks like, here we need to just understand what happens or can happen inside a local zone.
Component: Components explore the internal composition and detailed blocks of a container, a container can have many components like a database, api, event bus.
Code: This level represents the composition of each component code details like Class , Sequence and Entity relationship.
Using these abstractions:
Dividing the digital platform as per functional domain was the approach we defined to better contextualize the whole system.
Domains are mostly represented by Software System at C4 model, The Applications are represented as containers inside each domain and the components are the single purpose elements inside any container and defining each component hase some extra considerations like portability, composability and deployability.
As i mentioned earlier we dont use the 4th level of C4 model abstraction ( Code ) as at the moment its not achievable with a one shot solution and we dont see the necessity of having this level rapidly.
Structurizr
Structurizr is an open source C4 model based solution that provides a well and reasonably useful tooling like CLI, Container image, a text based DSL ( domain specific language ) and a varietyu of exporters for well-known diagraming tools like PlantUML, Mermaid, ilograph and etc ... . There are a lot of other open source tools that niicely integrate with structurizr.
Walking into practice
Using structurizr is really simple and straight forward, the following snippet illustrates a simple Structurizr DSL workspace.
workspace {
model {
User = person "User"
Order = softwareSystem "Order System" {
Ingestion = container "Order Ingestion" {
Api = component "Api"
Database = component "Database"
Broker = component "Broker"
}
}
Product = softwareSystem "Product System" {
OrderListener = container "Order Listner" {
Database = component "Database"
Processor = component "Availability Processor"
}
}
User -> Order.Ingestion.Api
order.Ingestion.Api -> Order.Ingestion.Database
Order.Ingestion.Api -> Order.Ingestion.Broker
product.OrderListener.Processor -> Product.OrderListener.Database
Order.Ingestion.Broker -> Product.OrderListener.Processor
}
views {
systemLandscape {
include *
}
systemContext Order {
include *
}
container Order {
include *
}
component Order.Ingestion {
include *
}
systemContext Product {
include *
}
container Product {
include *
}
component Product.OrderListener {
include *
}
properties {
structurizr.sort "type"
}
theme default
}
}
To render the DSL you can use the VsCode extension here or run the docker container locally using following command and browing at http://localhost:8080
docker run -it --rm -p 8080:8080 \
-v /<PATH_TO_SOURCE>/src:/usr/local/structurizr structurizr/lite
Rendering the above example results all defined views and selecting the Order component view is as illustrated below:
Refining for maintainability
As the organization has a lot of software context , domains and containers putting all these models and views at a single place can be hard to maintain. our first sight at structurizr raised the same attention. after some hours of reading the docs actually it is possible to defined a better scaffolding and modularity.
We can scaffold the project per domain and put the models and view dsl separately and include them in the workspace.dsl file
The workspace.dsl file now looks like bfollowing snepper:
workspace {
!identifiers hierarchical
model {
User = person "User"
!include product/models.dsl
!include Order/models.dsl
User -> Order.Ingestion.Api
Order.Ingestion.Broker -> Product.OrderListener.Processor
}
views {
systemLandscape {
include *
}
!include product/views.dsl
!include Order/views.dsl
properties {
structurizr.sort "type"
}
theme default
}
}
We have some chalenges and we recognized that the DSL includes are imported prior to rendering and there is not Flattening at some level that challenged us a bit.
This way of doing works great till you break the well system design principles, if there are bidirectional communications and complexities that we all have in some legacy systems this force you to put some models in the workspace or make a separate workspace for those.
I actually put the sample code with a readme on github to help use of structurizr.
Enjoy reading
Subscribe to my newsletter
Read articles from Omid Eidivandi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by