Semantic Architecture as Code¶
The idea¶
Structurizr DSL proved that architecture models can live as text files in Git — reviewed in PRs, validated in CI/CD, generating views from a single model. That workflow is compelling.
RDF/Turtle gives you the same workflow with richer semantics. Turtle files are text. They live in Git. They diff cleanly. They can be validated in CI/CD (via SHACL). And they carry typed elements, typed relationships, cross-layer traceability, and formal validation — things Structurizr DSL can't express.
The trade-off is real: Turtle is more verbose than Structurizr DSL, the learning curve is steeper (you need Turtle + SPARQL basics), and diagram generation is less polished. For a 3-container system, Structurizr DSL is faster. For a system where you need cross-layer queries, governance validation, or AI agent access, Turtle pays for itself.
Side by side¶
Structurizr DSL:
workspace "Order Management" {
model {
customer = person "Customer"
orderSystem = softwareSystem "Order Management" {
api = container "Orders API" "Handles order lifecycle" "Spring Boot"
db = container "Orders DB" "Stores orders" "PostgreSQL"
worker = container "Orders Worker" "Processes events" "Spring Boot"
}
paymentSystem = softwareSystem "Payment System" "External payment provider"
customer -> api "Places orders" "HTTPS"
api -> db "Reads/writes" "JDBC"
api -> worker "Sends events" "AMQP"
api -> paymentSystem "Processes payments" "HTTPS"
}
views {
container orderSystem { include * ; autolayout lr }
}
}
Same system in Turtle (ArchiMate semantics):
@prefix am: <https://meta.linked.archi/archimate3/onto#> .
@prefix arch: <https://meta.linked.archi/core#> .
@prefix bs: <https://meta.linked.archi/backstage/onto#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix ex: <https://model.example.com/orders#> .
# Business context (Structurizr can't express this)
ex:OrderFulfillment a am:Capability ;
skos:prefLabel "Order Fulfillment"@en .
ex:OrderPlacement a am:BusinessService ;
skos:prefLabel "Order Placement"@en ;
am:realizes ex:OrderFulfillment .
ex:Customer a am:BusinessActor ;
skos:prefLabel "Customer"@en .
# Application layer
ex:OrderManagement a am:ApplicationComponent ;
skos:prefLabel "Order Management"@en ;
am:composedOf ex:OrdersAPI, ex:OrdersDB, ex:OrdersWorker .
ex:OrdersAPI a am:ApplicationComponent ;
skos:prefLabel "Orders API"@en ;
skos:definition "Handles order lifecycle"@en .
ex:OrdersDB a am:ApplicationComponent ;
skos:prefLabel "Orders DB"@en .
ex:OrdersWorker a am:ApplicationComponent ;
skos:prefLabel "Orders Worker"@en .
ex:Order a am:DataObject ;
skos:prefLabel "Order"@en .
ex:OrdersAPI am:accesses ex:Order .
ex:OrdersAPI am:serves ex:OrdersWorker .
ex:OrdersAPI am:serves ex:PaymentSystem .
ex:PaymentSystem a am:ApplicationComponent ;
skos:prefLabel "Payment System"@en .
# Technology (Structurizr captures as string annotations)
ex:SpringBoot a am:SystemSoftware ; skos:prefLabel "Spring Boot 3.2"@en .
ex:PostgreSQL a am:SystemSoftware ; skos:prefLabel "PostgreSQL 15"@en .
ex:OrdersAPI am:assignedTo ex:SpringBoot .
ex:OrdersDB am:assignedTo ex:PostgreSQL .
# Ownership (Structurizr can't express this)
ex:OrdersTeam a bs:Group ; skos:prefLabel "Orders Team"@en .
ex:OrderManagement bs:ownedBy ex:OrdersTeam .
The Turtle version is longer but captures: business capabilities, typed relationships, data objects, technology assignments, and ownership. The Structurizr version captures none of these — it has labeled arrows and string annotations.
Repository structure¶
flowchart TD
subgraph repo["my-system/"]
src["src/ — Application source code"]
subgraph arch["architecture/"]
subgraph model["model/"]
om["orders-model.ttl — Architecture model"]
od["orders-decisions.ttl — Architecture decisions"]
end
subgraph views["views/"]
cs["context.sparql — System Context view query"]
cont["containers.sparql — Container view query"]
imp["impact.sparql — Impact analysis query"]
end
subgraph shapes["shapes/"]
pr["project-rules.ttl — Organization-specific SHACL rules"]
end
subgraph gen["generated/"]
docs["docs/ — Auto-generated Markdown"]
end
end
subgraph gh[".github/workflows/"]
yml["architecture.yml — CI/CD pipeline"]
end
mk["Makefile"]
end
CI/CD validation¶
The workflow on every PR that touches architecture files:
flowchart TD
A[".ttl file edited"] --> B["Syntax Check<br/>(Turtle parser)"]
B -->|pass| C["SHACL Validate"]
B -->|fail| X1["❌ Block merge"]
C -->|pass| D["SPARQL Views"]
C -->|violations| X2["⚠️ Report / block"]
D --> E["Generated views<br/>as build artifacts"]
subgraph SHACL["SHACL Validate"]
C1["Relationship validity"]
C2["Element constraints"]
C3["Custom governance"]
end
The tools are CI-platform-agnostic — validate.sh runs the Java-based Turtle syntax checker and SHACL validator, arq (Apache Jena) executes SPARQL queries. Wire them into GitHub Actions, GitLab CI, or whatever your team uses.
The key point: architecture governance runs on every commit, not quarterly in a review board. The review board still handles judgment calls — trade-offs, exceptions, strategic direction. The mechanical checks (ownership, relationship validity, required properties) are automated.
SPARQL views¶
In Structurizr, views are DSL declarations (container orderSystem { include * }). In this approach, views are SPARQL queries — more verbose but able to express things Structurizr can't.
Impact analysis (no Structurizr equivalent):
# What is affected if the Orders data object becomes unavailable?
PREFIX am: <https://meta.linked.archi/archimate3/onto#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX ex: <https://model.example.com/orders#>
SELECT ?level ?affected ?label WHERE {
{
?affected am:accesses ex:Order ;
skos:prefLabel ?label .
BIND("1-component" AS ?level)
} UNION {
?comp am:accesses ex:Order .
?comp am:assignedTo ?func .
?func am:realizes ?affected .
?affected a am:ApplicationService ;
skos:prefLabel ?label .
BIND("2-service" AS ?level)
} UNION {
?comp am:accesses ex:Order .
?comp am:assignedTo ?func .
?func am:realizes ?svc .
?svc am:realizes ?affected .
?affected a am:BusinessService ;
skos:prefLabel ?label .
BIND("3-business-service" AS ?level)
}
}
ORDER BY ?level
This traces from a data object through application components, through services, to business services — three layers of impact in one query.
SHACL governance-as-code¶
Beyond the ArchiMate relationship validity shapes (which validate the metamodel rules), you add project-specific governance:
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix am: <https://meta.linked.archi/archimate3/onto#> .
@prefix bs: <https://meta.linked.archi/backstage/onto#> .
# Every Application Component must have an owner
[] a sh:NodeShape ;
sh:targetClass am:ApplicationComponent ;
sh:property [
sh:path bs:ownedBy ;
sh:minCount 1 ;
sh:message "Application Component has no owner."@en ;
] .
# Every Application Service must realize a Business Service
[] a sh:NodeShape ;
sh:targetClass am:ApplicationService ;
sh:property [
sh:path am:realizes ;
sh:minCount 1 ;
sh:class am:BusinessService ;
sh:message "Application Service must realize at least one Business Service."@en ;
] .
Architecture governance runs on every commit. The review board handles judgment calls; the mechanical checks are automated.
The hybrid approach¶
You don't have to choose between Structurizr and Turtle. In practice:
- Developers maintain Structurizr DSL for day-to-day C4 diagrams (fast, familiar, great diagram output)
- The PlantUML/Structurizr converter extracts C4 elements into the RDF graph
- Enterprise architects maintain ArchiMate models in Archi (or write Turtle directly)
- The ArchiMate converter extracts into the same graph
- The unified graph connects C4 Software Systems to ArchiMate Application Components via shared identifiers
- SPARQL queries traverse across both — "for this C4 Container, what business capability does it support?"
Developers keep their workflow. Enterprise architects keep their precision. The graph connects them.
What you trade¶
- Diagram quality. Structurizr generates polished, interactive diagrams natively. Generating equivalent visual quality from RDF requires the static navigator or custom generators — functional but less polished.
- Terseness. Structurizr DSL is more concise for simple models. For a 3-container system, it's faster to write and easier to read.
- Learning curve. Structurizr DSL is learnable in hours. Turtle + SPARQL basics take a day or two.
- Ecosystem maturity. Structurizr has a focused, mature ecosystem (DSL, Lite, Cloud, CLI). The Linked.Archi tooling is newer and more modular.
What you gain: typed elements, typed relationships, cross-layer traceability, SHACL validation, SPARQL queryability, AI agent access, and composition with the full ontology ecosystem (decisions, quality attributes, TIME assessments, financial architecture).
References¶
- Architecture & Approach — Technical architecture of the ontology ecosystem
- Quick Start Guide — Create a metamodel from scratch
- Validation — SHACL validation details
- Two Positions: ArchiMate vs C4 — The broader debate
- Structurizr DSL Reference — For comparison
- SPARQL 1.1 Query Language — W3C
- SHACL Specification — W3C