Entity Spec Schema
This page defines the complete JSON Schema for Entity specifications in Fascia. An Entity is the fundamental business object abstraction -- it encapsulates fields, relationships, a status machine, and invariants. The database storage model (tables, columns, indexes) is automatically derived from the Entity spec.
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["name", "version", "description", "fields", "statusMachine"],
"properties": {
"name": {
"type": "string",
"pattern": "^[A-Z][a-zA-Z0-9]*$",
"description": "PascalCase entity name"
},
"version": {
"type": "integer",
"minimum": 1
},
"description": {
"type": "string"
},
"fields": {
"type": "object",
"minProperties": 1,
"additionalProperties": { "$ref": "#/$defs/Field" }
},
"relationships": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/Relationship" }
},
"statusMachine": { "$ref": "#/$defs/StatusMachine" },
"invariants": {
"type": "array",
"items": { "$ref": "#/$defs/Invariant" }
},
"rowLevelAccess": {
"type": "boolean",
"default": false
},
"ownerField": {
"type": "string"
}
}
}
Top-Level Properties
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | PascalCase entity name (e.g., Reservation, PaymentRecord). Must match ^[A-Z][a-zA-Z0-9]*$. |
version | integer | Yes | Immutable version number, starting at 1. Incremented on each change. |
description | string | Yes | Human-readable description of the entity's purpose. |
fields | object | Yes | Custom fields defined for this entity. At least one field is required. See Field. |
relationships | object | No | Connections to other entities. See Relationship. |
statusMachine | object | Yes | State machine defining allowed states and transitions. See StatusMachine. |
invariants | array | No | Business rules that must always hold true. See Invariant. |
rowLevelAccess | boolean | No | When true, non-admin users can only access rows they own. Defaults to false. |
ownerField | string | No | Field name that references the owning User entity. Required when rowLevelAccess is true. |
Definitions
Field
Each entry in the fields object defines a typed data attribute on the entity.
{
"type": "object",
"required": ["type"],
"properties": {
"type": {
"enum": ["string", "number", "boolean", "date", "datetime", "enum", "uuid", "json", "reference"]
},
"required": { "type": "boolean", "default": false },
"unique": { "type": "boolean", "default": false },
"indexed": { "type": "boolean", "default": false },
"default": {},
"enumValues": {
"type": "array",
"items": { "type": "string" },
"description": "Required when type is 'enum'"
},
"referenceTo": {
"type": "string",
"description": "Target entity name. Required when type is 'reference'."
},
"description": { "type": "string" }
}
}
| Property | Type | Required | Description |
|---|---|---|---|
type | enum | Yes | One of: string, number, boolean, date, datetime, enum, uuid, json, reference. |
required | boolean | No | Whether the field must have a value. Defaults to false. |
unique | boolean | No | Whether a unique constraint is applied. Defaults to false. |
indexed | boolean | No | Whether the field is indexed for query performance. Defaults to false. |
default | any | No | Default value applied when the field is not provided on creation. |
enumValues | string[] | Conditional | List of allowed values. Required when type is "enum". |
referenceTo | string | Conditional | PascalCase name of the target entity. Required when type is "reference". |
description | string | No | Human-readable description of the field. |
Allowed Field Types
| Type | Description | Database Mapping |
|---|---|---|
string | Text data | TEXT or VARCHAR |
number | Numeric data (integer or decimal) | NUMERIC |
boolean | True/false | BOOLEAN |
date | Calendar date without time | DATE |
datetime | Date with time and timezone | TIMESTAMPTZ |
enum | One of a fixed set of string values | TEXT with CHECK constraint |
uuid | UUID identifier | UUID |
json | Arbitrary JSON data | JSONB |
reference | Foreign key to another entity | UUID (FK constraint) |
Relationship
Defines connections between entities.
{
"type": "object",
"required": ["type", "target"],
"properties": {
"type": { "enum": ["hasOne", "hasMany", "belongsTo", "manyToMany"] },
"target": { "type": "string", "description": "Target entity name" },
"foreignKey": { "type": "string" },
"through": { "type": "string", "description": "Join table name for manyToMany" }
}
}
| Property | Type | Required | Description |
|---|---|---|---|
type | enum | Yes | One of: hasOne, hasMany, belongsTo, manyToMany. |
target | string | Yes | PascalCase name of the related entity. |
foreignKey | string | No | Name of the foreign key column. Auto-derived if omitted. |
through | string | No | Join table name. Required for manyToMany relationships. |
Relationship Types
| Type | Cardinality | Foreign Key Location |
|---|---|---|
hasOne | 1:1 | On the target entity |
hasMany | 1:N | On the target entity |
belongsTo | N:1 | On the current entity |
manyToMany | N:M | In a separate join table (specified by through) |
StatusMachine
Every entity must define a status machine, even if simple (e.g., active to deleted). The status machine governs which state transitions are allowed.
{
"type": "object",
"required": ["states", "initialState", "transitions"],
"properties": {
"states": {
"type": "array",
"items": { "type": "string" },
"minItems": 2
},
"initialState": { "type": "string" },
"transitions": {
"type": "array",
"items": { "$ref": "#/$defs/Transition" },
"minItems": 1
}
}
}
| Property | Type | Required | Description |
|---|---|---|---|
states | string[] | Yes | All possible states. Minimum of 2 states. Use lowercase_snake naming (e.g., pending, in_progress). |
initialState | string | Yes | The state assigned to newly created records. Must be in the states array. |
transitions | Transition[] | Yes | List of allowed state transitions. At least one transition is required. |
Rules:
- Every entity must have a status machine.
- Transitions must be explicit -- no wildcard transitions.
initialStatemust be one of the definedstates.
Transition
Defines an allowed state change within a status machine.
{
"type": "object",
"required": ["from", "to"],
"properties": {
"from": { "type": "string" },
"to": { "type": "string" },
"guard": {
"type": "string",
"description": "Value DSL boolean expression. Transition only allowed if true."
}
}
}
| Property | Type | Required | Description |
|---|---|---|---|
from | string | Yes | Source state. Must be in the states array. |
to | string | Yes | Target state. Must be in the states array. |
guard | string | No | A Value DSL boolean expression. The transition is only allowed when this expression evaluates to true. |
Invariant
A business rule that must always hold true for an entity. Invariants are checked after flow execution but before transaction commit (step 6 of the Execution Contract).
{
"type": "object",
"required": ["name", "expression"],
"properties": {
"name": { "type": "string", "description": "camelCase assertion-style name" },
"expression": { "type": "string", "description": "Value DSL boolean expression" },
"message": { "type": "string", "description": "Error message when violated" }
}
}
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | camelCase, assertion-style name (e.g., orderTotalPositive, endDateAfterStart). |
expression | string | Yes | A Value DSL boolean expression. Must evaluate to true for the entity to be valid. |
message | string | No | Human-readable error message returned when the invariant is violated. |
System Fields
Every entity automatically includes the following system fields. Do not define these manually in the fields object -- they are injected by the platform.
| Field | Type | Description |
|---|---|---|
id | uuid | Primary key. Auto-generated on creation. |
createdAt | datetime | Timestamp set automatically on creation. |
updatedAt | datetime | Timestamp set automatically on every update. |
deletedAt | datetime (nullable) | Set when the record is soft-deleted. null for active records. |
version | integer | Optimistic locking counter. Starts at 1, incremented on each update. |
Example
A complete Reservation entity spec:
{
"name": "Reservation",
"version": 1,
"description": "A vehicle reservation made by a customer",
"fields": {
"customerId": {
"type": "reference",
"referenceTo": "Customer",
"required": true,
"indexed": true,
"description": "The customer who made the reservation"
},
"vehicleId": {
"type": "reference",
"referenceTo": "Vehicle",
"required": true,
"description": "The reserved vehicle"
},
"startDate": {
"type": "datetime",
"required": true,
"description": "Reservation start date and time"
},
"endDate": {
"type": "datetime",
"required": true,
"description": "Reservation end date and time"
},
"totalPrice": {
"type": "number",
"required": true,
"description": "Total price for the reservation period"
},
"notes": {
"type": "string",
"description": "Optional notes from the customer"
}
},
"relationships": {
"customer": {
"type": "belongsTo",
"target": "Customer",
"foreignKey": "customerId"
},
"vehicle": {
"type": "belongsTo",
"target": "Vehicle",
"foreignKey": "vehicleId"
},
"payments": {
"type": "hasMany",
"target": "Payment"
}
},
"statusMachine": {
"states": ["pending", "confirmed", "in_progress", "completed", "cancelled"],
"initialState": "pending",
"transitions": [
{ "from": "pending", "to": "confirmed" },
{ "from": "pending", "to": "cancelled" },
{ "from": "confirmed", "to": "in_progress", "guard": "now() >= startDate" },
{ "from": "confirmed", "to": "cancelled" },
{ "from": "in_progress", "to": "completed", "guard": "now() >= endDate" }
]
},
"invariants": [
{
"name": "endDateAfterStart",
"expression": "endDate > startDate",
"message": "End date must be after start date"
},
{
"name": "totalPricePositive",
"expression": "totalPrice > 0",
"message": "Total price must be positive"
}
],
"rowLevelAccess": true,
"ownerField": "customerId"
}
See Also
- Tool Spec Schema -- Defines executable server logic that operates on entities
- Policy Spec Schema -- Rules enforced at design time
- Risk Classification Rules -- How Tool specs are evaluated for safety