Code-defined, dynamic and data-defined fields
Vox supports three ways to shape data. They solve different problems: a stable model belongs in code, an experiment can begin as dynamic schema, and a permanently extensible classification model can remain data-defined.
Code-defined entities and properties
The normal production model consists of CLR entity types, field groups, relations, actions and annotations. It is versioned with the application, reviewed like other code and available to the compiler. This is the right default for fields with stable business meaning, such as a product SKU, an order's customer or a campaign's status.
Code-defined does not mean inflexible. Vox derives storage, API contracts, actions, permissions, query fields and editor metadata from the model, so changing the model still changes the complete system coherently.
Dynamic schema for prototyping
Dynamic schema adds entity types or ordinary entity fields while an application is running. Dynamic fields can have validation, relations, query capabilities and editor metadata, and they participate in the same entity model as CLR properties.
Dynamic schema is intended for exploring a model before committing it to code. Vox retains schema history, detects conflicts with later code changes and can generate the C# represented by the prototype. Once a field has stable meaning, code becomes authoritative and the matching dynamic definition is no longer needed.
This makes dynamic schema useful for workshops and short feedback loops without turning the production domain model into an unreviewed collection of runtime configuration.
Permanent data-defined fields
Some fields are dynamic by nature. A manufacturer may publish technical attributes, a retailer may maintain its own classification, and different product families may use different specification sets. Those definitions should not need a new CLR property or deployment each time they change.
Vox represents them as stable typed fields grouped into reusable field sets. An entity property derives from
VoxFieldValues and selects a set:
public class TechnicalFields : IVoxFieldDefinitionProvider
{
public VoxField<decimal> Voltage { get; } =
new("technical.voltage", "voltage");
public VoxField<string> Color { get; } =
new("technical.color", "color");
public IEnumerable<VoxField> GetFields() => [Voltage, Color];
public IEnumerable<VoxFieldSet> GetFieldSets() =>
[new VoxFieldSet("technical.product", [Voltage, Color])];
}
[VoxFieldSet("technical.product")]
public class TechnicalSpecifications : VoxFieldValues, IVoxFieldGroup
{
}
Only values that are present are stored, so an entity can have a sparse set of specifications. The stable field ID is its identity; its JSON name can evolve without turning a rename into a different field.
Definitions can also be supplied as permanent field-schema data rather than by a code provider. Each definition has an owning source, and only that source may replace it or contribute fields to a set. Schema changes use optimistic concurrency and are shared across application instances.
Types and query behavior
Data-defined fields can be strings, numbers, booleans, dates, rich text, enums and entity relations. Lists and values that vary by context compose those types, so the same mechanism can express a list of measurements or a label by language and channel.
A definition can make a field filterable, sortable or searchable. Vox then treats it like a CLR property with the equivalent query annotation: it receives indexed storage, appears in entity metadata and is available to generic clients without those clients knowing the field at compile time.
Choosing the right model
| Need | Model |
|---|---|
| Stable domain behavior, actions or compile-time use | Code-defined entity property |
| Explore a new entity or field before committing its shape | Dynamic schema |
| Long-lived attributes controlled by data or another source | Permanent data-defined field |
The distinction is about ownership and lifecycle, not technical power. All three participate in serialization, storage, metadata and the generic Admin UI, but they make different promises about where the definition remains authoritative.
See Owned and mirrored data, Querying and search and Prototype without code.