Skip to content

Data model

Ramose presents records in application code, but its durable model is a set of typed facts: one entity, one field, one value, and the period during which that statement is true. Updating a single-value field closes the old statement and adds a new one; it does not overwrite history.

An entity definition is a durable record kind:

const Task = Ramose.Entity("task", {
title: Ramose.string(),
done: Ramose.boolean(),
assignee: Ramose.ref(User, { optional: true }),
labels: Ramose.Field.many(Ramose.ref(Label)),
})

Every stored row carries one protected concrete type. Application code cannot forge or change it. The type lets queries, policies, operation targets, and trait membership agree even when optional fields are absent.

Task.title is more than a TypeScript property. It is a stable field identity with a value type, cardinality, indexing, uniqueness, ownership, documentation, and creation behavior.

Single-value fields replace; many-value fields are sets. A unique field can be strict or can identify an existing entity during creation. An owned reference declares the one cascading-delete relationship. Requiredness alone never implies ownership.

Names belong to the data contract. Rename UI labels freely; rename an entity, field, trait, or operation only with an explicit migration plan.

A trait is a reusable group of fields and operations:

const Commentable = Ramose.Trait("commentable", {
comments: Ramose.Field.many(Ramose.ref(Comment)),
})
const Task = Ramose.Entity(
"task",
{ title: Ramose.string() },
{ traits: [Commentable] },
)

Composition is a transitive union, not inheritance. Trait fields retain the trait’s identity even though they flatten onto the entity for authoring. Querying a trait returns readable entities whose deployed concrete type composes it. Trait operations can target every compatible visible composer.

Diamonds are harmless when they reach the same trait. Cycles and two flattened fields competing for the same public name are rejected at definition time.

A reference is a typed edge to an entity or trait. Forward navigation follows the stored edge; reverse navigation finds rows that point back. Nested query projections can pull related data, but every hop remains subject to authorization.

References connect rows within the database. Use an explicit entity and policy rules to model tenancy or ownership boundaries.

Because changes accumulate, the current database is only one immutable value in a sequence. asOf selects an earlier value. history asks when statements appeared or were removed. Current deployed policy and current grants still govern historical reads.

This model descends from Datomic’s immutable database values and indexes, adapted here for TypeScript authoring, browser replication, and Cloudflare deployment.