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 repository's validation script, validate.sh, provides two levels of validation:
-
Turtle syntax — parses every
.ttlfile using the RDF4J Rio Turtle parser. Catches malformed IRIs, missing prefixes, unclosed strings, and other syntax errors. -
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 6.1.0, which reads RDF 1.2 (triple terms, rdf:reifies) and runs
SPARQL 1.2. The SDK is downloaded automatically on first run (~80MB) and cached; its version is set in one place,
which the validation and the repository's SPARQL query runner share.
Prerequisites¶
- Java 25+, which RDF4J 6 needs. When the
javaon thePATHis older, the scripts useJAVA_HOME, or on macOS ask/usr/libexec/java_homefor a JDK 25+, and stop with a message when there is none curlandunzip(for SDK download)- Python 3 for
--strengthand--ci(standard library only)
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 core-vis
./validate.sh --shacl archimate
./validate.sh --shacl archimate-derivation
./validate.sh --shacl archimate-principles
./validate.sh --shacl archimate-viewpoints
./validate.sh --shacl archimate4
./validate.sh --shacl archimate4-derivation
./validate.sh --shacl archimate4-principles
./validate.sh --shacl archimate4-viewpoints
./validate.sh --shacl c4
./validate.sh --shacl backstage
./validate.sh --shacl bpmn
./validate.sh --shacl uml
./validate.sh --shacl uml-instances
./validate.sh --shacl uml-publication
# SHACL validate all presets
./validate.sh --shacl all
Two of those presets differ in kind, and the difference decides what a clean report means.
archimate-viewpoints validates archimate3.2-onto.ttl — a vocabulary. It contains no views, so the
element and relationship conformance constraints match nothing and a clean report confirms only that
the shapes parsed and loaded. archimate4-viewpoints validates an instance model,
examples/archimate4-viewpoint-example.ttl, which has views, declared viewpoints, elements placed on
them and a relationship between two of them. There a clean report means the shapes ran and found
nothing wrong, which is a different and stronger claim.
That example is deliberately conformant and is the regression the preset guards: a shape that quietly
stops matching is invisible against data that could never match it. This is not hypothetical — two of
the 3.2 conformance constraints were written over arch:exposedInView, which no converter emitted, so
they returned zero rows and reported every view as conformant regardless of its contents. Prefer an
instance-model preset when adding conformance shapes of your own.
archimate4-principles is a third kind, and archimate-principles is the same for 3.2. The principle
shapes are advice — they warn or inform — and a model is expected to break some of them, so a clean
report is not the aim. Its example, examples/archimate4-principles-example.ttl, breaks each principle once and holds a few near misses
that must not be reported, and the preset passes when the report is exactly the list in
examples/archimate4-principles-example.expected.tsv: the shape, then the element it reports, one per
line. A principle that stops firing, or fires on something new, turns it red. RDF4J's ShaclSail
fails on any result whatever its severity, which is why shapes that only warn are tested this way.
The same check works on your own data and shapes:
archimate4-derivation runs the derivation rules rather than validating. RDF4J's SHACL engine supports
SHACL Core, SPARQL-based constraints and SPARQL targets, but none of SHACL-AF's rules or functions, so
the validation's rule runner, DeriveRules, runs each rule's CONSTRUCT as SPARQL — $this bound to the shape's targets,
as SPARQL 1.2, in SHACL-AF's order (sh:order) — until a round derives nothing new. The SHACL-AF
functions a rule calls (sh:SPARQLFunction) it registers with RDF4J, each evaluated as its query. Each
derived relationship comes as an asserted one does, the triple and a qualified relationship that reifies
it, which also carries the derivation's certainty and provenance; --output=<file> writes them as
Turtle.
DeriveRules checks each derivation: its source, target and class are those of the triple it reifies,
and its premises are two relationships in the model, neither only potential. The preset passes when what
examples/archimate4-derivation-example.ttl derives is exactly
examples/archimate4-derivation-example.expected.tsv, one line per derivation — the rule, the
certainty, the relationship and its two premises:
The example has a case of each of the twenty rules; one that a restriction of Appendix B.4 stops; two
that the relationship tables stop (PDR1 and PDR12 — the potential rules look the relationship up in
archimate4-relationship-tables.ttl, which the preset loads with the model, as anyone running the
rules must); one where a potential relationship is not a premise; and one where a relationship first
derived as potential is made valid. DeriveRules warns about a file the rules declare they need
(dcterms:requires) that the data does not hold.
archimate4-derivation-check checks derivations rather than deriving. The rules file also carries a
validation shape, am4deriv:DerivationRestrictions, for a derivation whatever made it: one that a
restriction of Appendix B.4 rules out is reported, with the restriction. The preset derives from the
derivation example (--output), and validates what it derived with that shape and the relationship
shapes, which check a derived relationship against the relationship tables as they check an asserted
one: it passes when there is nothing to report. Then it validates
examples/archimate4-derivation-check-example.ttl — derivations as another tool might record them,
three that B.4 rules out and three it allows — and passes when the report is exactly
examples/archimate4-derivation-check-example.expected.tsv.
When loading these shapes by hand, core/core-onto.ttl belongs in both the shapes list and the
data list, for two separate reasons. RDF4J resolves the rdfs:subClassOf closure for sh:targetClass
and sh:class from the shapes graph, so without it nothing fires on an arch:Diagram when the shape
targets arch:View. And a sh:sparql constraint is evaluated against the data graph, so a
rdfs:subClassOf* step inside one resolves nothing unless the hierarchy it walks is loaded as data
too.
# SHACL validate custom data against custom shapes
./validate.sh --shacl mydata.ttl core-shapes.ttl my-shapes.ttl
# Check that every namespace in docgen-index.yaml is declared by a source file
./validate.sh --registration
# ArchiMate's order of relationship strength: the taxonomies against the derivation rules
./validate.sh --strength
# ArchiMate's order of proposal (4.0 and 3.2): every scoped exception permitted by the relationship tables
./validate.sh --proposal-order
# Full CI run (syntax + registration + references + strength + proposal order + 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) |
Asset Registration — --registration¶
Syntax and SHACL check the content of an asset. This stage checks that the asset is wired into the
site: every namespace listed in the documentation generator's asset index must be declared as
vann:preferredNamespaceUri by some source .ttl, and where a file declares both, that namespace must
match its own @prefix :.
It exists because the declaration drifted silently across 22 assets — every
*/deliverable-templates#, */reference-data# and */presentation-contexts# document, plus the five
example models. They declared their namespace only through @prefix :, and both the docgen index matcher
and copy-ttl-sources.sh accept that as a fallback, so the documentation pages rendered and the
ontology.ttl downloads resolved. Nothing looked wrong.
What broke was quieter. generate-llms-txt.sh discards any asset with no dcterms:title, so none of the
22 reached llms.txt or llms-full.txt, none contributed a row to the canonical namespace table, and
inject-jsonld.sh emitted a schema.org Dataset with no identifier. A missing header is invisible in
the place you would look for it and only shows up in the generated artefacts.
Prefix collisions are reported but do not fail the run. Two worked examples legitimately both use acme:
for different fictional namespaces.
═══ Asset Registration ═══
! prefix "acme" is claimed by more than one namespace (not an error)
Registration: 160 namespaces checked, 0 errors.
What an asset needs, and where to put it. Every documented asset needs
vann:preferredNamespaceUri, vann:preferredNamespacePrefix and dcterms:title. They go on the root
resource the file already has — not in an owl:Ontology header bolted onto a file that is not an
ontology.
| File | Root resource | Where the metadata goes |
|---|---|---|
*-onto.ttl, *-shapes.ttl, *-metamodel.ttl |
owl:Ontology |
the ontology header, with owl:imports and owl:versionIRI |
*-tax.ttl, *-reference-data.ttl, *-reference-models.ttl, *-presentation-contexts.ttl |
skos:ConceptScheme |
on the ConceptScheme itself |
*-deliverable-templates.ttl |
arch:DeliverableTemplate individuals |
on the first template instance |
| example models | arch:Model |
on the arch:Model individual |
Reference data, presentation contexts, taxonomies and deliverable templates are controlled vocabularies
and instance data, not ontologies. Typing them owl:Ontology to give the metadata a home misstates what
they are, and for arch:Model and arch:DeliverableTemplate it actively breaks them —
MODEL-ASSET-SUPPORT.md classifies those two asset types by the absence of that triple.
owl:imports and owl:versionIRI are OWL ontology properties and do not belong on a ConceptScheme, a
template or a model. owl:versionInfo is an annotation property, so use that to carry the version.
Note what --registration can and cannot tell you: it checks that the declaration exists. It cannot
tell you that you put it on an invented resource.
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)"]
QRS["QualifiedRelationshipShape:<br/>exactly one arch:source + arch:target"]
COS["ConceptOwnerShape:<br/>arch:conceptOwner must be a Stakeholder"]
core --> 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/>13 per-relationship-type shapes + identity, naming, status, property-placement, and vocabulary-closure 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
--shacl archimate-derivation runs the 3.2 derivation rules (DR1-DR8, PDR1-PDR12) over
examples/archimate3.2-derivation-example.ttl — the 4.0 derivation example's cases in 3.2's element
types — and passes when they derive exactly examples/archimate3.2-derivation-example.expected.tsv; then
it validates what they derived, with the example, against the validation shape for derivations and the
relationship shapes, as archimate4-derivation-check does for 4.0.
The ShaclSail validates the data against all loaded shapes on commit. Violations are reported as a SHACL validation report in Turtle format.
Label rules are notation-owned¶
core-shapes.ttl carries no label shape. arch:Element means "a node in a model", and several
notations have nodes that are legitimately unnamed — BPMN gateways and events, ArchiMate junctions
— so requiring a label on every element is not a sound cross-notation rule. Each metamodel states
its own naming rule in its own shapes: bpmnsh:RequiredNameShape,
am4elsh:ArchiMateElementShape, c4sh:C4ElementLabelShape, bssh:BackstageElementLabelShape and so on.
Backstage additionally requires bs:name on the same seven element classes, via
bssh:BackstageEntityIdentityShape — a separate rule from the label, because bs:name is the catalog
identity half of the entity reference (kind:namespace/name), not a human-readable label.
A metamodel shape must not target arch:Element for this. In a merged multi-notation graph that
would apply one notation's naming policy to all the others.
Every one of those shapes asserts sh:datatype rdf:langString, so a plain string is rejected:
ex:t1 a bpmn:ServiceTask ; skos:prefLabel "Charge card" . # FAILS — no language tag
ex:t2 a bpmn:ServiceTask ; skos:prefLabel "Charge card"@en . # passes
A notation-native name attribute such as bpmn:name does not satisfy them either. Converters emit
both the source attribute and a language-tagged skos:prefLabel. See
DD-24.
Note that sh:severity does not affect the outcome. Under SHACL a report conforms only when it
contains no results at all, so downgrading a shape to sh:Warning reclassifies its output without
making the graph conform — RDF4J's ShaclSail does not distinguish severities either. To stop a
rule from firing, change what it targets rather than its severity.
sh:severity does not reach a property shape¶
A result takes the severity of the shape declaring the constraint it violated, and a sh:property
block is a shape in its own right. Declaring sh:severity sh:Warning on the node shape leaves the
property shapes nested under it at the sh:Violation default, so the constraint reports as an error
while the file says it is a warning. Declare the severity in both places when the two should agree:
:ViewMustDeclareViewpointShape
a sh:NodeShape ;
sh:targetClass arch:View ;
sh:severity sh:Warning ; # governs constraints on the node shape itself
sh:property [
sh:path arch:viewConformsToViewpoint ;
sh:minCount 1 ;
sh:severity sh:Warning ; # without this, the result is sh:Violation
] ;
.
sh:sparql behaves differently, because the constraint is stated on the node shape and therefore
takes that shape's severity. This is why the viewpoint shapes repeat sh:severity inside every
sh:property while their SPARQL-based shapes declare it once, at node level.
SHACL Profiles¶
| Profile | Data File | Shapes |
|---|---|---|
core-vis |
core-vis-onto.ttl + diagram-example.ttl |
core-shapes.ttl + core-vis-shapes.ttl + core-vis-onto.ttl + core-onto.ttl + archimate3.2-onto.ttl (the last three for the subclass closure) |
archimate |
archimate3.2-onto.ttl |
core-shapes.ttl + archimate3.2-relationship-shapes.ttl (generated) + archimate3.2-element-shapes.ttl |
archimate-derivation |
core-onto.ttl + archimate3.2-onto.ttl + archimate3.2-relationship-tables.ttl + archimate3.2-derivation-example.ttl |
archimate3.2-derivation-rules.ttl, run by DeriveRules; passes on the expected derivations, then what they derive passes the validation shape for derivations and the relationship shapes |
archimate-principles |
core-onto.ttl + archimate3.2-onto.ttl + archimate3.2-principles-example.ttl |
core-shapes.ttl + archimate3.2-principle-shapes.ttl (23 principle shapes) + archimate3.2-relationship-shapes.ttl + archimate3.2-onto.ttl + core-onto.ttl; passes on the expected results |
archimate-viewpoints |
archimate3.2-onto.ttl |
core-shapes.ttl + archimate3.2-viewpoint-shapes.ttl + archimate3.2-viewpoints.ttl |
archimate4 |
core-onto.ttl + archimate4-onto.ttl + the 4.0 viewpoint, principles and derivation examples, NordFreight and ACME |
core-shapes.ttl + archimate4-relationship-shapes.ttl (generated) + archimate4-onto.ttl + core-onto.ttl |
archimate4-derivation |
core-onto.ttl + archimate4-onto.ttl + archimate4-relationship-tables.ttl + archimate4-derivation-example.ttl |
archimate4-derivation-rules.ttl, run by DeriveRules; passes on the expected derivations |
archimate4-derivation-check |
what the rules derive from archimate4-derivation-example.ttl, with its data; archimate4-derivation-check-example.ttl |
am4deriv:DerivationRestrictions (in archimate4-derivation-rules.ttl) and the relationship shapes; passes when the derived relationships pass both and the check example gives the expected results |
archimate4-principles |
core-onto.ttl + archimate4-onto.ttl + archimate4-principles-example.ttl |
core-shapes.ttl + archimate4-principle-shapes.ttl (22 principle shapes) + archimate4-onto.ttl + core-onto.ttl; passes on the expected results |
archimate4-viewpoints |
core-onto.ttl + archimate4-onto.ttl + archimate4-viewpoints.ttl + archimate4-viewpoint-example.ttl |
core-shapes.ttl + archimate4-viewpoint-shapes.ttl + archimate4-viewpoints.ttl + archimate4-onto.ttl + core-onto.ttl |
c4 |
c4-onto.ttl |
core-shapes.ttl + c4-shapes.ttl |
structurizr |
structurizr-onto.ttl |
core-shapes.ttl + c4-shapes.ttl + structurizr-shapes.ttl |
backstage |
backstage-onto.ttl + payflow-model.ttl |
core-shapes.ttl + core-onto.ttl + backstage-onto.ttl + backstage-shapes.ttl |
bpmn |
linkedarchi-bpmn-onto.ttl |
core-shapes.ttl + linkedarchi-bpmn-shacl.ttl + linkedarchi-bpmn-infra-shacl.ttl |
bpmn-lite |
linkedarchi-bpmn-lite-onto.ttl |
core-shapes.ttl + linkedarchi-bpmn-lite-shacl.ttl |
bpmn-di |
linkedarchi-bpmndi-onto.ttl |
core-shapes.ttl + BPMNDI + DI + DC shapes |
uml |
uml-onto.ttl + uml-tax.ttl |
core-shapes.ttl + uml-shapes.ttl + uml-onto.ttl (for the subclass closure sh:targetClass needs) |
uml-instances |
uml-onto.ttl + uml-example.ttl |
core-shapes.ttl + uml-shapes.ttl + uml-onto.ttl + core-onto.ttl |
uml-publication |
uml-onto.ttl + uml-example.ttl |
as uml-instances plus uml-publication-shapes.ttl |
arch-decision |
arch-decision-onto.ttl + decisions-example.ttl |
core-shapes.ttl + arch-decision-shapes.ttl |
arch-processes |
archProcesses.ttl + acme-tax.ttl + acme-model.ttl |
core-shapes.ttl + archProcesses-shapes.ttl |
leanix |
leanix-docs-example-model.ttl |
core-shapes.ttl + core-onto.ttl + archProcesses.ttl + leanIX-v4-onto.ttl + leanIX-v3-onto.ttl + leanIX-shapes.ttl |
Every profile loads core-shapes.ttl, so the labelling contract above applies in all of them. A
new metamodel with shapes should be registered here as a profile — bpmn-lite went unvalidated for
a while, and in that time its shapes came to require arch:name, a property declared nowhere in the
repository.
Validate against instances, not only against the ontology¶
Most profiles above use a metamodel's own ontology as the data graph. That exercises the shapes constraining enumerated values, because the ontology carries the value individuals — but it reaches no model instance at all, since the rest of the file is class declarations and shapes fire on instances.
This is a real blind spot, not a technicality. umlsh:NamedElementShape required a skos:prefLabel on
every uml:NamedElement for two releases; the uml profile passed throughout, because the ontology
contains no uml:NamedElement instance for it to fire on. The defect surfaced only when a converted
state diagram reported a violation on every unlabelled transition, which is the ordinary way to write
one. See UML-DD-14.
Profiles that pair an ontology with a reference example — core-vis, archimate-principles, archimate4, archimate4-principles, archimate4-viewpoints, uml-instances,
uml-publication, arch-decision, arch-processes, leanix, backstage — do not have that blind spot.
Prefer this shape for a new profile, and make the example include the cases that are correct but look wrong:
an unlabelled transition, a grouping box with no model element behind it, a reply message with no name. Those
are what an over-strict shape reports.
backstage was the most recent to switch, and the switch is instructive about what the blind spot hides.
Under the ontology-as-data profile it passed while nothing checked entity identity, naming, relationship
endpoints, value-set membership or property placement — every one of those shapes targets instances. Pointing
it at payflow-model.ttl immediately produced findings on data that had been considered good, including four
APIs with no spec.definition.
Two mechanics are worth copying when you do this:
- The ontology goes in both positions. As shapes, because RDF4J's
ShaclSailreadsrdfs:subClassOffrom the shapes graph for its subclass reasoning, and without itsh:class arch:Elementdoes not resolve for abs:Component. As data, because DD-23 value vocabularies are named individuals declared in the ontology andsh:class bs:ResourceTypewants anrdf:typetriple in the data graph.owl:importsdoes not help — RDF4J does not follow it. - Order the data files with the vocabulary first.
ShaclValidatorcommits each data file in its own transaction andShaclSailvalidates per commit, so a vocabulary loaded after the model that references it is not yet visible when the model is checked. The symptom is every correct value-individual reference being reported at once.
The diagram layer needs core-vis-shapes¶
core-shapes.ttl targets no core-vis class, and neither does any notation's shapes document. Until
core-vis-shapes.ttl was published, nothing checked the diagram layer at all: a corpus of 447 malformed
diagram resources across 20 diagrams — label nodes typed arch-vis:ArchNode, carrying no
arch-vis:archElement and no arch-vis:view, with IRIs minted outside the model namespace — validated
clean.
Load core-vis-shapes.ttl whenever the data contains diagram interchange. The two rules that carry the
weight are archvissh:ArchNodeShape (a node claiming to stand for a model element must say which one)
and archvissh:PlacedDiagElementShape (a shape or edge belongs to exactly one view). It carries no label
rule, for the same reason core-shapes does not.
GitLab CI Integration¶
The .gitlab-ci.yml runs validate.sh --ci on every push that changes .ttl files. It runs on the
public Eclipse Temurin JDK 25 image, with nothing of the project's baked in: the validation tools
are compiled from the checkout on each run, so CI runs the code in the commit, and the RDF4J 6.1.0
SDK is downloaded at the version the repository pins and checked against its SHA-256. GitLab's
cache keeps the download between pipelines; a version change starts a fresh cache, so there is no
image to rebuild. Locally, the script downloads the SDK on the first run and keeps it.
The CI job:
1. Compiles the Java tools (TurtleSyntaxCheck, ShaclValidator, DeriveRules) from the checkout
2. Runs syntax validation on every .ttl file in the repository
3. Runs SHACL validation on every profile listed above
4. Reports pass/fail with proper exit codes
Adding a New SHACL Profile (Custom Governance Rules)¶
To add validation for a new metamodel:
-
Create a shapes file (e.g.,
my-metamodel-shapes.ttl) that importscore-shapes. Paste this as a starting point: -
Add per-relationship-type shapes with domain/range constraints
-
Add a new case to the
run_profile()function invalidate.sh: -
Add the profile name to the
PROFILESvariable
Files¶
The shapes and the reference examples the profiles use; the validation tools themselves are described in the scripts README, in the repository.
| File | Purpose |
|---|---|
.gitlab-ci.yml |
CI pipeline configuration |
core/core-shapes.ttl |
Base SHACL shapes for all metamodels |
core/core-vis-shapes.ttl |
SHACL shapes for the diagram interchange and visual notation layer |
modelingLanguages/uml/uml-shapes.ttl |
UML conformance shapes — no naming requirement, see UML-DD-14 |
modelingLanguages/uml/uml-publication-shapes.ttl |
Opt-in UML naming rules for models that will be published |
examples/diagram-example.ttl |
Reference diagram instance, data graph of the core-vis profile |
examples/uml-example.ttl |
Reference UML instance, data graph of the uml-instances and uml-publication profiles |
modelingLanguages/archimate/3.2/archimate3.2-relationship-shapes.ttl |
SHACL shapes for relationship source-target pairs, generated automatically from the relationship matrix |
modelingLanguages/archimate/3.2/archimate3.2-relationship-tables.ttl |
The same tables as lookups, for the potential derivation rules — generated with the shapes |
modelingLanguages/archimate/3.2/archimate3.2-element-shapes.ttl |
SHACL shapes for element/metamodel pattern constraints |
modelingLanguages/archimate/3.2/archimate3.2-principle-shapes.ttl |
SHACL shapes for architecture governance principles |
modelingLanguages/archimate/3.2/archimate3.2-derivation-rules.ttl |
ArchiMate 3.2 derivation rules (DR1-DR8, PDR1-PDR12) |
modelingLanguages/archimate/4.0/archimate4-relationship-shapes.ttl |
ArchiMate 4.0 relationship shapes, generated automatically from the relationship matrix |
modelingLanguages/archimate/4.0/archimate4-relationship-tables.ttl |
The 4.0 tables as lookups, generated with the shapes |
modelingLanguages/archimate/4.0/archimate4-derivation-rules.ttl |
ArchiMate 4.0 derivation rules (DR1-DR8, PDR1-PDR12) |