Bitemporal Persistence
Bitemporal persistence is a data management approach that tracks both valid time (when a fact is true in the real world) and transaction time (when a fact is stored in the database). This allows for historical queries, auditing, and correction of past data without losing information about previous states.
Core Concepts
Section titled “Core Concepts”Time Coordinates
Section titled “Time Coordinates”TimeCoordinates represents a pair of timestamps:
@Serializabledata class TimeCoordinates(val effective: Long, val recorded: Long)- Effective Time (Valid Time): When a fact is true in the real world. The time period during which the information is considered valid.
- Recorded Time (Transaction Time): When a fact was recorded in the system. The history of how the system’s knowledge of the real world evolved.
All bitemporal records are associated with a TimeCoordinates instance, enabling queries as of any point in both timelines.
BitemporalTable Schema
Section titled “BitemporalTable Schema”BitemporalTable abstracts a table with columns for:
eId— entity identity (UUID)rId— record identity (version identifier, UUID)effectiveAsOf— when this fact was effective in the real worldrecordedAsOf— when this fact was recorded in the systemauthor— who made this changeprevious— reference to the previous record in the lineage (UUID, nullable)retired— logical delete flag
TimeCoordinatesColumn is a composite column for storing and retrieving TimeCoordinates pairs.
BitemporalEntity and BitemporalRecord
Section titled “BitemporalEntity and BitemporalRecord”BitemporalEntity is a serializable data class representing a versioned entity:
abstract class BitemporalRecord<EP, M, TBL, SELF> { val rId: EntityID<UUID> var eId by table.eId var effectiveAsOf by table.effectiveAsOf var recordedAsOf by table.recordedAsOf var retired by table.retired var previous by table.previous var author by table.author var payload: EP var metadata: M}Guards and Idempotency
Section titled “Guards and Idempotency”CreateGuard,UpdateGuard,DeleteGuard: Type aliases for suspend functions that enforce business rules before operations.Idempotency: Enum controlling how updates handle duplicate or conflicting changes:REJECT: Reject the duplicateCONFIRM: Treat as confirmed (update succeeds with existing state)SKIP: Silently skip duplicates
Persistence API
Section titled “Persistence API”Contract
Section titled “Contract”The Universe interface defines the contract for bitemporal persistence:
create(payload, metadata, asOf, author): Creates a new bitemporal entity. TheasOfparameter specifies both effective and recorded time.read(eId, asOf): Retrieves the entity record valid at the specified effective time and recorded at or before the specified recorded time.readRecord(rId): Retrieves a specific historical record by record ID.update(update): Creates a new record representing the updated state, with neweffectiveAsOfandrecordedAsOftimestamps, linking to the previous record.delete(originEId, metadata, asOf, author): Creates a new record marked asretired = true.
All operations return DBIO<T> — see Functional Programming: DBIO.
BitemporalEntityClass
Section titled “BitemporalEntityClass”Provides Exposed-based implementations for:
- Reading the latest entity as-of a given time
- Listing all latest entities for each unique ID (using subquery or window function strategies)
- Counting entities as-of a given time
- Ensuring uniqueness and enforcing idempotency/versioning rules
Benefits
Section titled “Benefits”- Full auditability and historical reconstruction
- Correction of past data without data loss
- Support for complex business rules and versioning strategies
- “As-of” querying on either or both time dimensions
References
Section titled “References”- Source code:
/common-module/lib/src/main/kotlin/cards/arda/common/lib/persistence/bitemporal/ - Bitemporal Database (Wikipedia)
- Exposed ORM
See Also
Section titled “See Also”- Information Model Design — design-time playbook for defining new bitemporal entities (DDL conventions, indices, composite-value flattening).
- Universe Design — class hierarchy that consumes the bitemporal record types.
- Data Authority Module Pattern — the four-layer module structure that wraps a bitemporal universe.
Copyright: © Arda Systems 2025-2026, All rights reserved