ADR 0011 — Which reality a diagram describes¶
Status: Accepted, implemented
Implementation summary:
architectureState:is parsed into anArchitectureStatewith three values:baseline,target,transitional. An unrecognised value fails the run.- Values come from the individuals the core ontology already declares —
arch:Baseline,arch:Target,arch:Transitional— and reach the graph asarch:architectureStatein the semantic graph. No term is minted. - The checked field is available at model and view level in a diagram index, and in a
.pumlas'!la-architecture-state. Element level is reachable only through the generic annotation route. - Nothing is inherited and nothing is defaulted. Each level describes itself; an entry that says nothing gets no triple.
- A
.pumland the index may both declare one, and a disagreement fails the run rather than being resolved by precedence. - ArchiMate reaches views, elements and relationships through
object-properties:in--type-mapping, and has no model-level route. Structurizr has none.
Scope: how a model, view or element says which temporal reality it describes, and why that is a different vocabulary from publication status.
Depends on: ADR 0002 for the model/view split this attaches facts to.
Related: ADR 0004 governs status: — whether a diagram has been
released. The two are the most confusable pair in the index and the distinction is load-bearing;
see The confusion this exists to prevent.
ADR 0006 reached the same answer for a different enumerated
vocabulary, and this ADR repeats one of its findings: the term already existed upstream and the
converter was not emitting it.
Context¶
A repository that plans anything holds the same architecture more than once. How it is today. How it is meant to end up. The plateaus in between, each of which is a real state something ran in.
Nothing in the converters said which. The consequence is not a missing label, it is a merged graph: a target-state component and its baseline counterpart are minted under the same IRI if they carry the same id, so one resource ends up asserting both that it depends on the system being retired and that it depends on its replacement. A query for "what runs today" returns things nobody has built. A query for "what are we building" returns things already decommissioned. Neither result is distinguishable from a correct one.
Meanwhile core-onto.ttl had carried the vocabulary the whole time — arch:ArchitectureState as a
class, arch:architectureState as an owl:ObjectProperty with rdfs:range :ArchitectureState, and
three individuals with definitions and skos:altLabels. Its own skos:example shows exactly the
intended use, with a Model as the subject:
ex:CurrentPaymentArch a arch:Model ;
arch:architectureState arch:Baseline ;
arch:modelRepresentsSystem ex:PaymentPlatform .
ex:TargetPaymentArch a arch:Model ;
arch:architectureState arch:Target ;
arch:modelRepresentsSystem ex:PaymentPlatform .
So this is not a vocabulary decision. It is the decision about how an author states the fact, at which levels, and how strictly the converter reads it.
The confusion this exists to prevent¶
status: and architectureState: are two enumerated vocabularies, both on a view, both about
"state". They are not variants of one field:
| Field | Question | Subject | Reaches |
|---|---|---|---|
status: |
has this diagram been released? | the diagram, as a document | adms:status, provenance graph |
architectureState: |
which reality does it describe? | the architecture | arch:architectureState, semantic graph |
The case that settles it: a target-state diagram can be status: publish. Finished, reviewed,
published, and describing something that does not exist. There is no combination of the two that is
contradictory, which is the test for whether two vocabularies are really one.
Folding them into a single enum was considered and rejected on that basis. It would have produced
states like published-target whose cardinality is the product of two independent questions, and a
consumer wanting "everything current" would have to enumerate the combinations rather than match one
triple pattern.
Because the two are confusable in exactly one direction — an author reaching for draft under
architectureState: — that specific mistake is refused with a message naming the other field.
Decision drivers¶
A wrong answer here is worse than a missing one. Reporting planned architecture as operational sends someone to look for a system that was never built; reporting operational architecture as planned gets a running system decommissioned. Both are unrecoverable from the graph alone, because the graph looks correct. Every choice below leans towards asserting nothing over asserting a guess.
The levels are genuinely different resources, and a migration model uses all of them. A model representing a migration holds a baseline view and a target view. Any rule that makes one level's value stand in for another's breaks that case, which is the main case.
The ontology is open and the field cannot be. :ArchitectureState has three declared individuals
and no owl:oneOf, and core-shapes.ttl constrains the property not at all. So the vocabulary
permits a fourth state while the converter must reject a typo. Those requirements conflict, and the
resolution has to be explicit rather than accidental.
Options considered¶
A. A string property, or a boolean¶
arch:architectureState "target", or arch:isTargetState true.
Rejected for the reasons ADR 0006 sets out at length and
DD-23 lists as an anti-pattern: a string
carries no label, no definition, no translation and no join, and a typo in it is indistinguishable
from a new value. The boolean is worse — it cannot express transitional at all, and there may be
several transitional plateaus on one path.
B. The generic annotation route only¶
links: { arch:architectureState: arch:Target }, and no first-class field.
This works, and it is the route ArchiMate is left with. Its cost is that a generic predicate has no
set of values to check against, so arch:Targt resolves to a syntactically valid IRI that nothing
defines and the converter emits it without complaint. The failure is silent at conversion and at
query time: a filter on arch:Target returns one row fewer, with no error anywhere.
For an open-ended vocabulary that trade is right, and most extension data is open-ended. For a set of three it is not.
C. A checked first-class field, with the generic route retained¶
architectureState: reads a token from a closed set and fails the run on anything else. The generic
route stays available for the levels the field does not reach, and for a consumer who has minted a
fourth state the ontology permits.
Costs one more concept for authors to hold, and leaves a deliberate validation asymmetry between levels. Buys a typo that fails on the run that introduced it.
Decision¶
Adopt C, with seven rules.
-
Three values, from the individuals core already declares.
baseline→arch:Baseline,target→arch:Target,transitional→arch:Transitional. Nothing is minted. -
The spellings the ontology itself records are accepted.
as-is,current,current-state;to-be,future,future-state;transition,intermediate,intermediate-state. Every one is askos:altLabelon the corresponding individual incore-onto.ttl, so this is honouring the vocabulary rather than inventing synonyms — the same reasoning ADR 0004 applied topublishedandobsolete. Case and the choice of-,_or a space are normalised, soAs Isandas-isare one value. -
An unrecognised value fails the run, naming the entry, the file and the accepted set. A value that is a publication status is refused with a message pointing at
status:, because that is the one mistake the two vocabularies invite. -
Not gated behind
--emit-extension-data. Which reality a diagram describes is a property of the diagram, like its status — not data borrowed from a modelling tool's extension point, which is what that flag is about. -
The triple goes to the semantic graph, not provenance. Two reasons pointing the same way. It says which reality the diagram describes rather than how the file came to be, so it is not provenance in the sense the rest of the index metadata is. And the same predicate can arrive through the generic route, which lands in the semantic graph — one predicate appearing in two named graphs depending on which route wrote it would silently halve the results of any query scoped to a single graph. This is where it parts company with
adms:status, which is provenance in exactly that sense. -
Nothing is inherited. Each level describes itself. A model saying
baselineis a claim about the model, not a default for its views. -
Nothing is defaulted. An entry that declares no state gets no triple.
Why not inherit, when status: does¶
This is the one place the two vocabularies are deliberately asymmetric, and the asymmetry needs defending because a reader will read it as an inconsistency.
status: cascades from the model to its views because the ontology puts publication status on the
diagram: a model is not a document and has no release. So a model-level status: can only sensibly
be shorthand for "all my views", and it is emitted on each of them with the model never being the
subject. One line retires a model.
arch:architectureState is defined on a Model as readily as on a view, and the ontology's own example
uses a Model. So the model can be the subject, and if it can be then a value written there is a
claim rather than shorthand.
The case that decides it is the migration model: one model, a baseline view and a target view. Under a
cascade the model's baseline would land on the target view too, and the wrong answer would be exactly
the dangerous one from the decision drivers — a planned architecture asserted as operational. The
configuration that breaks is not an edge case, it is what a migration model is.
A consumer asking about a view that says nothing of its own may follow dct:isPartOf to its model and
decide for itself. That is a reading convenience, and it stays on the reading side where it can be
inspected, rather than being baked into the graph where it cannot be distinguished from something an
author wrote.
Why not default to baseline¶
Tempting, because most diagrams in most repositories do describe today.
Rejected because a repository that has never distinguished baseline from target is not thereby claiming
everything is as-is — it is claiming nothing, and the two are different facts. Defaulting would put a
claim in the graph that no author made, on every resource in every model ever converted, and it would
do so in the direction that gets running systems mistaken for settled ones. It is also unrecoverable:
once emitted, a defaulted arch:Baseline is indistinguishable from a declared one.
This is the same rule ADR 0004 applies to an absent status:, for the same reason, and it is worth
noting the two differ in what silence means for selection: an entry with no status: is still
published, because a run has to do something. An entry with no architectureState: needs no such
fallback, because saying nothing is a complete and correct outcome.
Why a disagreement is refused rather than resolved¶
A .puml can declare the state in the file and the index can declare it for the same view. When both
do and they differ, the run fails.
Both precedence rules were considered and both are wrong for this vocabulary. In-file wins is
defensible — the author is closest to the content. Index wins is defensible — the publisher owns the
publishing decision, which is how ADR 0001 assigns status:. Neither survives contact with the values:
baseline and target are opposite claims about one diagram, so whichever rule applies, half the time it
publishes a future architecture as current or the reverse, silently.
This is where it differs from a prefix declared twice, where the two declarations are the same kind of statement and the more specific one is evidently meant. Here there is no sense in which either declaration is more specific. So neither wins, and the author is told to remove one.
Where the field does not reach¶
Element level is reachable only through the generic route, in every notation. ArchiMate is on the generic route at every level, and has no model-level route at all because it has neither a diagram index nor property promotion on the model. Structurizr has no route.
These are recorded as gaps rather than justified, because none of them is a decision — they are the shape of what has been built. Their consequence is stated below.
Consequences¶
- As-is and to-be separate with one triple pattern, and a migration model can hold both in one model without the two contradicting each other.
- The vocabulary is legible without knowing this project, because it is the ontology's own, and a
consumer can follow
arch:architectureStateintoskos:definitionandskos:altLabel. - The converter is stricter than the ontology, on purpose.
:ArchitectureStatehas noowl:oneOf, so a fourth state is permitted upstream; the checked field refuses one. The generic route is what keeps that openness usable, which makes its lack of validation a feature at the edges and a hazard in the middle. - Element-level values are unvalidated in every notation.
arch:Targton an element is emitted as a link to an undefined IRI with no warning, and the resulting query returns one row fewer with no error. This is the sharpest consequence of the decision and the leading candidate in the open questions. - ArchiMate cannot state it on a model, so a model-wide claim for an ArchiMate model has to be made in the aggregation repository. Consistent with ADR 0001 decision 2, but worth knowing before designing around it.
- A view's state does not reach the elements in it. "Which applications are in the target architecture" is therefore a two-hop question — view to its contents — not a filter on elements. Authors who want the one-hop answer must state it per element, on the unvalidated route.
- Consumers must treat unstated as unknown, not baseline. A count of baseline resources is a count of declared baseline resources, and coverage has to be reported beside it or the number misleads.
- The
arch:prefix already appears in every output, so no new namespace is registered.
Open questions¶
- Should element scope get a checked route? The parsing exists — the index already reads
elements:and resolves author-written names onto minted IRIs, refusing an ambiguous one. What is missing is only value validation, and there are exactly two places it would go:ExtensionAssertionEmitter.emitOne, which every route funnels through, and the BPMN converter's ownExtensionEmitter, which bypasses it because an XML qualified name needs no CURIE resolution. A warning rather than a failure would preserve the fourth-state escape hatch. - Should ArchiMate get model-level property promotion? It is the only notation that cannot state the fact about a model, and the gap is an absence rather than a decision.
- Are transitional plateaus ordered? Several may exist on one migration path and nothing sequences them; they are distinguished only by id. A path with three plateaus is currently three unordered claims that something was true for a while.
- Should Structurizr get a route at all? It has neither an index nor an in-file extension point, which is the same position ADR-era extension data left it in.
- Should the reading-side fallback be offered as a convenience? Rule 6 keeps inheritance out of the graph. Whether a query template should coalesce a view's unstated value with its model's is a separate question, and the answer that keeps this decision honest is to expose both and let the caller choose.