Apps
Model
# model — in-memory typed models generated by the Schema Builder
`deft/model` is the runtime behind `<schema>.scm/model.dft`. The generated
module supplies the schema's relation table, persistence refs, insert order,
and id metadata; this package supplies the model handle and relation API.
A generated model is not required to be persistent. It is an in-memory
graph you can create, inspect, dump, and later commit to SQLite in one
transaction.
## Quick start
from "shop.scm/model" import new-shop set model [new-shop] set c [$model create %Customer{ :id "c1" :name "Ada" }] set a [$c create [Address --street "Main"]] set o [$c create [Order --id "o1"]] [$c list Address] # @{ %Address{...} } [$c list Order] [$model find Customer "c1"] [$model list Customer] [$model dump] set r [await [$model persist "shop.db"]] # r => %Ok{:value %{ :types %{ "Customer" 1 ... } :path "shop.db" }} set loaded [new-shop] set l [await [$loaded load "shop.db"]] # l => %Ok{:value %{ :types %{ "Customer" 1 ... } :version 1 :seq 1 }}
## Model operations
| Call | Result |
|---|---|
| `[$model create %Type{...}]` | Stored record, attached to the model; raises on validation failure |
| `[$model find Type id]` | Record or nil (exact type) |
| `[$model list Type]` / `[$model all Type]` | Vector of records |
| `[$model dump]` | Snapshot `%{ :types %{ "Type" @{...} } :counts %{...} }` |
| `[$model reset]` | Clears entities/ids but keeps the schema |
| `[$model persist "path.db"]` | Async `%Ok`/`%Err`; snapshot-replace the model tables and record the schema version |
| `[$model load "path.db"]` | Async `%Ok`/`%Err`; verify metadata, load all rows, replace the model atomically |
`Type` is a bare type name (`Customer`) or keyword/string. Child operations
use the same convention: `[$customer list Address]`.
## Relations
- Association — child has a `:references` field to the parent. Creating
the child injects the parent's referenced value into that field, and the
child is stored as an entity.
- Composition — parent has a field typed as another record/union. Creating
the child appends/sets it inside the parent; composition children are not
independent entities and are not separately findable.
`create` resolves the child type against the parent's generated relations. If
several relations match, it raises; `list` combines all matching relations.
Inherited relations are available on subtypes. Imported parent types (for
example a `Customer` from another `.scm`) also get relations/impls when a local
type references them, so `[$customer create [Order ...]]` works across schema
imports.
Composition creation updates the parent copy held by the model, not the
immutable record in your variable. Read the current children with `list` (or
`find`) rather than a previously captured parent field.
## Ids
- A declared `:primary-key` field is used directly and is required on create.
- If the type has no PK and a field named `id`, that field is the key and is
required.
- Otherwise the model assigns an implicit numeric `:id` to entity records.
Persisted associations require a declared primary key on the parent. Implicit
ids are in-memory only; a standalone no-PK entity persisted to SQLite receives
a database-assigned id.
## Dump
`[$model dump]` returns the current state as data, not a printed string:
echo [deft/stringify [$model dump]]
Internal `:__model` context is stripped from the snapshot; model-assigned
implicit ids remain. Association children appear under their type;
composition children appear nested inside their parent.
## Persistence
`persist` is a snapshot, not an append: inside one transaction it
deletes the model's existing rows (children first), inserts the current
entities parent-first with `persist/insert-many`, writes the schema
metadata, and commits. Any failure rolls back both data and metadata.
Composition fields stay nested in the parent row (persist's JSON codec);
association rows are separate. Views are not created.
After commit, the rows returned by `insert-many` replace the in-memory
entities, so database-assigned implicit ids and `:seq` agree with the DB.
`load` reads the current `_persist_meta_` blob for the model scope and
verifies its version and hash against the generated descriptor before
reading any rows. The dataset is staged and swapped in atomically; a
mismatch or bad row leaves the existing model untouched. `load` requires
the matching generated `model.dft`/`types.dft` — the metadata blob is a
manifest, not executable schema.
set model [new-shop] set r [await [$model load "shop.db"]] # r => %Ok{:value %{ :types %{"Customer" 1 …} :version 3 :seq 1 }}
If the schema changed, run the generated schema module's `migrate`
explicitly, then retry `load`; load itself never auto-migrates.
For a complete normal app that saves configuration/last state this way,
see `packages/todo` (`retro app deft/todo`) and the manual page
`help/manual/03-Platform/10-app-state.md`.
## Limits
- `load` is strict: the persisted schema/version/hash must match the
generated module. Older blobs are retained for diagnostics, not for
loading a different schema.
- Exact-type `find`/`all` (no polymorphic find).
- Persist owns its tables: snapshot replacement deletes rows in the
model's mapped tables.
- No persisted associations backed by implicit ids.
- Persist stores each exact type in its own table. A subtype row is not
materialized into its parent's table, so an association FK that targets the
parent table needs a parent-table row (create the base entity separately).
- Imported parent types referenced by local records are part of the model
graph: `persist` ensures their tables (with default storage options) and
inserts their entities before local children. If the owning schema customizes
storage options, ensure/migrate it separately with matching options.
- Composition children with implicit ids remain model-local; give the
child a declared primary key if its identity must survive a round-trip.
- One generated-model module per type namespace; duplicate type names across
schemas collide as usual.
- The generated module is regenerated on every Schema Builder save.
## Tests
```sh
deft test packages/model/test/test_model.dft
deft test packages/schema-builder/test/test_model_emit.dft
deft test packages/schema-builder/test/test_model_integration.dft
````