ADR 0009 — A lifeline is not the thing it represents¶
Status: Proposed — not implemented. Written to be implemented from; the two sub-decisions that blocked implementation are settled below.
Scope: how a PlantUML sequence participant becomes RDF: what is a uml:Lifeline, what is the
structural element it stands for, how uml:represents links them, and what a uml:Message connects.
Depends on: ADR 0002 and ADR 0005 for the name-derived, model-scoped identity that makes this necessary at all.
Supersedes part of ADR 0007. 0007 stopped a lifeline
being typed uml:Actor or uml:Node, which was right. It did so by making the element a
uml:Lifeline, which was the wrong resource to change — see The mistake in 0007.
Context¶
uml:represents is the association from Lifeline to a ConnectableElement: it answers "what is this
lifeline a lifeline of?" A Lifeline is not a classifier; it is the projection of one participant onto
the time axis of an Interaction. UML 2.5.1 §17.3 gives it multiplicity [0..1], and the target is
typically a Property — a part or role of the BehavioredClassifier that owns the Interaction — though
Port and Parameter are also allowed.
This is the highest-value property in the interaction package for our purposes, and for the reason it exists: it is the only edge that lets a query walk from something happening in an interaction to the structure it happened to, and from there onward to an ArchiMate component the class realises.
What the published ontology says, with two wrinkles¶
:represents a owl:ObjectProperty ;
arch:domainIncludes :Lifeline ;
arch:rangeIncludes :Property ; # ← Property only
skos:definition "References the ConnectableElement (Property, Port, or Parameter) …" ;
skos:scopeNote "… If represents is absent, the Lifeline represents the classifier
itself (the 'self' lifeline)." .
uml:ConnectableElementis not declared. The definition names it andumlsh:LifelineShape's message names it, but no such class exists inuml-onto.ttl.uml:Property,uml:Port,uml:Parameteranduml:TypedElementall do; their common supertype does not. So the axiom narrows toPropertywhile the prose promises three, and neither is enforceable against a class that is absent.- The shape constrains cardinality only.
umlsh:LifelineShapehassh:maxCount 1onuml:representsand nosh:class. Whatever we point it at will validate. The choice is therefore entirely a question of what is true, not of what passes.
Why we cannot emit it today¶
Not because of the range. Because there is no second resource to connect. Element ids are slugified
entity codes scoped to the model (ADR 0010), so for a participant
referred to as Customer:
- if a class
Customerexists in the same model, it is the same IRI —representswould be a self-loop; - if it does not, the participant element is the only resource bearing that name — there is nothing to point at.
In the current playground corpus it is the second case throughout. The first is real by design: a
colliding name yields one IRI carrying both uml:Class and uml:Lifeline, which is the conflation
0007's identity note flagged.
The uncomfortable corollary: the cross-model walk this ADR is about already works, and it works
because of that conflation. If Customer is one IRI, a message reaches the ArchiMate component in one
join and no represents is needed. It is free, and it is wrong — uml:Lifeline is
rdfs:subClassOf uml:NamedElement, not a Classifier, so the merged resource asserts two incompatible
things about one subject.
Absence is not neutral, in two places¶
Both are cases where omitting a triple is itself a claim under UML reading rules:
| Omission | Reads as | Our output today |
|---|---|---|
uml:represents absent |
the self lifeline of the enclosing classifier | every lifeline — so five self-lifelines in one Interaction |
uml:sendEvent absent |
a found message | every message |
uml:receiveEvent absent |
a lost message | every message |
umlsh:MessageShape states the second and third explicitly in its own messages. So today every message
is simultaneously found and lost, and every lifeline is the self lifeline. This is not a new defect
introduced by anything recent, but it does undercut the "absence is honest" argument used in 0007 for
not emitting represents — silence here is a quieter falsehood, not a neutral one.
The mistake in 0007¶
0007 observed that a sequence diagram produced uml:Lifeline, uml:Actor and uml:Node on one lifeline
axis, and fixed it by resolving every sequence participant to uml:Lifeline. The observation was right
and the fix was applied to the wrong resource.
There are two things per participant, and the keyword describes one of them:
| What it is | What types it | |
|---|---|---|
| the lifeline | a projection onto the interaction's time axis, one per interaction | always uml:Lifeline |
| the thing represented | the structural participant | the keyword — actor → uml:Actor, database → uml:Node |
Seen that way the original keyword mapping was never wrong; it was being applied to a resource that did not exist yet. 0007 removed a false statement by deleting information rather than by separating the subjects.
Options¶
A. Status quo: keep the merge, emit no represents¶
Free join, no work. One IRI is both a Classifier and a Lifeline, represents-absence reads as
self-lifeline, and a consumer cannot ask "which lifelines are there" without also getting classes.
B. Split, and synthesise the Property UML asks for¶
Lifeline --represents--> Property --type--> Class. Strictly correct.
Rejected on this codebase's own stated principle. UmlTypeDefaults.messageKindIri deliberately never
emits uml:messageKind uml:Complete because the converter emits no uml:MessageEnd resources and so
"would be claiming a fact about a structure it does not model". A synthesised Property is that twice
over: UML requires the Property to be a part of the classifier owning the Interaction, and PlantUML gives
us no such classifier either. Two fabricated resources per participant, one of them nameless.
C. Split, and point represents at the structural element directly¶
Lifeline --represents--> Actor | Node | Class | …. Deviates from the Property range, invents nothing,
and delivers the walk.
Decision¶
C, with the structural element typed by the keyword and the lifeline scoped to its view.
## actor Customer / participant Web_Shop / database Order_DB, in view `checkout`
<…/element/Customer> a arch:Element, arch:ModelConcept, uml:Actor ;
skos:prefLabel "Customer"@en .
<…/view/checkout/lifeline/Customer> a arch:Element, arch:ModelConcept, uml:Lifeline ;
skos:prefLabel "Customer"@en ;
uml:represents <…/element/Customer> ;
schema:keywords "actor" .
<…/element/Order_DB> a arch:Element, arch:ModelConcept, uml:Node ;
schema:keywords "database" .
<…/view/checkout/lifeline/Order_DB> a …, uml:Lifeline ;
uml:represents <…/element/Order_DB> .
Four parts, each of which was a real choice.
1. The lifeline is a new resource, addressed under its view.
…/view/{viewId}/lifeline/{participantId}, minted with the existing IriMinting.customIri. View-scoped
because a lifeline belongs to one Interaction and the same participant may appear in several — which is
precisely what the current model-scoped element cannot express. No existing IRI moves: …/element/{id}
keeps its address and its meaning, and the lifeline is additive.
2. The keyword types the structural element again, and the lifeline is always uml:Lifeline.
This restores what 0007 removed, at the resource where it was true all along. actor → uml:Actor,
database → uml:Node, boundary → uml:Class. participant — PlantUML's default — says nothing
structural, so the element gets arch:Element and arch:ModelConcept and no UML metaclass. That is
honest rather than untyped-by-accident: it is a named participant whose metaclass the source genuinely
does not state, and inventing uml:Class for it would be option B's error in miniature.
3. arch:source / arch:target on a Message stay on the structural elements. Unchanged, so nothing
migrates and no query breaks. P-7 requires the direct triple for analytics, and "does ClientApp call
OrderService" must not become a three-hop path through lifelines. The UML-faithful chain —
Message → sendEvent → MessageOccurrenceSpecification → covered → Lifeline — is deferred, see
Phase 2.
4. There is no uml:Interaction resource in phase 1, and the view is not typed as one.
Typing the view uml:Interaction was the obvious move and it is wrong:
uml:Interaction ⊑ uml:Behavior ⊑ uml:Class ⊑ … ⊑ uml:Element ⊑ arch:Element, and also
uml:Interaction ⊑ uml:InteractionFragment ⊑ uml:NamedElement ⊑ uml:Element ⊑ arch:Element. So it would
make every sequence view an arch:Element, inflating every element count with things that are not
elements. If an Interaction is wanted it needs its own resource, which is phase 2. The per-interaction
scoping that matters now is carried by the lifeline's address.
What represents points at, and the range deviation¶
arch:rangeIncludes uml:Property is guidance, not inference — that is the whole point of DD-8 — and no
shape enforces it, so pointing at uml:Actor or uml:Node produces no violation and no wrong entailment.
It is nonetheless a deviation, and it is recorded as one rather than glossed: we are saying "this lifeline
stands for that participant" where UML says "…for that part of the enclosing classifier". The intermediate
Property is the thing PlantUML does not have.
The alternative is to leave represents empty and let it read as self-lifeline, which is a worse
statement, not a more cautious one.
Phase 2, deferred deliberately¶
Not in scope here, listed so it is not rediscovered:
uml:Interactionas its own resource, withuml:hasLifelineanduml:hasMessage. Needs an answer to how it relates to the view without the view becoming anarch:Element.MessageOccurrenceSpecificationpairs, givinguml:sendEvent/uml:receiveEventand closing the found/lost trap.umlsh:OccurrenceSpecificationShaperequiresuml:coveredexactly 1 anduml:sequenceNumberexactly 1 — both of which PlantUML can supply, since it gives message order. The cost is two extra nodes per message: on the measured corpus, ~3,100 for 1,567 messages.- Real
<<stereotypes>>viauml:Stereotype/uml:ProfileApplication, from 0007's open questions.
Consequences¶
- The interaction-to-structure walk becomes an explicit one-hop edge instead of an accident of naming, and it works whether or not a class diagram in the same model happens to use the same name.
uml:Classanduml:Lifelinestop sharing a subject. 0007's identity-merge note is resolved rather than documented: aCustomerclass and aCustomerparticipant are now two resources with an edge between them, which is what they are.- The same participant in two sequence diagrams of one model becomes two lifelines representing one element — correct, and not currently expressible.
- Element counts rise.
uml:Lifelineis anarch:Elementby inheritance (uml:Lifeline ⊑ uml:NamedElement ⊑ uml:Element ⊑ arch:Element), so a five-participant sequence diagram goes from five elements to ten. Defensible under P-9 — a lifeline is a node on a diagram, it renders, it has identity — but it is a visible change to every count and every folder listing, and it is the main cost of this ADR. - Existing element IRIs and message IRIs are unchanged, so there is no republish and no identity-lock
concern. Only additions, plus
rdf:typemoving back on the structural element. schema:keywordsmoves to the lifeline for shape keywords (actor,database) since that is what the shape describes, and stays on the element for structural ones. Worth checking against 0007's derived rule during implementation — the rule compares the keyword against the resolved class, and there are now two classes in play.- A view node (
archvis:ArchNode) currently points at the element viaarchvis:archElement. It should point at the lifeline, because that is what is drawn on a sequence diagram. This is the one place an existing triple changes meaning.
Implementation plan¶
PumlModel: sequence participants keep producing onePumlElement; add nothing to the model. The split is an emitter concern — the parser already has everything.IriMinting: addlifelineIri(viewId, participantId)rather than usingcustomIriad hoc, so the scheme is in one place with the others.UmlTypeDefaults: revert theresolveElementType(keyword, ns, diagramType)overload added by- With the split, the diagram kind no longer changes how the element is typed. Keep a documented
default for
participantthat yields no UML class. LinkedArchiEmitter(PlantUML), sequence path only: emit the structural element as today minus the 0007 override; emit a lifeline per participant per view, typeduml:Lifeline, labelled, withuml:representsat the element; move the shape keyword to the lifeline.archvis:archElementon sequence view nodes: retarget to the lifeline.- Tests: extend
KeywordAndLifelineTest; theevery sequence participant is a lifelinecase inverts — the lifeline resource is the Lifeline and the element is anActor/Node/untyped. - Amend 0007 in place: its sequence-override section is superseded here, and its "absent is honest"
line on
representsis wrong for the self-lifeline reason. docs/converters/plantuml.md: the sequence section needs the two-resource picture.
Example¶
@startuml
' checkout.puml — view `checkout`
actor Customer
participant Web_Shop
database Order_DB
Customer -> Web_Shop: place order
Web_Shop -> Order_DB: persist
@enduml
<…/element/Customer> a …, uml:Actor ; skos:prefLabel "Customer"@en .
<…/element/Web_Shop> a arch:Element, arch:ModelConcept ; skos:prefLabel "Web Shop"@en . # no UML class
<…/element/Order_DB> a …, uml:Node ; schema:keywords "database" .
<…/view/checkout/lifeline/Customer> a …, uml:Lifeline ;
uml:represents <…/element/Customer> ; schema:keywords "actor" .
<…/view/checkout/lifeline/Web_Shop> a …, uml:Lifeline ;
uml:represents <…/element/Web_Shop> .
<…/relationship/Customer__Web_Shop__place_order__…> a …, uml:Message ;
uml:messageSort uml:SynchCall ;
arch:source <…/element/Customer> ; # unchanged — the analytics contract
arch:target <…/element/Web_Shop> .
The query this is for, across notations:
# from a message in an interaction to the ArchiMate component the participant realises
SELECT ?message ?component WHERE {
?message a uml:Message ; arch:target ?element .
?lifeline uml:represents ?element .
?element am:realizes ?component .
}
Open questions¶
- Should
representspoint at aPropertyafter all, if one is ever derivable? A class diagram in the same model that declaresWeb_Shopwith a part could in principle supply one. Nothing in PlantUML connects a sequence participant to a class attribute, so this stays hypothetical. - Does the
participantdefault deserve a UML class? It gets none here.uml:Classwould be a guess;uml:Classifieris abstract andarch:isAbstractClass-annotated upstream, so it is not instantiable either. Leaving it atarch:Elementis the honest reading, but it does mean a sequence-only model has elements with no metaclass, which theConversionVerifiermay want to stop reporting as incomplete. - Should the lifeline live in the semantic graph or the views graph? It is addressed under a view and
it is a
uml:Lifeline, which is model content, not layout. Semantic graph is the proposal, on the grounds that a lifeline is a UML metaclass instance andarchvis:holds geometry — but it is genuinely arguable, and thearchvis:archElementretarget in step 5 crosses the boundary either way. uml:ConnectableElementis missing upstream, anduml:representsnarrows toPropertywhile its own prose says Property, Port or Parameter. Not filed intodo/UPSTREAM-ISSUES-meta.md; it would make the range guidance match the specification and give the shape a class it could actually check.