Modern software architectures, especially those built around microservices, require clear and maintainable documentation. While service contracts and API documentation are essential, understanding how services interact and fit into the larger system often proves challenging. This is where architecture diagrams become invaluable.
Model is an open source project that brings the power of code to architecture documentation. It implements the C4 model approach, which provides a hierarchical way to describe and communicate software architecture through different levels of abstraction.
By defining architecture diagrams in code, Model enables you to:
To get started with Model, install the required command-line tools:
# Install the diagram editor and viewer
go install goa.design/model/cmd/mdl@latest
Let’s create a simple system diagram that shows a user interacting with a service that uses a database. The following example demonstrates the key concepts of Model’s design language:
package design
import . "goa.design/model/dsl"
var _ = Design("Getting Started", "This is a model of my software system.", func() {
// Define the main software system - this represents your entire application
var System = SoftwareSystem("Software System", "My software system.", func() {
// Define a database container within the system
Database = Container("Database", "Stores information", "MySQL", func() {
Tag("Database") // Tags help with styling and filtering
})
// Define a service container that uses the database
Container("Service", "My service", "Go and Goa", func() {
Uses(Database, "Reads and writes data")
})
})
// Define an external user of the system
Person("User", "A user of my software system.", func() {
Uses(System, "Uses") // Create a relationship to the system
Tag("person") // Tag for styling
})
// Create a view to visualize the architecture
Views(func() {
SystemContextView(System, "System", "System Context diagram.", func() {
AddAll() // Include all elements
AutoLayout(RankLeftRight) // Arrange elements automatically
})
})
})
This example introduces several key concepts:
Design
function defines the scope of your architectureSoftwareSystem
represents your applicationContainer
defines major components (services, databases, etc.)Person
represents users or rolesUses
creates relationships between elementsViews
define different ways to visualize your architectureModel implements the C4 model’s hierarchical approach to describing software architecture. Each view type serves a specific purpose and audience, as defined by the C4 Model:
Views(func() {
// System Landscape: Show all systems and people in the enterprise landscape
SystemLandscapeView("landscape", "Overview", func() {
AddAll()
AutoLayout(RankTopBottom)
})
// System Context: Focus on one system and its immediate relationships
SystemContextView(System, "context", func() {
AddAll()
AutoLayout(RankLeftRight)
})
// Container: Show the high-level technical building blocks
ContainerView(System, "containers", func() {
AddAll()
AutoLayout(RankTopBottom)
})
// Component: Detail the internals of a specific container
ComponentView("System/Container", "components", func() {
AddAll()
AutoLayout(RankLeftRight)
})
})
Let’s look at each view type in detail:
This view shows the big picture of your software systems landscape. It helps stakeholders understand how your system fits into the overall enterprise IT environment.
This diagram shows your software system in its environment, focusing on the people who use it and the other systems it interacts with. It’s an excellent starting point for documenting and communicating context with both technical and non-technical audiences.
As described in the C4 Model Container Diagram guide, a container view zooms into your software system to show the high-level technical building blocks. A “container” in this context represents a separately runnable/deployable unit that executes code or stores data, such as:
This view helps developers and operations staff understand:
Note that this diagram intentionally omits deployment details like clustering, load balancers, and replication, as these typically vary across environments.
This view zooms into an individual container to show its components and their interactions. It’s useful for developers working on the codebase to understand how the container is structured internally.
Model provides an interactive editor through the mdl
command that makes it easy to
refine and export your diagrams. Start the editor with:
# Start with default output directory (./gen)
mdl serve goa.design/examples/model/design
# Or specify a custom output directory
mdl serve goa.design/examples/model/design -dir diagrams
This launches a web interface at http://localhost:8080 where you can:
The editor provides several ways to manipulate your diagrams:
Element Positioning:
Relationship Management:
Selection Tools:
The following shortcuts help you work efficiently with the editor:
Category | Shortcut | Effect |
---|---|---|
Help | ?, SHIFT + F1 | Show keyboard shortcuts |
File | CTRL + S | Save SVG |
History | CTRL + Z | Undo |
History | CTRL + SHIFT + Z, CTRL + Y | Redo |
Zoom | CTRL + =, CTRL + wheel | Zoom in |
Zoom | CTRL + -, CTRL + wheel | Zoom out |
Zoom | CTRL + 9 | Zoom to fit |
Zoom | CTRL + 0 | Zoom 100% |
Select | CTRL + A | Select all |
Select | ESC | Deselect |
Move | Arrow keys | Move selection |
Move | SHIFT + Arrow keys | Move selection faster |
Model allows you to customize the appearance of your diagrams through styles:
Views(func() {
// Define the view
SystemContextView(System, "context", func() {
AddAll()
AutoLayout(RankTopBottom)
})
// Apply custom styles
Styles(func() {
// Style for elements tagged as "system"
ElementStyle("system", func() {
Background("#1168bd") // Blue background
Color("#ffffff") // White text
})
// Style for elements tagged as "person"
ElementStyle("person", func() {
Shape(ShapePerson) // Use person icon
Background("#08427b") // Dark blue background
Color("#ffffff") // White text
})
})
})
While the mdl
tool is perfect for local development and SVG export, Model also
integrates with the Structurizr service for advanced
visualization and sharing capabilities. The stz
tool manages this integration:
# Install stz
go install goa.design/model/cmd/stz@latest
# Generate a Structurizr workspace file
stz gen goa.design/examples/model/design
# Upload to your Structurizr workspace
stz put workspace.json -id ID -key KEY -secret SECRET
# Download from Structurizr
stz get -id ID -key KEY -secret SECRET -out workspace.json
Consider using Structurizr when you need:
For Goa users, Model provides a plugin that ensures your architecture diagrams stay synchronized with your service definitions. Enable the plugin by importing it in your Goa design:
import (
. "goa.design/goa/v3/dsl"
. "goa.design/plugins/v3/model/dsl"
)
var _ = API("calc", func() {
// Link to your Model design package
Model("goa.design/model/examples/basic/model", "calc")
// Configure container naming (optional)
ModelContainerFormat("%s Service")
// Exclude certain containers from validation
ModelExcludedTags("database", "external", "thirdparty")
// Enable complete validation (optional)
ModelComplete()
})
The plugin provides several features:
You can also configure validation at the service level:
var _ = Service("calculator", func() {
// Override the container name
ModelContainer("Calculator Service")
// Or disable validation for this service
// ModelNone()
})
When working with Model and architecture diagrams:
Version Control Keep your architecture diagrams in source control and review them as part of your normal code review process. This helps maintain accuracy and provides a history of architectural decisions.
Diagram Organization Break down complex architectures into focused views that tell a specific story. Use consistent naming and maintain clear descriptions for all elements.
Goa Integration When using the Goa plugin, maintain alignment between your service definitions and architecture diagrams. Use the same terminology across both to avoid confusion.
Documentation Include context and rationale in your descriptions. Document significant architectural decisions and keep diagrams focused on their intended purpose.