Artifact Relations Graph
Model Forge artifacts form a typed graph across all artifact types — not just Elements. Schemas reference other schemas, mappings connect source and target Elements, pipelines wire sources, mappings and sinks, DataSources and DataSinks bind to the Element that describes their payload, and DataSetsDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. and DataStructures aggregate their members. Every edge is recorded durably and fed into the in-memory dependency graph (ADR 054).
Edge types
Each relation an artifact declares is stored as a typed edge:
| Relation source | Stored edge type | Meaning |
|---|---|---|
JSON Schema $ref inside an Element | schema-ref | Element depends on another Element |
XSD xs:import | xsd-import | XSD-backed Element imports another Element namespace |
Element x-core-ref association target | association-ref | Element references another Element by a concrete association (foreign-key) target |
Mapping source | mapping-source | Mapping reads from a source Element |
Mapping target | mapping-target | Mapping writes to a target Element |
| Pipeline nodes | pipeline-node | Pipeline uses a DataSource, Mapping or DataSink |
DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. *Refs arrays | dataset-ref | DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. contains or references member artifacts; reference_name carries the member kind (datastructure, mapping, pipeline, datasource, datasink) |
DataSource element field | datasource-element | The DataSource's own payload is described by an Element |
DataSink element field | datasink-element | The DataSink's own payload is described by an Element |
DataStructure $defs.*.$ref | datastructure-ref | DataStructure groups member Elements |
Note the distinction between the two DataSource/DataSink edge families. A
DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. → member edge from a DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. manifest's *Refs arrays is always
dataset-ref, whatever the member is; the member kind goes into
reference_name. The deletion policy depends on that uniform typing, because it
counts the DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. memberships of an artifact separately from its other
references. datasource-element / datasink-element are a different thing: the
DataSource's / DataSink's own binding to the Element that describes its
payload.
The durable source is artifact_reference, described in
PostgreSQL Artifact Registry.
The table stores complete per-version edges, including cycles and unresolved
targets. It preserves the authored target URN verbatim, including pinned versions
and :latest.
The embedded facade exposes these relations through:
dependencies/dependentsreturn an artifact's direct graph neighbours (version-precise).mapsTo/mappedFromreturn the Mappings an Element participates in.- A Mapping document names its
sourceandtargetElements; a Pipeline document references its DataSources, DataSinks and Mappings; a DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. manifest lists its members by URN.
dependencies / dependents answer for any artifact URN, over all the
edge types above — this is what the admin UI's graph view renders. The same
typed edges are also returned as the dependency lists of every write result
(ArtifactWriteResult.dependencies), so a host can mirror them without a
separate query.
Composition and delete protection
The edge types above arrange the artifact types into a composition DAG.
Some artifacts act as containers that reference the artifacts they compose or
consume; the referenced artifacts are their contents. Read top-down: a DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions.
composes Pipelines, Mappings, DataSources, DataSinks and Elements; a Pipeline
wires DataSources, Mappings and DataSinks; a Mapping binds a source and a target;
a DataSource and a DataSink each bind the Element that describes their payload;
a DataStructure groups its member Elements; and an Element may $ref further
Elements.
An arrow A → B means A references B, so B is a content of the container A.
The integrity rule follows directly from this DAG. Keeping the model internally consistent is Model Forge's core job, so it never lets a reference dangle:
- A container may always be deleted. Deleting it removes only the container, never its contents; nothing it points at can block it (its own outgoing edges never protect it).
- A content artifact cannot be deleted while any container still references
it.
deleteArtifactthrowsArtifactInUseExceptionand lists every blocking dependent. To remove the artifact, first update or delete the referencing container(s). - Only an artifact's own self-references (cycles inside a single document) are exempt — a document cannot block its own deletion.
Put differently: a delete is blocked by incoming edges, never by outgoing
ones, and every edge type blocks — a grouping edge (datastructure-ref)
protects its member exactly like a hard schema-ref dependency does.
| Deleting… | …is blocked while it is referenced by | via edge |
|---|---|---|
| Element | a DataStructure, Mapping, DataSource, DataSink, DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions., or another Element | datastructure-ref, mapping-source/mapping-target, datasource-element, datasink-element, dataset-ref, schema-ref/xsd-import/association-ref |
| DataStructure | a Mapping that reads or writes it | mapping-source / mapping-target |
| DataSource / DataSink | a Pipeline node or a DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. | pipeline-node, dataset-ref |
| Mapping | a Pipeline node or a DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. | pipeline-node, dataset-ref |
| Pipeline | a DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. that composes it | dataset-ref |
| DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. | nothing references a DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. — it is always deletable | — |
Cascading delete
deleteArtifact takes an optional cascade flag. With cascade=true the target's members
(the artifacts it references) are deleted along with it — but each member only if, once this
container is gone, no other artifact still references it. A member that another container still
groups, or that a sibling references, is kept. The cascade applies the same rule recursively, so
deleting a DataSetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. can remove its Pipelines, Mappings, DataSources and Elements down the tree,
stopping wherever something is still in use (mutually-referencing members keep each other).
The target itself still obeys the base rule — cascade only ever deletes downward into members, never a target that something else references. Each artifact is deleted in its own transaction and re-checked against the live registry, so a cascade can never leave a dangling reference even though it is not a single atomic operation.