Structurizr Workspaces Split Across Files Now Import Into Archyl

Some Structurizr workspaces keep almost nothing in workspace.dsl. A header, a model block, and a column of !include lines, one per system, with the real model spread across the files they point to.

Until this week, Archyl could not import that. Our 4 August post on the Structurizr Cloud shutdown said so in one line: multi-file workspaces had to be flattened first. That ruled out workspaces that keep every system in its own file. Structurizr Cloud shuts down on 30 September, two weeks from now.

You can now upload the workspace as a .zip, and Archyl resolves every !include against the files inside it.

Why a single file was never going to work

Take a workspace laid out like this:

workspace.dsl
model/
  people.dsl
  relationships.dsl
systems/
  ledger.dsl
  notifications.dsl
  payments.dsl

with a root file that only stitches the pieces together:

workspace "Payments Platform" "Card payments and settlement" {
    model {
        !include model/people.dsl
        !include systems
        !include model/relationships.dsl
    }
}

Upload or paste that root file on its own and the importer has nothing to resolve the paths against. It parses what it has, skips each include, and tells you:

line 3: directive '!include' is not supported and was skipped
line 4: directive '!include' is not supported and was skipped
line 5: directive '!include' is not supported and was skipped

The warnings are accurate, and they are also the whole result: nothing from those six files is in it. A single self-contained .dsl file still imports exactly as before. The zip is for everything else.

Zip the workspace directory, not the repository

If your workspace uses !include, the split files exist as files somewhere. Structurizr's include documentation describes a file include as "a single local file, specified by a relative path", so the copy that matters is on a disk or in Git, not in the cloud. Find the directory holding workspace.dsl and zip that.

From inside the directory:

zip -r workspace.zip workspace.dsl model systems

Or, if the workspace lives in a repository, straight from a commit:

git archive --format=zip -o workspace.zip HEAD:docs/architecture

The distinction matters because the archive is capped at 500 entries, and that count is checked before anything is filtered out. Zip a whole repository with its .git folder and you can pass the cap before a single .dsl file is read. A wrapper folder at the top of the archive is fine, since includes resolve relative to the file doing the including.

What happens when you upload it

In the import modal, the Structurizr DSL tab's button now reads Upload .dsl or .zip. Choose an archive and the code editor is replaced by a card showing its name and size. Click Validate and the card adds the file count and the root file it picked.

This works when importing into an existing project and when creating a new one. A new project takes its name from the workspace header, so workspace "Payments Platform" { ... } can create a project and a bare workspace { ... } cannot.

On the server, the archive goes through four steps:

  1. Pick the root. workspace.dsl if the archive has one, otherwise the shallowest .dsl file. When it has to choose between several files and none is named workspace.dsl, a warning names the file it used.
  2. Expand includes as text, before parsing. The included file's content replaces the !include line, the same inlining Structurizr does. Paths are relative to the including file, so systems/index.dsl including shared/platform.dsl finds systems/shared/platform.dsl.
  3. Resolve directory includes. !include systems pulls in every .dsl file directly inside systems/, in name order. Subdirectories are not walked.
  4. Stop at the edges. A file that ends up including itself, directly or through another file, has the cycle broken and reported. Nesting stops at 10 levels.

The expanded workspace then goes through the same Structurizr importer as a single file, with the same fidelity and the same warning list for anything it skips.

An include that cannot be resolved, such as a missing file or a path pointing outside the archive, becomes a warning too. The rest of the workspace still imports.

Remote includes are refused on purpose

Structurizr also lets !include point at an HTTPS URL. Archyl does not follow those. Resolving them would let any uploaded file make our servers request an address of its choosing, which is a server-side request forgery vector regardless of who uploads it. The line is skipped with a warning:

!include: remote target "https://example.com/shared/identity.dsl" is not supported and was skipped

If the remote file holds model elements you need, download it into the archive and change the line to a relative path.

The limits

Limit Value When exceeded
Archive size 10 MiB Upload refused
Entries in the archive 500 Upload refused
Total size once expanded 50 MiB Upload refused
Any single file 5 MiB File skipped, with a warning
Include nesting 10 levels Deeper include skipped, with a warning

Entries whose path tries to escape the archive, through an absolute path or .. segments, are skipped with a warning. Only text files are kept: .dsl, .md, .json, .yaml, .yml and .txt. Images and everything else are dropped without a warning, since the DSL importer has no use for them.

What it still does not do

Git sync does not resolve includes. Repository sync reads archyl.yaml, not a Structurizr workspace, so there is no path yet where Archyl pulls a multi-file DSL straight from a repository. If your DSL lives in Git, the git archive command above is the workflow for now.

Nothing else about Structurizr fidelity changed. Layout, styles and deployment views were not imported before and are not imported from a zip. There is still no workspace.json path either: Archyl reads DSL text. If hand-tuned layout is what you value most in Structurizr, the options in the shutdown post that keep you on Structurizr's own tooling are still the better fit.

Before 30 September

  1. Make sure you hold the source files. A workspace split with !include was authored as files, so find them. If anything lives only in the cloud copy, such as layout nudged in the browser or documentation written there, the 4 August post covers how to get it out.
  2. Zip the directory holding workspace.dsl, not the repository around it.
  3. Upload and validate. Open Import Project, or the import modal inside an existing project, pick Structurizr DSL, upload the zip and click Validate. Check the root file it chose and read every warning before you import.
  4. Commit the directory to a repository if it is not in one already, so the next person does not have to find it on your laptop.

The importer's full behavior, including the name rules for new projects, is in the Architecture as Code docs. For the other formats Archyl imports, see importing Structurizr, LikeC4 and IcePanel projects.