Chapter 7 · Volume II — The ADL Domain Language

Type System

7.1 Four-Tier Architecture

ADL uses a closed type system organized into four tiers of increasing specialization, plus a content type category for rich UI fields. Every field in a resource MUST declare a type from this system. No custom types can be defined — the type system is fixed by this specification.

The tiers reflect the level of semantic meaning the platform attaches to the stored value:

  • Tier 0 — Core: Fundamental data types with direct database mappings. Every application uses these.
  • Tier 1 — Extended: Specialized storage types with format-specific behavior. Used when a core type lacks the needed semantics.
  • Tier 2 — Specialized: Types with built-in validation or security behavior. Used for specific data concerns.
  • Tier 3 — Domain: Types with business-domain semantics and format standards. Used for internationalization, finance, and visual identity.
  • Content Types: Types for rich, structured content that requires specialized editing and rendering. Used in CMS, publishing, and UI-heavy domains.

7.2 Tier 0 — Core Types

Ten fundamental types that map directly to database primitives.

TypeDescriptionJSON RepresentationValidation
stringShort text"hello"maxLength (default 255), minLength, pattern, trim, lowercase, uppercase
textLong text"long content..."maxLength (unlimited by default)
integerWhole number42min, max
decimalPrecise number"29.99"precision, scale, min, max. JSON representation is a string to preserve precision.
booleanTrue/falsetrueNone. Accepts true, false, 1, 0.
dateCalendar date"2026-05-17"ISO 8601 date format. min, max.
datetimeDate and time"2026-05-17T14:30:00Z"ISO 8601 timestamp with timezone. min, max.
uuidUniversally unique identifier"550e8400-e29b-41d4-a716-446655440000"UUID v4 format. Auto-generated on create unless allowUserProvided: true.
emailEmail address"user@example.com"RFC 5322 format validation.
urlWeb address"https://example.com"URL format validation with scheme. maxLength.

Usage Notes

string vs text: Use string for short, constrained values (names, titles, codes) where a maximum length is meaningful. Use text for long-form content (descriptions, bodies, notes) where length is unconstrained. Both map to TEXT in SQLite; in PostgreSQL, string maps to VARCHAR(n) and text maps to TEXT.

decimal precision: The decimal type stores values as strings in the JSON API and in SQLite to avoid floating-point precision loss. In PostgreSQL, it uses NUMERIC(p,s). Always specify precision and scale for financial values:

price:
  type: decimal
  precision: 10
  scale: 2
  min: 0

uuid auto-generation: By default, the id field is auto-generated as a UUID v4 on create. The client-submitted value is ignored. To allow client-provided IDs (for imports, migrations, or deterministic seeding), set allowUserProvided: true:

id:
  type: uuid
  primaryKey: true
  allowUserProvided: true

7.3 Tier 1 — Extended Types

Six types with format-specific behavior beyond core storage.

TypeDescriptionJSON RepresentationBehavior
floatFloating-point number3.14159Native float. Use decimal for financial precision.
timestampUnix epoch1716000000Stored as integer. Converted to/from ISO 8601 in API.
slugURL-safe identifier"my-blog-post"Auto-generated from a source field. Always unique.
jsonArbitrary JSON{"key": "value"}Stored as-is. No schema validation (use component for typed JSON).
markdownMarkdown text"# Title\n\nContent"Stored as raw markdown string. Rendering is a UI concern.
autoincrementSequential integer42Auto-assigned, monotonically increasing. Read-only.

Slug Auto-Generation

A slug field requires a from attribute specifying the source field:

slug:
  type: slug
  unique: true
  from: title

The slug is generated by converting the source field’s value to lowercase, replacing spaces and non-alphanumeric characters with hyphens, and removing consecutive hyphens. The slug is regenerated on create and whenever the source field changes on update.

If the generated slug conflicts with an existing record’s slug (violating the unique constraint), the server appends a numeric suffix (-1, -2, etc.) until the slug is unique.


7.4 Tier 2 — Specialized Types

Five types with built-in validation or security behavior.

TypeDescriptionJSON RepresentationBehavior
phonePhone number"+14155551234"Format validation. Use pattern for E.164: "^\\+[1-9]\\d{1,14}$".
timeTime of day"14:30:00"ISO 8601 time format.
passwordHashed credentialNever returned in responsesAutomatically hashed with Argon2id on write. Implies writeOnly: true.
tokenOpaque token"abc123def456"Auto-generated random string. Read-only after creation.
arrayTyped list["a", "b", "c"]Ordered array with item type validation.

Array Configuration

The array type supports typed items and length constraints:

tags:
  type: array
  items:
    type: string
  minItems: 1
  maxItems: 10
  uniqueItems: true
ConstraintTypeDescription
items.typestringType of each array element
minItemsintegerMinimum number of elements
maxItemsintegerMaximum number of elements
uniqueItemsbooleanIf true, reject duplicate values

Arrays are stored as JSON strings in the database.


7.5 Tier 3 — Domain Types

Nine types with business-domain semantics tied to international standards.

TypeDescriptionJSON RepresentationStandardValidation
moneyMonetary amount"1234.5678"Implicit precision 19, scale 4. String in JSON.
percentagePercentage value75.5Range validation. Stored as decimal.
currencyCurrency code"USD"ISO 42173-letter uppercase code.
countryCountry code"US"ISO 3166-1 alpha-22-letter uppercase code.
languageLanguage code"en"ISO 639-12-letter lowercase code.
timezoneTimezone identifier"America/New_York"IANAValidated against the IANA timezone database.
colorColor value"#FF5733"Hex color format (#RRGGBB).
bytesBinary dataBase64 stringmaxSize in bytes.
setUnique value collection["admin", "editor"]Like array with uniqueItems: true enforced.

Money Type

The money type is designed for financial values where floating-point precision loss is unacceptable:

amount:
  type: money
  min: 0
  required: true

Money values are stored as strings in the API (JSON) and as TEXT in SQLite / NUMERIC(19,4) in PostgreSQL. The runtime converts between string and numeric representations for arithmetic operations (computed fields, aggregation).

For currency-aware money fields, pair with a currency field:

price:
  type: money
  min: 0
currency:
  type: currency
  default: USD

7.6 Content Types

Seven types for rich, structured content that requires specialized editors and renderers. These types store complex data as JSON in the database.

media — File References

featuredImage:
  type: media
  accept: [image/jpeg, image/png, image/webp]
  maxSize: 5mb
  minWidth: 800
  minHeight: 600
  aspectRatio: "16:9"
ConstraintTypeDescription
acceptstring[]Allowed MIME types. Wildcards supported: image/*.
maxSizestringMaximum file size with unit: 5mb, 500kb, 1gb.
minWidthintegerMinimum image width in pixels (images only).
maxWidthintegerMaximum image width in pixels.
minHeightintegerMinimum image height in pixels.
maxHeightintegerMaximum image height in pixels.
aspectRatiostringRequired aspect ratio: "16:9", "1:1", "4:3".

Media files are uploaded via multipart/form-data and stored in the media library. The field value is a JSON object containing the file metadata and reference:

{
  "id": "550e8400-...",
  "filename": "hero.jpg",
  "mimeType": "image/jpeg",
  "size": 245760,
  "width": 1920,
  "height": 1080,
  "url": "/media/550e8400-.../hero.jpg"
}

The media library tracks references from resources to media items, enabling orphan detection (§18.2).

svg — Sanitized Vector Graphics

logo:
  type: svg
  maxSize: 50kb

SVG content is sanitized on write to remove potentially dangerous elements and attributes (scripts, event handlers, external references). The sanitizer:

  • Strips <script> elements
  • Strips event handler attributes (onclick, onload, etc.)
  • Strips external references (xlink:href to external URLs)
  • Strips comments and metadata elements
  • Preserves structural SVG elements (<svg>, <path>, <rect>, <circle>, <g>, <text>, etc.)

blocks — Block Editor Content

content:
  type: blocks
  allowedBlocks: [paragraph, heading, image, code, list, callout]
  maxBlocks: 100
ConstraintTypeDescription
allowedBlocksstring[]Block types permitted in this field. If omitted, all block types are allowed.
maxBlocksintegerMaximum number of blocks.

Blocks content is stored as a JSON array of block objects:

[
  {
    "type": "paragraph",
    "content": "Introduction text."
  },
  {
    "type": "heading",
    "content": "Section Title",
    "props": { "level": 2 }
  },
  {
    "type": "code",
    "content": "const x = 42;",
    "props": { "language": "javascript" }
  },
  {
    "type": "callout",
    "content": "Important note.",
    "props": { "type": "warning", "title": "Caution" }
  }
]

Custom block types are defined in the blocks: section of the domain (§18.3).

icon — Icon Set Reference

statusIcon:
  type: icon
  iconSets: [lucide, custom]

Stores an icon identifier in the format set:name (e.g., "lucide:check-circle"). The iconSets constraint limits which icon sets are valid. Icon sets are configured in the icons: section of the domain.

font — Font Reference

headingFont:
  type: font
  sources: [google, system]

Stores a font family name (e.g., "Inter"). The sources constraint limits where fonts can be loaded from. Font sources are configured in the fonts: section of the domain.

component — Nested Object

address:
  type: component
  object:
    street: { type: string, required: true }
    city: { type: string, required: true }
    state: { type: string, maxLength: 2 }
    zip: { type: string, pattern: "^[0-9]{5}$" }
    country: { type: country, default: "US" }
  collapsed: false

A component is a single nested object with typed fields. It is stored as a single JSON column. Components do not have their own id, are not independently queryable, and cannot participate in relationships. Use relationships for data that needs to be shared or queried independently.

The object definition MAY use $ref to reference a shared object:

seo:
  type: component
  object: { $ref: '#/objects/SEO' }
  collapsed: true
ConstraintTypeDescription
objectobjectField definitions for the nested object (inline or $ref).
collapsedbooleanUI hint: whether to render collapsed by default. Default false.
requiredbooleanWhether the component itself is required. Individual fields within the component have their own required settings.

repeater — Ordered Array of Objects

lineItems:
  type: repeater
  sortable: true
  minItems: 1
  maxItems: 50
  object:
    productId: { type: uuid, required: true }
    quantity: { type: integer, min: 1, required: true }
    unitPrice: { type: decimal, required: true }
    notes: { type: text }

A repeater is an ordered array of objects, each conforming to the same field definitions. It is stored as a single JSON array column.

ConstraintTypeDescription
objectobjectField definitions for each array element (inline or $ref).
sortablebooleanUI hint: enable drag-and-drop reordering. Default false.
minItemsintegerMinimum number of entries.
maxItemsintegerMaximum number of entries.

7.7 Field Constraints

Constraints restrict the values a field can hold. They are declared alongside the field type and are enforced at the API boundary on every create and update operation.

Universal Constraints

Available on all field types:

ConstraintTypeDescription
requiredbooleanIf true, the field MUST be present and non-null on create. Default false.
uniquebooleanIf true, no two records may have the same value. Enforced by a database index. Default false.
defaultanyDefault value applied when the field is omitted on create.
immutablebooleanIf true, the field cannot be changed after creation. Update attempts with a different value are rejected with 422. Default false.
nullablebooleanIf true, the field explicitly accepts null. Default true for non-required fields.
descriptionstringHuman-readable description. Included in OpenAPI spec.

String Constraints

ConstraintTypeDescription
maxLengthintegerMaximum character count.
minLengthintegerMinimum character count.
patternstringRegular expression the value must match.
trimbooleanIf true, strip leading/trailing whitespace on write.
lowercasebooleanIf true, convert to lowercase on write.
uppercasebooleanIf true, convert to uppercase on write.
enumstring or string[]Restrict to a fixed set of values (inline list or enum name).

Numeric Constraints

ConstraintTypeDescription
minnumberMinimum value (inclusive).
maxnumberMaximum value (inclusive).
precisionintegerTotal number of digits (for decimal).
scaleintegerNumber of digits after the decimal point (for decimal).

Date/Time Constraints

ConstraintTypeDescription
minstringEarliest allowed value (ISO 8601).
maxstringLatest allowed value (ISO 8601).

Enum Constraint

The enum constraint restricts a string field to a fixed set of values. It can be declared inline or by reference:

# Inline enum values
priority:
  type: string
  enum: [low, medium, high, critical]
  default: medium

# Reference to a domain-level enum
status:
  type: string
  enum: PostStatus

# Or via $ref
status:
  $ref: '#/enums/PostStatus'
  default: draft

All three forms produce identical runtime behavior. The $ref form is preferred when the enum carries rich metadata (§9).

Field Security

ConstraintTypeDescription
writeOnlybooleanIf true, the field is never included in read responses. Used for secrets. Implied by password type.
internalbooleanIf true, the field is never exposed via the API (403 on write attempt). Used for system-internal fields.
hiddenbooleanIf true, the field is omitted from responses by default but can be included with ?fields=.
requestablebooleanIf true and hidden: true, the field can be requested via ?fields=. Default true.

7.8 Auto-Generated Fields

Several field types have auto-generation behavior managed by the server:

autoNow

The autoNow property automatically sets a datetime field to the current timestamp:

createdAt:
  type: datetime
  autoNow: create    # Set once on creation, never updated

updatedAt:
  type: datetime
  autoNow: always    # Set on every create and update
ValueBehavior
createSet to now() on create. Never modified on update.
updateSet to now() on every update. Not set on create.
alwaysSet to now() on create and on every update.

Fields with autoNow are auto-managed — client-submitted values for these fields are silently ignored.

Slug Auto-Generation

slug:
  type: slug
  unique: true
  from: title

The from property specifies the source field. The slug is regenerated whenever the source field changes. See §7.3 for the generation algorithm.

UUID Auto-Generation

Fields with type: uuid and primaryKey: true are auto-generated as UUID v4 on create. Client-submitted values are ignored unless allowUserProvided: true:

id:
  type: uuid
  primaryKey: true
  allowUserProvided: true    # Accept client-provided UUIDs

Autoincrement

orderNumber:
  type: autoincrement

Autoincrement fields are assigned the next sequential integer on create. They are read-only — client-submitted values are ignored. Autoincrement values are never reused, even after deletion.


7.9 Complete Type Reference

CategoryTypeJSON TypeSQLitePostgreSQLConstraints
Tier 0stringstringTEXTVARCHAR(n)maxLength, minLength, pattern, trim, lowercase, uppercase, enum
textstringTEXTTEXTmaxLength
integernumberINTEGERINTEGERmin, max
decimalstringTEXTNUMERIC(p,s)precision, scale, min, max
booleanbooleanINTEGERBOOLEAN
datestringTEXTDATEmin, max
datetimestringTEXTTIMESTAMPTZmin, max, autoNow
uuidstringTEXTUUIDprimaryKey, allowUserProvided
emailstringTEXTVARCHAR(255)RFC 5322 validation
urlstringTEXTTEXTURL format validation, maxLength
Tier 1floatnumberREALDOUBLE PRECISIONmin, max
timestampnumberINTEGERBIGINTmin, max
slugstringTEXTVARCHAR(255)from, unique
jsonobjectTEXTJSONB
markdownstringTEXTTEXTmaxLength
autoincrementnumberINTEGERSERIALread-only
Tier 2phonestringTEXTVARCHAR(20)pattern
timestringTEXTTIME
passwordstringTEXTTEXTwriteOnly, Argon2id hash
tokenstringTEXTTEXTauto-generated, read-only
arrayarrayTEXTJSONBitems, minItems, maxItems, uniqueItems
Tier 3moneystringTEXTNUMERIC(19,4)min, max
percentagenumberREALNUMERIC(7,4)min, max
currencystringTEXTVARCHAR(3)ISO 4217
countrystringTEXTVARCHAR(2)ISO 3166-1 alpha-2
languagestringTEXTVARCHAR(2)ISO 639-1
timezonestringTEXTTEXTIANA validation
colorstringTEXTVARCHAR(7)Hex format
bytesstringBLOBBYTEAmaxSize
setarrayTEXTJSONBitems, uniqueItems enforced
ContentmediaobjectTEXTJSONBaccept, maxSize, dimensions, aspectRatio
svgstringTEXTTEXTmaxSize, auto-sanitized
blocksarrayTEXTJSONBallowedBlocks, maxBlocks
iconstringTEXTTEXTiconSets
fontstringTEXTTEXTsources
componentobjectTEXTJSONBobject, collapsed
repeaterarrayTEXTJSONBobject, sortable, minItems, maxItems

Total: 37 types across 5 categories.


Spatial type (v0.3.1): the geometry / geojson field type — GeoJSON values with optional geometryType/srid, PostGIS-backed storage, and spatial query operators — is specified in Chapter 41 — GIS & Spatial Data.

End of Chapter 7.

Next: Chapter 8 — Relationships