Skip to content

Entity References

An entity reference is a value that enables a part of the system to access an entity, inspect its public data, or request actions on it. Entity references are themselves values — they can be stored, transmitted, and processed by different parts of the system.

Entities in the Arda system are journaled in a bitemporal sense. A reference to an entity can identify either:

  • Floating Reference: Identifies the complete lineage of versions of an entity. Accessing a floating reference requires specifying bitemporal coordinates to identify the specific version to access. When floating references are used with “now” time coordinates, the returned values may change depending on when the entity is accessed, or the entity may even have been deleted.

  • Pinned Reference: Identifies a specific bitemporal version (record) of an entity. Pinned references can rely on the fact that the version they point to will not change over time. In normal operation, no record is ever deleted, though OAM operations (archiving, purging) may remove records.

The choice between pinned and floating references is a domain-level concern. Business processes that must produce immutable records (e.g., a closed purchase order) should use pinned references to the reference data they captured at the time of the transaction.

Regardless of the data type used to encode an entity reference, its information contents can be expressed using a standardized URI structure. The components are:

The protocol used to access the entity:

SchemaProtocol
httpsREST-based access using HTTPS (typically HTTP/1.1)
grpcgRPC-based access using HTTP/2
eventbusArda event bus based access (TBD)
localAccess within the same component
contextualInterpreted by the user of the reference

The endpoint where the entity can be accessed. UserInfo must not be used in the authority section. The authority is mandatory for all schemas except contextual.

For https: a globally resolvable hostname or IP address; no port number (port 443 is implicit).

For grpc: a hostname resolvable within the Kubernetes cluster. By convention Arda components run in namespaces named <partition>-<component> (e.g., prod-operations), but the authority for a gRPC URI uses only the component name (e.g., operations). Components are not allowed to access entities outside their partition.

The unique identifier of the entity within the authority’s domain. For https:

  • module: The name of the module (service) managing the entity
  • resource / entity-type: The type of entity among those managed by the module. Both module and resource segments must be present even when they have the same value.
  • Then one of:
    • {local-eid} — Floating reference: the UUID identifying the complete bitemporal lineage
    • {local-eid}/rid/{local-rid} — Pinned reference: the UUID of a specific bitemporal record within the lineage

An entity reference is completely defined by its schema, authority, and path. One additional parameter is useful for floating references when an entity has been deleted:

  • includedeleted=true: Return the last version of the entity at the time of deletion rather than a “not found” response. The default for all references is to return “not found” if the entity does not exist at the specified bitemporal coordinates.

These parameters are not part of the entity reference itself but are standard parameters used in requests that address an entity:

ParameterDescription
tenantidUUID of the tenant that the entity belongs to. Mandatory for regular user requests. Encoded as an HTTP header for the https schema.
effectiveasofUnix epoch milliseconds specifying the effective time dimension. If omitted, the server uses its local clock as “now”. Encoded as a query parameter for https.
recordedasofUnix epoch milliseconds specifying the recorded time dimension. If omitted, the server uses its local clock as “now”. Encoded as a query parameter for https.

Note on current system (as of 2025-12): The system is not yet consistent in using operationtime vs effectiveasof for mutation operations. Use of requestId for idempotency is also limited at this time.

Excluded from this document: gRPC, eventbus, local, and contextual schema path details are described in the source document but are implementation-level concerns. Protocol-specific header encoding details belong in the API reference section of the Current System documentation.

Within Arda backend modules (Kotlin / Ktor / Exposed), an entity reference is realised as a sealed interface with at least one nested Value data class, both implementing EntityPayload:

@Serializable
sealed interface ItemReference : EntityPayload {
@Serializable(with = UUIDSerializer::class)
override val eId: EntityId
@Serializable(with = UUIDSerializer::class)
val rId: UUID? // present when pinning to a specific version; null for floating
val name: String? // cached display field (so consumers can render without re-fetch)
@Serializable
data class Value(
@Serializable(with = UUIDSerializer::class)
override val eId: EntityId,
@Serializable(with = UUIDSerializer::class)
override val rId: UUID? = null,
override val name: String? = null,
) : ItemReference {
override fun validate(ctx: ApplicationContext, mutation: Mutation) = Result.success(Unit)
companion object {
fun fromItem(item: Item, rId: UUID? = null): Value = Value(item.eId, rId, item.name)
}
}
}

The eId carries the floating identity (the entity, regardless of version). The optional rId pins the reference to a specific bitemporal record (the version of the entity at the moment the reference was captured). Cached display fields are stored alongside so consumers can render the reference without re-fetching the target entity.

The seal lets the type evolve (e.g., a future ItemReference.External(...) variant for entities owned by a remote system) without breaking existing call sites.

When persisted, the reference value object is flattened into the holder’s table with prefix-based column naming (e.g., "ITEM_REFERENCE_entity_id", "ITEM_REFERENCE_item_name", "ITEM_REFERENCE_record_id"). This keeps the foreign-side data accessible without a join while explicitly preserving “no SQL FK across services” for cross-service references — see Data Authority Module Pattern § Cross-Service Isolation.

For the broader design playbook (when to introduce a reference family, how to choose the cached display fields, how to handle resend chains and parent-child relationships), see Information Model Design.