Skip to content

Validate Architecture Models with SHACL — Ontology Syntax and Governance Checks

Architecture governance is only useful if it's enforced. Most organizations have rules like "every application must have an owner" or "cross-domain relationships must be typed" — but these live in documents nobody reads, and violations are only caught in quarterly reviews (if ever).

SHACL shapes turn those rules into executable constraints that run automatically. This guide covers how to validate Linked.Archi ontology files for syntax correctness and SHACL conformance — and how to add your own governance rules to the pipeline.


Overview

The validation script (.scripts/validate.sh) provides two levels of validation:

  1. Turtle syntax — parses every .ttl file using the RDF4J Rio Turtle parser. Catches malformed IRIs, missing prefixes, unclosed strings, and other syntax errors.

  2. SHACL conformance — loads ontology data into an RDF4J ShaclSail and validates it against the SHACL shapes. Catches missing required properties, wrong types, cardinality violations, and domain/range constraint violations.

Both levels use Eclipse RDF4J 4.3.14. The SDK is downloaded automatically on first run (~50MB, cached in .scripts/.lib/).

Prerequisites

  • Java 17+ (tested with OpenJDK 21)
  • curl and unzip (for SDK download)

Usage

Run these commands to validate your Turtle files:

# Syntax-check all .ttl files in the repo
./validate.sh

# Syntax-check a single file
./validate.sh --syntax path/to/file.ttl

# SHACL validate a preset profile
./validate.sh --shacl archimate
./validate.sh --shacl archimate-derived
./validate.sh --shacl archimate-principles
./validate.sh --shacl c4
./validate.sh --shacl backstage
./validate.sh --shacl bpmn

# SHACL validate all presets
./validate.sh --shacl all

# SHACL validate custom data against custom shapes
./validate.sh --shacl mydata.ttl core-shapes.ttl my-shapes.ttl

# Full CI run (syntax + all SHACL profiles)
./validate.sh --ci

Exit Codes

Code Meaning
0 All validations passed
1 One or more validations failed
2 Usage error (wrong arguments)

SHACL Validation Architecture — Two-Layer Shapes

Validation uses a two-layer shapes architecture:

flowchart TD
    core["core-shapes.ttl<br/>(base contract — applies to all metamodels)"]
    MCS["ModelConceptShape:<br/>every ModelConcept must have skos:prefLabel"]
    QRS["QualifiedRelationshipShape:<br/>exactly one arch:source + arch:target"]
    COS["ConceptOwnerShape:<br/>arch:conceptOwner must be a Stakeholder"]

    core --> MCS & QRS & COS

    amrel["archimate3.2-relationship-shapes.ttl<br/>11 qualified + 62 unqualified shapes"]
    amelem["archimate3.2-element-shapes.ttl<br/>24 shapes: structural integrity, metamodel patterns"]
    amprin["archimate3.2-principle-shapes.ttl<br/>20 shapes: governance principles"]
    amderiv["archimate3.2-derivation-rules.ttl<br/>SHACL Rules: DR1-DR8 + PDR1-PDR12"]
    c4s["c4-shapes.ttl<br/>4 per-relationship-type shapes"]
    bss["backstage-shapes.ttl<br/>7 per-relationship-type shapes"]

    core -->|"imports"| amrel & amelem & amprin & amderiv & c4s & bss

When you run --shacl archimate, the script loads: 1. core-shapes.ttl into the SHACL shapes graph 2. archimate3.2-relationship-shapes.ttl into the SHACL shapes graph 3. archimate3.2-element-shapes.ttl into the SHACL shapes graph 4. archimate3.2-onto.ttl as data

When you run --shacl archimate-derived, it loads all of the above plus: 5. archimate3.2-derivation-rules.ttl (DR1-DR8, PDR1-PDR12 SHACL Rules)

The ShaclSail validates the data against all loaded shapes on commit. Violations are reported as a SHACL validation report in Turtle format.

SHACL Profiles

Profile Data File Shapes
archimate archimate3.2-onto.ttl core-shapes.ttl + archimate3.2-relationship-shapes.ttl (generated) + archimate3.2-element-shapes.ttl
archimate-derived archimate3.2-onto.ttl All archimate shapes + archimate3.2-derivation-rules.ttl (DR1-DR8, PDR1-PDR12)
archimate-principles archimate3.2-onto.ttl core-shapes.ttl + archimate3.2-principle-shapes.ttl (20 governance principle shapes)
c4 c4-onto.ttl core-shapes.ttl + c4-shapes.ttl
backstage backstage-onto.ttl core-shapes.ttl + backstage-shapes.ttl
bpmn linkedarchi-bpmn-onto.ttl core-shapes.ttl

GitLab CI Integration

The .gitlab-ci.yml runs validate.sh --ci on every push that changes .ttl files. It uses a custom Docker image (validator) with Java 21, RDF4J 4.3.14 SDK, and pre-compiled validators baked in — no download or compilation at runtime.

Building the Validator Image

docker build --platform linux/amd64 --no-cache \
  -t linked-archi/validator .scripts/

Push to your GitLab container registry:

docker tag linked-archi/validator registry.gitlab.com/<namespace>/linked-archi-meta/validator:latest
docker push registry.gitlab.com/<namespace>/linked-archi-meta/validator:latest

Why --platform linux/amd64: Apple Silicon Macs build ARM images by default. GitLab shared runners are x86_64 and will reject ARM images.

Why --no-cache: Prevents Docker from reusing cached ARM layers when switching platforms.

The image contains: - Eclipse Temurin JDK 21 - RDF4J 4.3.14 SDK (/opt/rdf4j/lib/) - Pre-compiled TurtleSyntaxCheck.class and ShaclValidator.class (/opt/rdf4j/build/)

The validate.sh script detects the pre-built paths via RDF4J_LIB and RDF4J_BUILD environment variables and skips download/compilation entirely. For local development without Docker, the script falls back to downloading the SDK on first run (cached in .scripts/.lib/).

The CI job: 1. Downloads the RDF4J SDK (cached after first run) 2. Compiles the Java validation tools (cached) 3. Runs syntax validation on all 69+ .ttl files 4. Runs SHACL validation on all 5 profiles 5. Reports pass/fail with proper exit codes

Adding a New SHACL Profile (Custom Governance Rules)

To add validation for a new metamodel:

  1. Create a shapes file (e.g., my-metamodel-shapes.ttl) that imports core-shapes. Paste this as a starting point:

    owl:imports <https://meta.linked.archi/core-shapes#> ;
    

  2. Add per-relationship-type shapes with domain/range constraints

  3. Add a new case to the run_profile() function in validate.sh:

    my-metamodel)
        validate_shacl \
            "$REPO_ROOT/path/to/my-metamodel-onto.ttl" \
            "$core_shapes" \
            "$REPO_ROOT/path/to/my-metamodel-shapes.ttl"
        ;;
    

  4. Add the profile name to the PROFILES variable

Files

File Purpose
.scripts/validate.sh Main validation script
.scripts/TurtleSyntaxCheck.java RDF4J-based Turtle parser wrapper
.scripts/ShaclValidator.java RDF4J ShaclSail-based SHACL validator
.scripts/.lib/ Auto-downloaded RDF4J SDK (gitignored)
.scripts/.build/ Compiled Java classes (gitignored)
.gitlab-ci.yml CI pipeline configuration
core/core-shapes.ttl Base SHACL shapes for all metamodels
modelingLanguages/archimate/archimate3.2-relationship-shapes.ttl Generated SHACL shapes for relationship source-target pairs
modelingLanguages/archimate/archimate3.2-element-shapes.ttl SHACL shapes for element/metamodel pattern constraints
modelingLanguages/archimate/archimate3.2-principle-shapes.ttl SHACL shapes for architecture governance principles
modelingLanguages/archimate/archimate3.2-derivation-rules.ttl ArchiMate derivation rules (DR1-DR8, PDR1-PDR12)
.scripts/generate-archimate-shapes.py Generator for relationship shapes from XML matrix