TS-4: Modeling

This technical standard provides guidance on how to model software systems at different levels of abstraction.

See also TS-54: Threat Modeling.

Modeling levels

Models are abstract representations of a system’s functionality and data flow. Models can be created at different levels of abstraction, each offering different levels of detail and different insights.

Different models provide alternative viewpoints into the architecture, which will be useful to different groups of stakeholders – an executive will benefit the most from the high-level conceptual architecture, a programmer from the logical and physical breakdown of components, a performance engineer from the descriptions of the runtime processes, and an operator needs most to understand the deployment infrastructure.

The following architectural views are RECOMMENDED. These are extended from the 4+1 architectural view model. The seven views are arranged as an abstraction ladder – the first levels are the most abstract, with subsequent levels incrementally becoming more concrete:

  • Conceptual: The most abstract and least detailed view. It provides a strategic, high-level overview of a whole system, defining the major components and how they interact. This view is readable by non-technical stakeholders.
  • Logical: Describes how a system works in terms of its functions and logical information flows, while abstracting away implementation details. This view is about functional decomposition — the major software components, their responsibilities, and the logical information flows between them, without going into any detail about how the code is organized, deployed, or run.
  • Development: The static organization of the implementation — the modules, layers, repositories, and build artifacts the code is divided into, and the dependencies between them.
  • Process: The runtime structure — the processes, threads, and services the system runs as, and how they communicate, synchronize, and handle concurrency.
  • Physical: The deployment topology — how the software maps onto infrastructure: the hosts, networks, data stores, and devices it runs on. Components that may be represented include clients, servers, load balancers, databases, storage, caches, message queues, and external services. This view maps the logical and process views to concrete infrastructure, providing the information teams need to deploy and observe their systems in production.
  • Technical: The lowest level of abstraction. This is the concrete technology stack — the specific programming languages, runtime environments, and system software from which the production system is made.

Cutting across all of the above:

  • Scenarios: A handful of architecturally significant end-to-end flows, traced through the perspectives above. Scenarios are not an abstraction level but a cross-cut through the others. They illustrate how the architecture realizes important behaviors, and serve as a consistency check that the other perspectives agree with one another. (This is the "+1" of the 4+1 model.)

these modeling perspectives as the organizing structure for a system’s living architectural documentation.

Text-to-diagram modeling tools

Text-to-diagram tools allow you to define diagrams using a simple, human-readable markup language, rather than manually drawing them with a graphical tool.

The primary benefit is that diagrams become version-controllable – they are plain text, not binary image files – which means they integrate naturally with version control systems and code review workflows. It makes keeping diagrams in sync with code changes much easier than maintaining separate image files.

However, text-to-diagram tools come with tradeoffs. They require learning a domain-specific language, and complex diagrams can be tedious to express textually. The diagrams also require rendering tools to view – you can’t simply look at the text and visualize the diagram. This means adding tooling and build-time dependencies to your workflow.

PlantUML is a popular choice for this purpose, offering a lightweight text-markup language for creating UML diagrams. Other options include Mermaid, Graphviz, and many others.

Of these, Mermaid now enjoys the broadest native support, particularly for client-side web rendering. It ships as a JavaScript library (mermaid.js) that renders diagrams directly in the browser via SVG, with no server-side rendering step or external service required. This makes it well suited to contexts like static site generators, documentation platforms, and web apps, where diagrams can be rendered on the fly from embedded text. Mermaid is also natively supported in GitHub- and GitLab-flavoured Markdown, so mermaid code fences render automatically in READMEs, issues, and pull requests, without any extension or configuration.

PlantUML, by contrast, is less widely supported. It has no equivalent client-side JavaScript renderer, so diagrams must be rendered either by a local Java installation or by a PlantUML server (local or hosted), adding a runtime or infrastructure dependency wherever the diagrams need to be viewed. For this reason, Mermaid is the default choice at this time.

Nevertheless, the rest of this technical standard covers some logistics for rendering PlantUML, for reference purposes.

To embed text-to-diagram markup directly in AsciiDoc and Markdown documentation files and have the diagrams render automatically in VS Code, you’ll need to configure rendering tools. The sections below explain how to set this up for both AsciiDoc and Markdown files.

AsciiDoc

To render PlantUML diagrams in AsciiDoc, the easiest option is to use the online Kroki service, a free web service that renders diagrams and charts from text representations, not only in the PlantUML DSL but also Mermaid, Graphviz and many others.

In VS Code, add the following to your settings.json file:

{
  "asciidoc.extensions.enableKroki": true,
  "asciidoc.preview.asciidoctorAttributes": {
    "kroki-server-url": "https://kroki.io",
  },
}

You can also run a local Kroki server. The full instructions are here, but basically it involves pulling and running a Docker container based on the official Kroki image:

# Pull the Kroki server image from Docker Hub.
# https://hub.docker.com/r/yuzutech/kroki
docker pull yuzutech/kroki

# Run a container based on this image.
# Run the container in detached mode (-d) and map the container's
# port 8000 to your local machine's port 8080.
docker run -d -p 8080:8000 yuzutech/kroki

Go to http://localhost:8080/ to verify that the Kroki server is running. Then change your settings.json to point to the local server instead. These settings need to be defined at the user or workspace level.

{
  "asciidoc.extensions.enableKroki": true,
  "asciidoc.preview.asciidoctorAttributes": {
    "kroki-server-url": "http://localhost:8080",
  },
}

For AsciiDoc, the PlantUML DSL code needs to be embedded within literal blocks. If you have everything configured correctly, you should be able to see the rendered diagram below, when viewing this document in preview mode. (You might need to restart VS Code for the configuration changes to take effect.)

@startuml
entity person {
* id: INT <<FK>>
* name: VARCHAR(128)
---
address: VARCHAR(256)
email: VARCHAR(128)
phone: VARCHAR(16)
}
@enduml

If you can see the Kroki landing page via http://localhost:8080 in your web browser, but the diagrams do not generate in AsciiDoc preview in VS Code, try adjusting the security settings. Open VS Code’s command palette (Ctrl+Shift+P), select "AsciiDoc: Manage Preview Security Settings", and choose "Allow insecure local content".

Markdown

Asciidoctor.js, the JavaScript port of Asciidoctor that powers VS Code’s AsciiDoc extension, specifically uses Kroki for the rendering of embedded graphics DSLs. For Markdown files, you have more flexibility. For simplicity, it is recommended to use a dedicated PlantUML server, rather than a more general-purpose service like Kroki.

The PlantUML project runs a free PlantUML server at https://www.plantuml.com/plantuml. To use that, simply add the following configuration to VS Code’s settings.json file:

"plantuml.render": "PlantUMLServer", }

You can also run a local instance of the PlantUML server using Docker. The full instructions are here, but essentially:

# Pull the PlantUML server image from Docker Hub.
# This image uses Jetty as the server.
# https://hub.docker.com/r/plantuml/plantuml-server
docker pull plantuml/plantuml-server:jetty

# Run a container based on this image.
# Run the container in detached mode (-d) and map the container's
# port 8080 to your local machine's port 8080.
docker run -d -p 8080:8080 plantuml/plantuml-server:jetty

Once the container is running, you can access the PlantUML server in your browser by visiting http://localhost:8080/. You should see the PlantUML server’s user interface, which allows you to type PlantUML code and see the generated diagram in real time.

Next, you’ll need to install a PlantUML extension in your code editor, and configure the extension to use your locally-running PlantUML server. The following extensions are available for VS Code and JetBrains IDEs:

These are the settings you’ll need for VS Code:

{
  "plantuml.server": "http://localhost:8080",
  "plantuml.render": "PlantUMLServer",
}

Alternatively, if you want to use the Kroki service to render PlantUML diagrams in Markdown, there’s a VS Code extension for that, called Markdown Kroki (pomdtr.markdown-kroki).

To render PlantUML diagrams within Markdown files, no further steps are required. Simply embed the PlantUML DSL within code blocks, as below, and view the Markdown document in preview mode. Select the option to "Allow insecure local content", and you should see the rendered diagram.

```plantuml
@startuml
entity person {
* id: INT <<FK>>
* name: VARCHAR(128)
---
address: VARCHAR(256)
email: VARCHAR(128)
phone: VARCHAR(16)
}
@enduml
```

Depending on your setup, you may need additional settings. For example, if you have enabled the Markdown Preview Enhanced extension, you will need to add the following setting to your settings.json file:

{ "markdown-preview-enhanced.plantumlServer": "http://localhost:8080", }

Notations

The modeling levels above describe what to model. This section covers notations for how to draw the models.

Unified Modeling Language (UML)

The Unified Modeling Language (UML) is the best-known notation for modeling object-oriented software systems. It is a graphical language for specifying, visualizing, and documenting the artifacts of a system, standardized by the Object Management Group since 1997 as a unification of three earlier competing notations (Booch, OMT, and OOSE).

UML diagrams fall into two broad categories:

  • Structure diagrams depict the static elements of a system, irrespective of time — eg. class, component, package, and deployment diagrams.
  • Behavior diagrams depict the dynamic interactions between elements of a system — eg. use case, sequence, activity, and state machine diagrams.

Of the many diagram types UML defines, use case, class, and sequence diagrams see the most real-world use. It is NOT RECOMMENDED to attempt to learn or apply the full UML specification — only a small subset of it covers the majority of practical modeling needs.

UML is most useful for modeling object-oriented designs, and is less suited to other programming paradigms, with the notable exception of use case diagrams, which are paradigm-agnostic and useful for requirements analysis (see TS-1: Use cases).

UML diagrams serve two purposes, often over the same diagram’s lifetime: forward design, sketching a system before it is built, and backward documentation, describing a system as it was built. A diagram frequently starts as the former and is fleshed out into the latter, accumulating implementation detail — field names, method signatures, concrete relationships — as the design solidifies into code.

As with any notation, the goal is clear communication, not strict conformance to a specification. It is RECOMMENDED to use only as much of UML’s formal notation as is needed to convey a design clearly, deviating from the standard where doing so improves clarity. UML diagrams SHOULD be drawn at a level of abstraction appropriate to their audience and purpose — from rough whiteboard sketches during design, to detailed blueprints documenting an implementation.

See also TS-1: Event storming, a workshop-based technique for discovering the domain events and components of a system, which is a useful precursor to formal modeling.

The model-code gap

A model that documents an existing system is only useful while it remains an accurate reflection of the code. Left unmaintained, a model drifts from the system it describes — a discrepancy known as the model-code gap (see "Just Enough Software Architecture" by George Fairbanks). A model that is mostly accurate is one nobody can trust, since a reader cannot tell which parts have drifted.

This risk applies to any model used as living documentation, not only formal design docs — see TS-3: Design docs for how the living, decision-free principle keeps a design doc synchronized with production. Where a model is not maintained as living documentation — eg. a diagram drawn to support a one-time discussion — it SHOULD be labeled as a point-in-time snapshot, so a future reader does not mistake it for current truth.