History lifecycle
This page describes how a persisted semantic modification is recorded in the history of a project’s branch, with or without an impact on a representation.
It follows the current implementation, where snapshots use the Sirius Web SemanticData, RepresentationMetadata and RepresentationContent aggregates.
1. Lifecycle diagram
The diagram follows the service, aggregate and event conventions of the Sirius Web project creation lifecycle. It shows the successful recording path, starting with an input whose semantic changes are persisted. An impact on a representation means that its persisted metadata or content changes.
2. Without an impact on a representation
Persisting the semantic modification emits SemanticDataUpdatedEvent.
SemanticDataRecorder resolves the ProjectSemanticData associated with the live semantic data and publishes SnapshotCreationRequested.
The request carries both the branch’s projectSemanticDataId and its live semanticDataId, and keeps the original event as its cause.
If no matching ProjectSemanticData exists, this path does not request a snapshot.
SnapshotContentCapturer handles this request synchronously in the recorder transaction and captures document values, domain URIs, dependency identifiers, representation metadata and representation content in an immutable SnapshotCapturedEvent.
After that transaction commits, SemanticDataSnapshotCreator asynchronously creates new semantic data from the captured values.
The resulting SemanticDataCreatedEvent is handled by RepresentationSnapshotCreator, which creates the representations from that same capture without rereading the live state.
This includes representations whose metadata and content did not change.
Logical representation IDs are preserved, while the snapshot semantic-data ID distinguishes the copies from the live representations.
Only after copying the representations does this listener publish ProjectSnapshotCreatedEvent.
ProjectRevisionCreator then delegates to ProjectRevisionCreationService to persist a revision referencing the branch’s ProjectSemanticData and the new snapshot SemanticData.
The revision also records the root cause’s ID, its author and its creation time.
3. With an impact on a representation
The semantic modification follows the same recording path.
Persisting a representation change additionally emits RepresentationMetadataUpdatedEvent or RepresentationContentUpdatedEvent.
RepresentationDataRecorder handles these events and requests another complete snapshot through the same SnapshotCreationRequested pipeline.
Representation creation or deletion can also enter this pipeline through the corresponding events handled by this recorder.
Each request copies the full state; it does not patch the previous snapshot or reuse unchanged representation snapshots. The diagram shows causal relationships, not a guaranteed global order between semantic persistence and representation persistence.
Before creating a revision, ProjectRevisionCreator checks the current revision of the same ProjectSemanticData.
It amends that revision only if all the following conditions hold:
-
The current revision is not saved (kept as a tag).
-
Its
causedByvalue is not null. -
Its
causedByvalue equals the new snapshot event’s root cause ID.
Amendment deletes the previous revision and creates a new revision with the new complete snapshot.
Consequently, successive events originating from the same input can be consolidated into the current unsaved revision.
If these conditions do not hold, the previous revision is retained and another revision is created.
ProjectRevisionDeletedEvent triggers SemanticDataSnapshotCleaner to delete the removed revision’s semantic snapshot.
4. Transactions and causality
The recording listeners shown in the diagram use @TransactionalEventListener with its default AFTER_COMMIT phase and @Transactional(REQUIRES_NEW).
Each transactional listener therefore runs in a new transaction after the transaction that published its input event has committed.
SnapshotContentCapturer is the exception: its synchronous @EventListener captures values inside the existing recorder transaction, without opening another transaction.
SemanticDataSnapshotCreator and ProjectRevisionCreator use @Async("historyTaskExecutor").
These boundaries break the nested transaction chain so that the publishing thread can release its connections instead of retaining them through the remaining history writes.
Both listeners share one worker with a FIFO queue, which serializes background history tasks within a server instance and prevents concurrent revision amendments by those tasks.
The executor propagates the submitting user’s security context and is managed by Spring.
Both recorders ignore events whose causal ancestry contains SnapshotCreationRequested.
This prevents the creation of snapshot aggregates from recursively recording more history.
Conversely, RepresentationSnapshotCreator requires that cause to distinguish snapshot semantic data from ordinary semantic-data creation.
The root cause is obtained by following causedBy to the beginning of the chain; the intermediate snapshot request ID is not the amendment correlation ID.