In this module, you will learn how to:
- Define a
deployment group - Define an
infrastructure node - Enrich insights with
health check1
⌛ Estimated time to complete: 45 min
When crafting a new deployment, you can either start from scratch or from an existing one. We take this last option here, duplicating previously created dev_ deployment environment.
✏️ Update model section accordingly.
📙 REVEAL THE ANSWER
model {
+ prod_ = deploymentEnvironment "prod" {
+ deploymentNode "User workstation" "" "Microsoft Windows 10" "" 1 {
+ deploymentNode "Web browser" "" "Opera" "" 1 {
+ spa_ = containerInstance mila.spa "" "" {
+ }
+ }
+
+ icarus_ = softwareSystemInstance icarus "" "" {
+ }
+
+ deploymentNode "Virtual device" "" "Android" "" 1 {
+ mobile_ = containerInstance mila.mobile "" "" {
+ }
+ }
+
+ deploymentNode "Orchestration" "" "Docker-compose" "" 1 {
+ deploymentNode "mcr.microsoft.com/dotnet/aspnet:6.0" "" "Docker" "" 1 {
+ api_ = containerInstance mila.api "" "" {
+ }
+ }
+
+ deploymentNode "mongo:latest" "" "Docker" "" 1 {
+ url https://hub.docker.com/_/mongo
+ store_ = containerInstance mila.store "" "" {
+ }
+ }
+ }
+ }
+ }
}✏️ Update views section to manage this new deployment environment.
📙 REVEAL THE ANSWER
# C4.D
views {
deployment * dev_ "DevDeployment" "" {
include *
}
+ deployment * prod_ "ProdDeployment" "" {
+ include *
+ autolayout lr
+ }
}✏️ Amend some points:
- As we do have 2 personas, we have to materialize 2 user workstations, one hosting
MILA, the other hostingIcarus. - Obviously,
virtual deviceis replaced bymobile device - Kubernetes being the de-facto standard for remote orchestration, we swap previous
docker-composelayer.
📙 REVEAL THE ANSWER
model {
prod_ = deploymentEnvironment "prod" {
- deploymentNode "User workstation" "" "Microsoft Windows 10" "" 1 {
+ deploymentNode "Matt workstation" "" "Microsoft Windows 10" "" 1 {
deploymentNode "Web browser" "" "Opera" "" 1 {
spa_ = containerInstance mila.spa "" "" {
}
}
- icarus_ = softwareSystemInstance icarus "" "" {
- }
+ }
+ deploymentNode "Simon workstation" "" "Microsoft Windows 10" "" 1 {
+ icarus_ = softwareSystemInstance icarus "" "" {
+ }
+ }
- deploymentNode "Virtual device" "" "Android" "" 1 {
- mobile_ = containerInstance mila.mobile "" "" {
- }
- }
+ deploymentNode "Mobile device" "" "Android" "" 1 {
+ mobile_ = containerInstance mila.mobile "" "" {
+ }
+ }
+ deploymentNode "Backend" "" "Azure" "" 1 {
- deploymentNode "Orchestration" "" "Docker-compose" "" 1 {
+ deploymentNode "Orchestration" "" "Kubernetes" "" 1 {
deploymentNode "mcr.microsoft.com/dotnet/aspnet:6.0" "" "Docker" "" 1 {
api_ = containerInstance mila.api "" "" {
}
}
deploymentNode "mongo:latest" "" "Docker" "" 1 {
url https://hub.docker.com/_/mongo
store_ = containerInstance mila.store "" "" {
}
}
}
}
}
}Resulting view materializes this configuration:
Imagine you foresee high traffic. You could introduce a new backend stack to make sure you can handle it.
✏️ Duplicate the Backend content by introducing 2 new root deploymentNode.
📙 REVEAL THE ANSWER
deploymentNode "Backend" "" "Azure" "" 1 {
- [...]
+ deploymentNode "Worker 01" "" "Azure" "" 1 {
+ [...]
+ }
+ deploymentNode "Worker 02" "" "Azure" "" 1 {
+ [...]
+ }
}Every relationships is duplicated, as we do not specify how Structurizr should cluster deployed instances.
Deployment group is the Structurizr way to cluster stack, and enforce proper relationships bulkhead.
✏️ Add 2 deployment group within deploymentEnvironment section.
✏️ Dispatch api & db container instances among those 2 deployment group, leveraging dedicated container instance field.
Remember to hover C4 keyword if you are lost.
📙 REVEAL THE ANSWER
prod_ = deploymentEnvironment "prod" {
+ worker1_ = deploymentGroup "Worker 01"
+ worker2_ = deploymentGroup "Worker 02"
deploymentNode "Matt workstation" "" "Microsoft Windows 10" "#windows" 1 {
deploymentNode "Web browser" "" "Opera" "#opera" 1 {
spa_ = containerInstance mila.spa worker2_ "" {
}
}
}
deploymentNode "Simon workstation" "" "Microsoft Windows 10" "#windows" 1 {
icarus_ = softwareSystemInstance icarus worker1_,worker2_ "" {
}
}
deploymentNode "Mobile device" "" "Android" "#android" 1 {
mobile_ = containerInstance mila.mobile worker1_ "" {
}
}
deploymentNode "Backend" "" "Azure" "" 1 {
deploymentNode "Worker 01" "" "Azure" "#azure" 1 {
deploymentNode "Orchestration" "" "Kubernetes" "#k8s" 1 {
deploymentNode "mcr.microsoft.com/dotnet/aspnet:6.0" "" "Docker" "#docker" 1 {
- api_ = containerInstance mila.api "" "" {
+ api_ = containerInstance mila.api worker1_ "" {
}
}
deploymentNode "mongo:latest" "" "Docker" "#docker" 1 {
url https://hub.docker.com/_/mongo
- store_ = containerInstance mila.store "" "" {
+ store_ = containerInstance mila.store worker1_ "" {
}
}
}
}
deploymentNode "Worker 02" "" "Azure" "#azure" 1 {
deploymentNode "Orchestration" "" "Kubernetes" "#k8s" 1 {
deploymentNode "mcr.microsoft.com/dotnet/aspnet:6.0" "" "Docker" "#docker" 1 {
- api_ = containerInstance mila.api "" "" {
+ api_ = containerInstance mila.api worker2_ "" {
}
}
deploymentNode "mongo:latest" "" "Docker" "#docker" 1 {
url https://hub.docker.com/_/mongo
- store_ = containerInstance mila.store "" "" {
+ store_ = containerInstance mila.store worker2_ "" {
}
}
}
}
}Relationships are no more tangled, because they simply disappear...
✏️ Fix that by spreading over deploymentGroup information among other container instance & software system instance.
📙 REVEAL THE ANSWER
prod_ = deploymentEnvironment "prod" {
worker1_ = deploymentGroup "Worker 01"
worker2_ = deploymentGroup "Worker 02"
deploymentNode "Matt workstation" "" "Microsoft Windows 10" "#windows" 1 {
deploymentNode "Web browser" "" "Opera" "#opera" 1 {
- spa_ = containerInstance mila.spa "" "" {
+ spa_ = containerInstance mila.spa worker2_ "" {
}
}
}
deploymentNode "Simon workstation" "" "Microsoft Windows 10" "#windows" 1 {
- icarus_ = softwareSystemInstance icarus "" "" {
+ icarus_ = softwareSystemInstance icarus worker1_,worker2_ "" {
}
}
deploymentNode "Mobile device" "" "Android" "#android" 1 {
- mobile_ = containerInstance mila.mobile "" "" {
+ mobile_ = containerInstance mila.mobile worker1_ "" {
}
}
}This dynamic dispatch per device type is unlikely to happen from its own. For realistic sake, let's materialize the brick that perform the underlying magic. To do so, we introduce a new kind of element, the infrastructure node.
✏️ Add an infrastructure node within Backend deployment node.
📙 REVEAL THE ANSWER
- deploymentNode "Backend" "" "Azure" "" 1 {
+ backend_ = deploymentNode "Backend" "" "Azure" "" 1 {
+ gateway_ = infrastructureNode "api.application.com" "" "Application Gateway" ""✏️ Replace deployment group references by new relationships between instances & infrastructure node.
📙 REVEAL THE ANSWER
prod_ = deploymentEnvironment "prod" {
worker1_ = deploymentGroup "Worker 01"
worker2_ = deploymentGroup "Worker 02"
- deploymentNode "Matt workstation" "" "Microsoft Windows 10" "" 1 {
+ ws_ = deploymentNode "Matt workstation" "" "Microsoft Windows 10" "" 1 {
- deploymentNode "Web browser" "" "Opera" "" 1 {
+ browser_ = deploymentNode "Web browser" "" "Opera" "" 1 {
- spa_ = containerInstance mila.spa worker2_ "" {
+ spa_ = containerInstance mila.spa "" "" {
}
}
}
deploymentNode "Simon workstation" "" "Microsoft Windows 10" "" 1 {
icarus_ = softwareSystemInstance icarus worker1_,worker2_ "" {
}
}
- deploymentNode "Mobile device" "" "Android" "" 1 {
+ phone_ = deploymentNode "Mobile device" "" "Android" "" 1 {
- mobile_ = containerInstance mila.mobile worker1_ "" {
+ mobile_ = containerInstance mila.mobile "" "" {
}
}
backend_ = deploymentNode "Backend" "" "Azure" "" 1 {
gateway_ = infrastructureNode "api.application.com" "" "Application Gateway" ""
- deploymentNode "Worker 01" "" "Azure" "" 1 {
- deploymentNode "Orchestration" "" "Kubernetes" "" 1 {
- deploymentNode "mcr.microsoft.com/dotnet/aspnet:6.0" "" "Docker" "" 1 {
+ w01_ = deploymentNode "Worker 01" "" "Azure" "" 1 {
+ k8s_ = deploymentNode "Orchestration" "" "Kubernetes" "" 1 {
+ docker_ = deploymentNode "mcr.microsoft.com/dotnet/aspnet:6.0" "" "Docker" "" 1 {
api_ = containerInstance mila.api worker1_ "" {
}
}
deploymentNode "mongo:latest" "" "Docker" "" 1 {
url https://hub.docker.com/_/mongo
store_ = containerInstance mila.store worker1_ "" {
}
}
}
}
- deploymentNode "Worker 02" "" "Azure" "" 1 {
- deploymentNode "Orchestration" "" "Kubernetes" "" 1 {
- deploymentNode "mcr.microsoft.com/dotnet/aspnet:6.0" "" "Docker" "" 1 {
+ w02_ = deploymentNode "Worker 02" "" "Azure" "" 1 {
+ k8s_ = deploymentNode "Orchestration" "" "Kubernetes" "" 1 {
+ docker_ = deploymentNode "mcr.microsoft.com/dotnet/aspnet:6.0" "" "Docker" "" 1 {
api_ = containerInstance mila.api worker2_ "" {
}
}
deploymentNode "mongo:latest" "" "Docker" "" 1 {
url https://hub.docker.com/_/mongo
store_ = containerInstance mila.store worker2_ "" {
}
}
}
}
}
+ prod_.phone_.mobile_ -> prod_.backend_.gateway_ "makes API call to" "" ""
+ prod_.ws_.browser_.spa_ -> prod_.backend_.gateway_ "makes API call to" "" ""
+
+ prod_.backend_.gateway_ -> prod_.backend_.w01_.k8s_.docker_.api_ "routes incoming calls to" "" ""
+ prod_.backend_.gateway_ -> prod_.backend_.w02_.k8s_.docker_.api_ "routes incoming calls to" "" ""
}Infrastructure node are there to materialize DNS, load balancer, gateway, ...
✏️ Improve rendering by introducing new style for infrastructure node.
Eg, picking new color schema to align with Cornifer one strengthens consistency.
📙 REVEAL THE ANSWER
views {
styles {
+ element "Infrastructure Node" {
+ background #B8F5ED
+ stroke #19967d
+ strokeWidth 8
+ }
}
}It is still difficult for laypeople to distinguish elements quickly. Luckily, there are plenty of traits you can modify to strengthen visual identity.
✏️ What about changing shape & height as well to explicit?
📙 REVEAL THE ANSWER
views {
styles {
element "Infrastructure Node" {
+ shape RoundedBox
+ Height 200
background #B8F5ED
stroke #19967d
strokeWidth 8
}
}
}Better, isn't it?
Health check is a widespread facility in cloud environments. It allows an administrator or service to ping a dedicated URL and assess the health of an endpoint. Having an easy way to visualize ecosystem health at once is very useful when you need to monitor or diagnose issues.
✏️ Pick a software system instance or a container instance you would like to enrich, start typing healthCheck.
Let Cornifer be your guide:
healthCheck "name" url interval timeout
✏️ Fill in placeholders.
Assuming you have properly instrumented your API application, you should end up with:
📙 REVEAL THE ANSWER
deploymentNode "Orchestration" "" "Docker-compose" "#dockerCompose" 1 {
deploymentNode "mcr.microsoft.com/dotnet/aspnet:6.0" "" "Docker" "#docker" 1 {
api_ = containerInstance mila.api "" "" {
+ healthCheck "API is up and running" "http://localhost:5000/healthz" 60 0
}
}Sadly, at the time of writing, Structurizr Lite does not support health check view, where:
Healthyelements are highlighted in greenFaultyelements are highlighted in redBlurryelements are highlighted in amber
Mirroring what health check view provides can be achieved by leveraging what we learned thus far.
-
✏️ Create a brand-new
theme-health-check.dslfto mirror📙 REVEAL THE ANSWER
+ views { + styles { + element "#healthy" { + stroke #000000 + background #42a31f + } + element "#blurry" { + stroke #000000 + background #ffbf00 + } + element "#faulty" { + stroke #000000 + background #e01436 + } + } + }
-
✏️ Amend your
workspace.dslaccordingly📙 REVEAL THE ANSWER
views { !include theme.dslf + !include theme-health-check.dslf
-
✏️ Tag some instances
📙 REVEAL THE ANSWER
w01_ = deploymentNode "Worker 01" "" "Azure" "#azure" 1 { k8s_ = deploymentNode "Orchestration" "" "Kubernetes" "#k8s" 1 { docker_ = deploymentNode "mcr.microsoft.com/dotnet/aspnet:6.0" "" "Docker" "#docker" 1 { - api_ = containerInstance mila.api worker1_ "" { + api_ = containerInstance mila.api worker1_ "#healthy" { } } deploymentNode "mongo:latest" "" "Docker" "#docker" 1 { url https://hub.docker.com/_/mongo - store_ = containerInstance mila.store worker1_ "" { + store_ = containerInstance mila.store worker1_ "#blurry" { } } } }
Obviously, health check view performs this dynamically leveraging provided healthCheck url. Depending on what software you are working on, this single feature can justify the Structurizr paid upgrade.
📘 Completing this stage should lead to this final workspace.
Whether you leverage autolayout or you tailor a custom one, ensure you keep consistency between related view. Here, you may notice how layout support difference spotting between dev & prod deployments, as everything is consistently dispatched.
Next chapter will go a step further to beautify deployment views.
Footnotes
-
Health checkis a widespread facility in cloud environments. It allows an administrator or service to ping a dedicated URL and assess the health of an endpoint. Having an easy way to visualize ecosystem health at once is very useful when you need to monitor or diagnose issues. ↩