In this module, you will learn how to:
- Define a
deployment group
- Define an
infrastructure node
- Enrich insights with
health check
1
⌛ 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 materiliaze 2 user workstations, one hosting
MILA
, the other hostingIcarus
. - Obviously,
virtual device
is replaced bymobile device
- Kubernetes being the de-facto standard for remote orchestration, we swap previous
docker-compose
layer.
📙 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 we do foresee high traffic to cope with. We could introduce a new backend stack to make sure we 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 disapear...
✏️ 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 strenghtens consistency.
📙 REVEAL THE ANSWER
views {
styles {
+ element "Infrastructure Node" {
+ background #B8F5ED
+ stroke #19967d
+ strokeWidth 8
+ }
}
}
It is still difficult to discriminate for layman. Luckily, there are plenty of traits one 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 environment. It allows an administrator or service to ping a dedicated URL to assess the health of an endpoint. Having an easy way of visualizing all ecosystem health at once is very useful when one would like to monitor or diagnose issue.
✏️ 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 & running" "http://localhost:5000/healthz" 60 0
}
}
Sadly, at the time of writing, Structurizr Lite
does not support health check
view, where:
Healthy
elements are highlighted in greenFaulty
elements are highlighted in redBlurry
elements 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.dslf
to 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.dsl
accordingly📙 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 check
is a widespread facility in Cloud environment. It allows an administrator or service to ping a dedicated URL to assess the health of an endpoint. Having an easy way of visualizing all ecosystem health at once is very useful when one would like to monitor or diagnose issue. ↩