Data Modelling
How to design template schemas, property hierarchies, and relationships in Dual.
Template Property Schema
Every Template defines a properties object that acts as the schema for all Objects minted from it. Properties are stored as a flat JSON object, no nested documents, which keeps indexing fast and queries simple.
// Example: Event Ticket template properties{"event_name": "string","event_date": "string", // ISO-8601"seat": "string","tier": "string", // "vip" | "general" | "standing""redeemed": "boolean"}
Naming Conventions
Use snake_case for property keys. Prefix domain-specific fields to avoid collisions when templates are shared across organizations:
ticket_tierinstead oftierwine_vintageinstead ofyearproperty_addressinstead ofaddress
Immutable vs. Mutable Properties
By convention, properties set at mint time are treated as immutable (e.g. serial_number, origin_vineyard). Properties that change over the object's lifecycle (e.g. redeemed, current_owner_name) should be updated via Actions so every change is recorded in the audit trail.
Relationships Between Objects
Dual doesn't enforce foreign keys between objects, but you can model relationships with property references:
// Parent → Child: store the parent object ID{"parent_object_id": "obj_abc123","position_in_set": 3}// Collection: store a shared collection identifier{"collection_id": "col_wine2024","edition": "42/500"}
Use the Objects API search endpoint to query objects by these reference fields.
Template Variations
When you need the same base schema with minor differences, for example, a "VIP Ticket" vs. "General Ticket", use template variations rather than creating separate templates. Variations inherit the parent template's schema and faces, overriding only the fields that differ.
// TypeScript SDKconst vipVariation = await client.templates.createVariation("tmpl_base_ticket",{name: "VIP Ticket",properties: { tier: "vip", lounge_access: true }});
Designing for Search
The Public API (Indexer) supports filtering on any top-level property. To keep searches performant:
- Keep property values short, avoid storing large blobs inline
- Use the Storage API for images, PDFs, and other binary assets; store the returned URL as a property
- Add a
statusorstateproperty if you need to filter by lifecycle stage
Further Reading
- Templates, core concept deep-dive
- Objects, minting and lifecycle
- Code Samples, end-to-end examples