Skip to main content

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​

NeedModel
Stable domain behavior, actions or compile-time useCode-defined entity property
Explore a new entity or field before committing its shapeDynamic schema
Long-lived attributes controlled by data or another sourcePermanent 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.