DevMatrixDocumentation
Back to Home
Language Reference

DMX Specification Language

The complete reference for writing DevMatrix specifications. From data types and annotations to flows, state machines, and infrastructure.

Overview

DMX (DevMatrix Specification Language) is a declarative language for defining full-stack microservices. A single .dmx file describes entities, APIs, business flows, state machines, security, infrastructure, and more — then compiles deterministically to production-ready code. DMX is not a template engine. It is a compiler IR: every construct maps to concrete backend code through deterministic passes. There is no interpolation, no scripting, and no runtime interpretation.

Lexical Structure

DMX source files use UTF-8 encoding. The language is whitespace-insensitive (indentation is conventional, not syntactic). Blocks are delimited by curly braces {}.

Comments

Line comments start with //. Block comments use /* ... */.
Comments
// This is a line comment

/* This is a
   block comment */

Strings

Strings are double-quoted only. Backslash escapes are supported: \", \\, \n.

Numbers

Integer literals: 42, 0. Float literals: 3.14. No hex/octal support.

Identifiers

PascalCase for entities and types (UserProfile). snake_case for fields and keywords (created_at). UPPER_SNAKE_CASE for enum values (ACTIVE, PENDING_REVIEW).

Operators

= — assignment. -> — state transition or mapping. * — wildcard/all.

Annotations

Annotations start with @ followed by an identifier. Some accept parenthesized arguments: @default("active"), @fk(User.id).

Dollar Tokens

Runtime references prefixed with $. Simple: $input, $context. Dotted: $computed.now, $input.email.

Data Types

DMX supports 18 data types that map to database column types and API schema types.

NameDescription
UUIDUniversally unique identifier (v4)
StringVariable-length text, up to 255 characters
TextUnlimited-length text (CLOB/TEXT)
IntegerWhole number (32-bit signed)
FloatIEEE 754 floating-point
DecimalArbitrary-precision decimal
Booleantrue or false
DateCalendar date without time (YYYY-MM-DD)
DateTimeDate and time with timezone (ISO 8601)
TimeTime of day without date
JSONArbitrary JSON object or array
BytesBinary data (BLOB)
ArrayOrdered collection of a single type
EnumEnumerated set of named values
CharFixed-length character string
PointGeographic point (latitude, longitude)
PolygonGeographic polygon boundary
GeometryArbitrary geometric shape

Every entity field has a name, a type, and optional annotations.

Field declarations with types
entity User {
  id          UUID     @pk @default($computed.uuid)
  email       String   @unique @not_null
  name        String   @not_null
  bio         Text     @nullable
  age         Integer  @check("age >= 0")
  balance     Decimal  @default(0)
  is_active   Boolean  @default(true)
  joined_at   DateTime @default($computed.now)
  metadata    JSON     @nullable
  role        Enum(ADMIN, MEMBER, GUEST) @default(MEMBER)
  location    Point    @nullable
}

Annotations

Annotations modify the behavior of fields, entities, APIs, state machines, and ClickHouse columns.

Field Annotations

NameDescriptionSyntax
@pkPrimary key@pk
@not_nullNOT NULL constraint@not_null
@uniqueUnique constraint@unique
@nullableAllows NULL values@nullable
@defaultDefault value@default("value") or @default($computed.now)
@fkForeign key reference@fk(Entity.field)
@checkCheck constraint expression@check("age >= 18")
@requiredRequired in API input@required
@sensitivePII / secrets — excluded from logs@sensitive
@computedServer-computed, not in API input@computed
@json_listStore as JSON array column@json_list
@immutableCannot be updated after creation@immutable
@indexedCreate a database index@indexed
@freetextFree human text — fixes the injection security class@freetext
@scopedABAC row-filter: binds this column to a principal claim (scoped / scope_match strategy)@scoped(claim="sub")
@scope_anchorABAC scope anchor: when a child has N>1 @fk to the same @scoped parent, marks WHICH FK anchors the inherited transitive row-scope (else first-declared anchors)@fk(User.id) @scope_anchor
@scope_reverseABAC reverse scope marker: on a junction (M2M bridge) entity's parent-@fk column, grants the parent reverse row-visibility to rows reachable through the junction (reverse-EXISTS RLS). Junction (@role(membership)) @fk columns only.@fk(Org.id) @scope_reverse
@formatOpenAPI format hint (closed vocab: email, uuid, date-time, url, …)@format(email)
@patternExplicit validation regex@pattern("^[A-Z]{3}$")
@validation_patternValidation pattern type (snapshot, calculation, immutability, constraint, relationship)@validation_pattern(calculation)
@seed_templateSeed-data value shape (email, personal_name, url, phone, description, currency_code, timestamp)@seed_template(email)
@field_purposeSemantic field purpose (closed allowlist: auth_failed_attempts, auth_locked_until, auth_last_login, auth_email_verified, password) — declares a capability-owned auth field (or the credential password) so emitters route on the structural role instead of name-matching@field_purpose(auth_locked_until)
@deprecatedMarks field deprecated (since / until / replacement)@deprecated(since: "1.2", replacement: "new_field")

Entity Annotations

NameDescriptionSyntax
@aggregate_rootDDD aggregate root — owns child entities@aggregate_root
@tenancyTenant isolation strategy@tenancy("row") or @tenancy("schema")
@descriptionHuman-readable description@description("User accounts")
@auditAuto-generate created_at / updated_at / deleted_at@audit
@soft_deleteSoft delete (set deleted_at) instead of hard delete@soft_delete
@platform_scopeNot tenant-scoped (platform-level entity)@platform_scope
@append_onlyInsert-only table (no UPDATE or DELETE)@append_only
@append_only_afterAppend-only after a condition is met@append_only_after("status = CLOSED")
@unique_togetherComposite unique constraint@unique_together(field_a, field_b)
@checkEntity-level check constraint@check("start_date < end_date")
@relationDefine relationship to another entity@relation("has_many", Target)
@roleEntity role classification (domain, user, credential, workspace, membership, audit, infra)@role(audit)
@deprecatedMarks entity deprecated (since / until / replacement)@deprecated(since: "2.0", replacement: "Order")

API Annotations

NameDescriptionSyntax
@entityBind API to an entity@entity(User)
@paginatedEnable pagination@paginated
@statusOverride HTTP status code@status(201)
@triggersEmit events on action@triggers(user_created)
@self_resourceEndpoint operates on the caller's own resource (self-access)@self_resource
@security_tierEndpoint security tier@security_tier = "public"
@deprecatedMarks endpoint deprecated (since / until / replacement)@deprecated(since: "2.0", replacement: "/v2/users")

State Machine Annotations

NameDescriptionSyntax
@rolesRestrict transition to roles@roles(ADMIN, MANAGER)
@guardGuard condition for transition@guard("amount > 0")

ClickHouse Annotations

NameDescriptionSyntax
@low_cardinalityClickHouse LowCardinality optimization@low_cardinality
@piiMark column as PII for compliance@pii

Dollar Tokens ($)

Runtime value references used in defaults, flow steps, and computed fields. The compiler resolves these to actual code.

NameDescriptionSyntax
$inputCurrent request/step input data$input.email, $input.password
$contextExecution context (authenticated user, tenant, request metadata)$context.user_id, $context.tenant_id
$instanceCurrent entity instance (in flows and state machines)$instance.status, $instance.id
$resultResult from the previous flow step$result.id, $result.token
$computed.nowServer timestamp (UTC) — 0 args@default($computed.now)
$computed.now_plus_minutesTimestamp + N minutes — 1 arg$computed.now_plus_minutes(15)
$computed.now_minus_minutesTimestamp − N minutes — 1 arg$computed.now_minus_minutes(5)
$computed.now_plus_hoursTimestamp + N hours — 1 arg$computed.now_plus_hours(24)
$computed.now_minus_hoursTimestamp − N hours — 1 arg$computed.now_minus_hours(72)
$computed.now_plus_daysTimestamp + N days — 1 arg$computed.now_plus_days(7)
$computed.now_minus_daysTimestamp − N days — 1 arg$computed.now_minus_days(30)
$computed.token_expiryDefault 24-hour expiry — 0 argsexpires_at = $computed.token_expiry
$computed.uuidGenerate UUID v4 — 0 args@default($computed.uuid)
$computed.random_token32-byte URL-safe random token — 0 argsreset_token = $computed.random_token
$computed.secure_randomn-byte URL-safe cryptographically-secure token (secrets.token_urlsafe) — 1 arg (n); author-supplied width vs fixed-width random_tokenapi_key = $computed.secure_random(48)
$computed.recovery_codesList of N 8-byte recovery codes — 1 arg$computed.recovery_codes(10)
$computed.token_hashSHA-256 hex digest of token — 1 argrefresh_hash = $computed.token_hash($result.refresh_plaintext)
$computed.hashAlias of token_hash; SHA-256 hex digest — 1 arg$computed.hash($input.payload)
$computed.verify_hashReturns bool — sha256(value) == hash — 2 args$computed.verify_hash($instance.token_hash, $input.token)
$computed.hash_passwordBCrypt hash via pwd_context — 1 argpassword_hash = $computed.hash_password($input.password)
$computed.bcryptAlias of hash_password; BCrypt hash — 1 argpassword_hash = $computed.bcrypt($input.password)
$computed.bcrypt_verifyReturns bool — pwd_context.verify — 2 args (crypto-safe for passwords)$computed.bcrypt_verify($input.password, $instance.password_hash)
$computed.bcrypt_randomFresh unguessable bcrypt hash of a random 32-byte seed — 0 args. Canonical for credential-invalidation (pair with requires_reset=true).password_hash = $computed.bcrypt_random
$computed.totp_secretGenerate base-32 TOTP secret — 0 argsmfa_secret = $computed.totp_secret
$computed.verify_totpReturns bool — verify TOTP code — 2 args$computed.verify_totp($instance.mfa_secret, $input.code)
$computed.utf8_decodeCoerce bytes→str (value.decode()) — 1 arg; synthesized for BINARY/encrypted secrets$computed.verify_totp($computed.utf8_decode($instance.mfa_secret), $input.code)
$computed.decode_jwtDecode and validate JWT — 1 argclaims = $computed.decode_jwt($input.token)
$computed.jwt_signIssue JWT access token — 3 args (subject, tenant_id, role). Delegates to create_access_token(); claims declared explicitly in the spec, no implicit context-pickup.access_token = $computed.jwt_sign($instance.id, $context.tenant_id, "PATIENT_PORTAL_ACCOUNT")
$computed.system_admin_role_idTenant-scoped SYSTEM_ADMIN role UUID lookup — 1 arg (tenant_id). Companion gate G_TENANT_ROLE_ONBOARDING_PROVIDED enforces the seed flow.role_id = $computed.system_admin_role_id($context.tenant_id)
$computed.prefixLeading slice value[:n] — canonical short-code / key-prefix derivation — 2 args (value, n); pure stdlibshort_code = $computed.prefix($instance.raw_key, 8)
$computed.s3_signTime-limited presigned download URL via the object-storage client — 3 args (bucket, key, ttl_s); provider-dispatched, compiler emits only the delegating calldownload_url = $computed.s3_sign($instance.bucket, $instance.object_key, 3600)
$computed.s3_object_sha256SHA-256 hex digest of a stored object's bytes — 2 args (bucket, key); tamper-evident content seal for evidence/audit/integrity manifestscontent_hash = $computed.s3_object_sha256($instance.bucket, $instance.object_key)
$computed.tenant_sequenceCanonical generic per-tenant monotonic sequence — 2 args (name, tenant_id); name is a DSL value (string literal), domain-agnostic. Supersedes nc_sequence/protocol_sequence (ADR-346).invoice_number = $computed.tenant_sequence("invoice_number", $context.tenant_id)
$computed.nc_sequenceLegacy domain-named alias of tenant_sequence("nc_number", tenant_id) — 1 arg (tenant_id). Superseded by the generic form (ADR-346).nc_number = $computed.nc_sequence($context.tenant_id)
$computed.protocol_sequenceLegacy domain-named alias of tenant_sequence("protocol_number", tenant_id) — 1 arg (tenant_id). Superseded by the generic form (ADR-346).protocol_number = $computed.protocol_sequence($context.tenant_id)

Top-Level Blocks

DMX specs are composed of top-level blocks. Each block defines a different concern of the service.

service

The root block that defines a microservice. Contains all other blocks.
Service definition
service UserService {
  version = "1.0.0"
  port = 8000
  api_prefix = "/api/v1"
  database = "postgresql"

  // ... entities, api, flows, etc.
}

identity

Optional nested sub-block inside service { ... } that lets the spec author override canonical-derived service-identity fields. Mirrors the MicroservicesIR sub-IR hierarchy (ServiceIdentityIR / GatewayIR / ArchitectureIR). Each attribute is closed-taxonomy parse-time enforced. When the block is absent the producer derives every field canonically (service_id from the service block name, service_slug + internal_hostname from the canonical lowercase slug, internal_port from the infrastructure block, etc.). When present, every set attribute lands on the corresponding sub-IR overridden_fields frozenset sentinel for cross-build provenance.

All 12 spec-declarable attrs in a single block. Each sub-block keeps the canonical-strict closed taxonomy enforcement (GATEWAY_MODE = passthrough | auth_precheck; ARCHITECTURE_MODE = standalone | microservices | mixed; ARCHITECTURE_MODE_SOURCE = explicit | inferred | default).

Identity overrides — full surface
service AuthService {
  version    = "1.0.0"
  port       = 8000
  api_prefix = "/api"

  identity {
    service_name      = "Auth Service"
    service_version   = "2.4.1"
    service_slug      = "auth-svc"
    internal_hostname = "auth-svc.svc.cluster.local"
    internal_port     = 8081
    description       = "identity overrides"

    gateway {
      enabled          = true
      public_port      = 8443
      public_base_path = "/v2"
      mode             = auth_precheck
    }

    architecture {
      mode        = microservices
      mode_source = explicit
    }
  }
}

Spec authors only pin the attributes they want to override; the rest canonical-derive. Here the platform pins a service version explicitly, leaving every other identity field on its canonical default.

Identity overrides — single attribute
service AuthService {
  version    = "1.0.0"
  port       = 8000
  api_prefix = "/api"

  identity {
    service_version = "2.4.1"
  }
}

entity

Defines a data model. Fields have a name, type, and optional annotations. Entities compile to database tables, ORM models, and Pydantic schemas.
Entity with annotations
entity Order @audit @soft_delete {
  id          UUID      @pk @default($computed.uuid)
  customer_id UUID      @fk(Customer.id) @not_null
  total       Decimal   @not_null @check("total >= 0")
  status      Enum(DRAFT, PENDING, PAID, SHIPPED, CANCELLED) @default(DRAFT)
  notes       Text      @nullable
}

api

Defines REST, GraphQL, or WebSocket endpoints for an entity. Route-level RBAC via permissions = ["resource:action"] is enforced end-to-end — the compiler emits the permission check in the generated service (ADR-354), not advisory metadata. Each key is resource:action (2+ non-empty colon-separated segments).
REST API
api REST @entity(Order) @paginated {
  // Standard CRUD endpoints are generated automatically
  // Custom endpoints:
  POST   "/orders/{id}/cancel"  @status(200) @triggers(order_cancelled)
  GET    "/orders/summary"      @roles(ADMIN)
}

flow

Multi-step business process. Each step has an action, target entity, and field mappings. Flows compile to service-layer functions with transaction management. A flow must have either at least one step OR an intentional_stub = "reason" declaration (not both): a stepless intentional_stub declares an endpoint that is deliberately unimplemented (compiles to a declared NotImplementedError instead of a silent 501).
Registration flow
flow RegisterUser {
  step validate_input {
    action = validate
    target = User
    mapping {
      email = $input.email
      password = $input.password
    }
  }

  step create_user {
    action = create
    target = User
    mapping {
      id = $computed.uuid
      email = $input.email
      password = $computed.hash_password($input.password)
      created_at = $computed.now
    }
  }

  step generate_token {
    action = calculate
    expression = $computed.sign_jwt($result.id, $result.role)
    result_capture = "token"
  }
}

state_machine

Defines valid state transitions with guards and role restrictions. Compiles to a state machine implementation with validation.
Order state machine
state_machine OrderStatus {
  initial = DRAFT

  DRAFT -> PENDING @roles(MEMBER, ADMIN)
  PENDING -> PAID @guard("payment_confirmed == true")
  PAID -> SHIPPED @roles(ADMIN)
  SHIPPED -> DELIVERED
  PENDING -> CANCELLED @roles(MEMBER, ADMIN)
  DRAFT -> CANCELLED
}

validations

Validation rules applied to entity fields. Compile to both database constraints and API-level validation.
Validation rules
validations {
  email {
    format = "email"
    uniqueness = true
  }
  age {
    range = "0..150"
    presence = true
  }
  username {
    format = "^[a-zA-Z0-9_]{3,30}$"
    uniqueness = true
  }
}

security

Authentication, authorization, rate limiting, and password policies.
Security configuration
security {
  auth_scheme = "jwt"
  jwt_algorithm = "HS256"
  token_expiry_minutes = 60
  refresh_token_enabled = true
  password_hashing = "bcrypt"

  rate_limit {
    policy = "sliding_window"
    allow = 100
    for = "1m"
  }

  roles = [ADMIN, MEMBER, GUEST]
}

When the password hash lives on a separate credential entity carrying a credential-kind enum, `credential_type_field` names that enum field and `password_credential_kind` names the member that denotes a password credential. Both keys are optional; omitting them is byte-equal.

Credential-split discriminator (optional)
security {
  auth_scheme = "jwt"
  user_entity = "User"
  active_field = "is_active"

  credential_type_field = "credential_type"
  password_credential_kind = "password"
}

errors

Custom error definitions with HTTP status codes, categories, and messages.
Error definitions
errors {
  USER_NOT_FOUND {
    status = 404
    category = "not_found"
    message = "User not found"
  }
  INVALID_CREDENTIALS {
    status = 401
    category = "auth"
    message = "Invalid email or password"
  }
}

jobs

Platform-scope scheduled jobs (ADR-260) — declared inside platform { jobs { ... } }; the per-service top-level jobs block was retired. Scheduling, retry policies, and queue routing.
Job definitions
jobs {
  cleanup_expired_tokens {
    schedule = "0 */6 * * *"
    timeout = "5m"
    retry {
      max_attempts = 3
      backoff = "exponential"
    }
  }
  send_daily_digest {
    schedule = "0 8 * * *"
    queue = "low_priority"
  }
}

notifications

Multi-channel notification configuration (email, SMS, WhatsApp, push).
Notification setup
notifications {
  providers {
    email { adapter = "smtp" }
    sms   { adapter = "twilio" }
    push  { adapter = "firebase" }
  }
  order_confirmed {
    channels = [email, push]
    recipient = $instance.customer.email
    template = "order_confirmed"
    locale = $context.locale
  }
}

file_storage

File upload configuration with size limits, type restrictions, and CDN.
File storage
file_storage {
  provider = "s3"
  bucket = "my-app-uploads"
  max_size = "10MB"
  allowed_types = ["image/png", "image/jpeg", "application/pdf"]
  virus_scan = true
  cdn_enabled = true
}

infrastructure

Docker, Redis, RabbitMQ, Celery, API gateway, CORS, and deployment configuration.
Infrastructure config
infrastructure {
  docker {
    compose_version = "3.8"
    base_image = "python:3.12-slim"
  }
  redis {
    broker = true
    cache = true
    pool_size = 10
  }
  celery {
    concurrency = 4
    queue = "default"
  }
  cors {
    allow = ["https://app.example.com"]
  }
}

Canonical message broker block — AMQP 0.9.1 with vhost isolation, env-bound credentials, optional TLS, declared exchanges + queues with DLQ + quorum-queue HA replication, management plugin, cluster discovery, publisher confirms + mandatory routing.

RabbitMQ broker (Wave 1 + Wave 2 capabilities)
infrastructure {
  rabbitmq {
    vhost = "/tenant_a"
    user_env = "RABBITMQ_USER"
    password_env = "RABBITMQ_PASSWORD"
    use_tls = true
    tls_ca_path_env = "RABBITMQ_CA_PATH"
    pool_size = 10
    max_overflow = 5
    heartbeat_seconds = 60
    prefetch_count = 50
    quorum_replication_factor = 3
    publisher_confirms = true
    mandatory_routing = true
    management {
      port = 15672
      user_env = "RABBITMQ_MGMT_USER"
      password_env = "RABBITMQ_MGMT_PASSWORD"
    }
    cluster {
      discovery_mode = "dns"
      static_peers = ["rmq-0", "rmq-1", "rmq-2"]
    }
    exchange "events" {
      type = "topic"
      durable = true
    }
    queue "events.q" {
      durable = true
      dead_letter_exchange = "events.dlx"
      dead_letter_routing_key = "events.dlq"
      max_length = 100000
      quorum = true
    }
  }
}

Canonical Kafka event-streaming block — SASL/SSL + env-bound credentials, optional TLS keystore/truststore, per-topic partitions + replication + min-in-sync + ACLs + retention, idempotent producer + canonical consumer group, optional Confluent Schema Registry binding. Compiler emits Docker Compose with KRaft mode (Zookeeper-less) + K8s StatefulSet broker cluster + optional Schema Registry sidecar.

Kafka event streaming (Wave 1 + Wave 2 capabilities)
infrastructure {
  kafka {
    bootstrap_servers_env = "KAFKA_BOOTSTRAP_SERVERS"
    security_protocol = "SASL_SSL"
    sasl_mechanism = "SCRAM-SHA-512"
    sasl_user_env = "KAFKA_SASL_USER"
    sasl_password_env = "KAFKA_SASL_PASSWORD"
    tls_truststore_path_env = "KAFKA_TLS_TRUSTSTORE_PATH"
    tls_truststore_password_env = "KAFKA_TLS_TRUSTSTORE_PASSWORD"
    consumer_group_id = "svc_a.consumer"
    auto_offset_reset = "earliest"
    producer_acks = "all"
    producer_idempotence = true
    compression_type = "zstd"
    schema_registry {
      url_env = "KAFKA_SCHEMA_REGISTRY_URL"
      auth_user_env = "SR_USER"
      auth_password_env = "SR_PASSWORD"
    }
    topic "events" {
      partitions = 6
      replication_factor = 3
      min_insync_replicas = 2
      retention_ms = 604800000
      cleanup_policy = "compact"
      acl_read_principals = ["User:svc_a"]
      acl_write_principals = ["User:svc_publisher"]
    }
  }
}

Canonical Elasticsearch block — cluster hosts env binding, HTTPS + cert verification + CA bundle, auth method (basic_auth / api_key / bearer_token) with secret_env binding, per-index tenant_prefix + shards/replicas + ILM lifecycle + ingest pipeline + mappings, ILM phases (hot/warm/cold/delete), snapshot repository, bulk batch + timeout, optional Watcher alerting. Compiler emits Python elasticsearch-py client + Docker Compose service + K8s StatefulSet + put_index_template / put_lifecycle installers.

Elasticsearch search/analytics (Wave 1 + Wave 2 capabilities)
infrastructure {
  elasticsearch {
    hosts_env = "ELASTICSEARCH_HOSTS"
    use_https = true
    verify_certs = true
    ca_bundle_path_env = "ELASTICSEARCH_CA_BUNDLE"
    rbac_roles = ["search_user", "index_admin"]
    bulk_batch_size = 1000
    bulk_timeout_seconds = 60
    watcher_enabled = false
    auth {
      method = "api_key"
      api_key_env = "ELASTICSEARCH_API_KEY"
    }
    index "logs" {
      tenant_prefix = "tenant_a"
      number_of_shards = 3
      number_of_replicas = 2
      min_insync_replicas = 2
      refresh_interval = "30s"
      lifecycle_policy = "logs-policy"
      ingest_pipeline = "enrich-logs"
    }
    ilm_policy "logs-policy" {
      phase "hot" { min_age = "0ms" }
      phase "warm" { min_age = "7d" }
      phase "delete" { min_age = "90d" }
    }
    snapshot_repository "primary" { type = "s3" }
    ingest_pipeline "enrich-logs" { description = "Add tenant tag" }
  }
}

platform

Multi-service platform orchestration. Declares modules with their compile-time dependencies; the compiler derives topological build order automatically.
Platform definition
platform "MyPlatform" {
  version = "1.0.0"

  modules {
    UserService         { spec = "user.dmx";         dependencies = [] }
    OrderService        { spec = "order.dmx";        dependencies = [UserService] }
    NotificationService { spec = "notification.dmx"; dependencies = [OrderService] }
  }

  shared_kernel { modules = [tenant_context, jwt_utils, event_bus, audit_decorator, observability] }

  interactions {
    "order-confirmed-notifies" {
      tier     = 1
      producer = OrderService
      consumer = NotificationService
      trigger  = event("order.confirmed")
      task     = "send_confirmation"
      queue    = "notifications"
    }
  }
}

interactions (cross-service registry · WAVE 0)

Platform-scope cross-service interaction registry, declared inside platform { interactions { ... } }. Each "" { ... } row declares a producerconsumer edge with its tier, trigger, and task/queue. Registry-centric (WAVE 0) enriches a producer edge with these declared attributes: - event_schema = "Name@version" — a versioned reference into the platform event_schemas { } registry. Resolution is *lenient*: a bare Name resolves when the registry holds a single version of it; the @version suffix is required only when two or more versions of that Name are declared. - ordering_key = "..." — advisory per-key ordering hint (also accepted inside delivery { }). - payload_map { = } — envelope projection. Each is a closed discriminator in three kinds: a *literal* (string/number/bool), $instance. (the triggering entity), or $context. (request/tenant context). $computed and $result are not valid inside a payload_map, and $input is not yet supported here. - delivery { ... } — delivery + reliability policy: transport ∈ {celery, redis_streams, kafka, sqs, rabbitmq}, outbox = (transactional outbox), ordering_key, plus nested retry { ... } (max_attempts · backoff · initial_delay_ms · max_delay_ms) and a bare dlq { ... } block (stream_name · max_attempts_before_dlq · dlq_archive_strategy · archive_retention_days) — distinct from the saga dlq_topic / dlq_retention attributes, and reusing the platform-tier worker policy rules verbatim (M4 single-source). The producer derives every field canonically from the registry; the compiler reads the IR directly (no name heuristics). The shapes above are declared-syntax (validator-enforced) — independent of emit behavior.

Row i-11: a producer → consumer edge with a versioned event schema, payload projection from the triggering instance, and a full delivery policy (transport + outbox + retry + DLQ).

Enriched async interaction (registry-centric)
interactions {
  "i-11-order-confirmed-notifies" {
    tier         = 1
    producer     = OrderService
    consumer     = NotificationService
    trigger      = event("order.confirmed")
    task         = "send_confirmation"
    event_schema = "order.confirmed@v2"
    ordering_key = "tenant_id"

    payload_map {
      order_id    = $instance.id
      customer_id = $instance.customer_id
      tenant_id   = $context.tenant_id
    }

    delivery {
      transport    = redis_streams
      outbox       = true
      ordering_key = "tenant_id"
      retry {
        max_attempts     = 5
        backoff          = "exponential"
        initial_delay_ms = 100
      }
      dlq {
        stream_name             = "order-confirmed.dlq"
        max_attempts_before_dlq = 5
      }
    }
  }
}

events

Domain event definitions for event-driven communication between services.

tenant_config

Multi-tenancy configuration: isolation strategy, tenant resolution, and feature flags per tier.

workers (platform-scope, ADR-260)

Platform-scope worker topology: tiers, queue routing patterns, retry + dead-letter policies, concurrency, scaling hints. Declared inside platform { workers { ... } }. Superseded the per-service task_queue block (ADR-211). Optional tasks { "" { consumes_from = [...]; produces_to = [...] } } sub-block (ADR-309 Phase E) declares which entities a task reads/writes, emitting the WorkerTask→Entity CONSUMES_FROM / PRODUCES_TO edges.

row_level_isolation

Row-level security policies for data isolation.

sequences

Ordered sequence generators (e.g., invoice numbers).

observability

Canonical observability surface (ADR-361) — metrics, structured logging, OTEL tracing, correlation, and health probes in one block. Carries the union of the legacy service_observability + infrastructure.observability surfaces plus tracing_enabled / tracing_exporter / log_level_env_var.

service_observability (deprecated alias)

Deprecated alias for observability (same attributes, minus tracing / log_level_env_var). Parses identically; migrate to observability.

object_storage

Platform-scope object storage topology — declares the cloud provider, credentials, and per-bucket configuration. Five providers are supported (closed taxonomy enforced parse-time): s3 / gcs / azure-blob / minio / cloudflare-r2. Each bucket carries name, region, owning_service, access_pattern (read/write/admin/shared), encryption_kind (SSE-S3/SSE-KMS/SSE-C), optional encryption_secret_ref (required for SSE-KMS+SSE-C), presigned_url_ttl_s, and lifecycle_rule blocks (transition/expiration with after_days). Cloudflare R2 rejects SSE-KMS at IR construction (R2 has no KMS integration).

permissions

Role-based and attribute-based access control definitions. The `sod_rules { ... } sub-block declares Separation-of-Duties rules; each named rule lists conflicting_permissions = [...] (the permission set that must not be co-held), an optional scope, breakglass_eligible flag, description, and an enforcement mode. enforcement is a closed taxonomy: block (the DEFAULT — omit and the rule behaves as block — records the conflict at read time and denies with 403, the fail-closed floor), warn and log_only (both record the conflict forensically and then ALLOW the request). Grant-time is always block structurally (not tunable); the enforcement` mode only relaxes read-time behavior. Provided by C-SOD-2.

sagas

Distributed transaction orchestration with compensating actions. Each `saga declares a sequence of step definitions; on failure of any step, prior completed steps run their compensation action in reverse order. Two patterns supported: orchestration (default — central coordinator drives steps) and choreography (event-driven — each step subscribes to the previous step's completion event). Persistence: outbox (default — DB-backed state machine + transactional outbox table for event publishing in the same TX as business state) or in_memory_only (opt-in for non-critical sagas where crash recovery is not required). Brokers: none (in-process — orchestration only), redis (redis-streams), kafka (aiokafka). Choreography requires a non-none broker. Per-step action_handler and compensation_handler are fully-qualified Python references (e.g. src.services.payment.charge); when omitted the compiler defaults to src.services.sagas... idempotency_key_field is the request-payload field used as the unique key for retry / replay safety. max_retries per step overrides the saga-level default. DLQ topic + retention (integer + days|hours` unit) configure the dead-letter queue for failed events that exceed retries. Provided by ADR-319 Distributed Sagas Productive Closure.

Flow Actions

Actions available in flow steps. Each action maps to a specific operation on the target entity or system.

NameDescriptionSyntax
createCreate a new entity instanceaction = create
updateUpdate an existing entityaction = update
deleteDelete an entity instanceaction = delete
queryQuery entities with filtersaction = query
validateRun validation rulesaction = validate
extractExtract fields from dataaction = extract
calculateCompute derived valuesaction = calculate
state_changeTrigger a state machine transitionaction = state_change
external_callCall an external serviceaction = external_call
batchBatch multiple operationsaction = batch
exportExport data to a formataction = export
infraInfrastructure stepaction = infra

Preconditions (Guards)

Structured guards the server enforces BEFORE an operation runs (fail-closed 4xx/409 on violation). A precondition { } block attaches to a command { } and to a flow step with action = validate. It is declared structurally with kind = <kind> plus that kind's attributes — the compiler lowers each kind to a real check, so a guard never ships as an un-lowerable string. The kind set is closed (17 productive + 2 markers); omitting kind defaults to field_value_compare. Every entity/field reference is spec-author-supplied — zero per-domain vocabulary (M2).

NameDescriptionSyntax
field_value_compare (default)Compare a field to a value. operator ∈ eq, not_eq, in, not_in, gt, gte, lt, lte, is_null, is_not_null. Carries error_message / error_code.field, operator, value
role_required_singleThe principal holds the single role.role = "admin"
role_required_any_ofThe principal holds ANY of roles (≥ 2).roles = ["admin", "manager"]
permission_requiredThe principal holds a named permission.permission_name = "invoice:write"
not_expiredA timestamp field is still in the future (not expired).expiry_field = "expires_at"
workspace_presentA workspace/tenant id is present on the request.workspace_source = jwt | header | jwt_or_header
field_presentAn input field is supplied (non-null).field = "email"
field_uniqueThe field value is unique across entity rows.entity = "User", field = "email"
fk_validAn FK field resolves to a live target_entity row.field = "user_id", target_entity = "User"
min_list_cardinalityA list/collection field has at least count items.field = "items", count = 1
no_conflictNo overlapping entity row exists on time_field (time-window conflict).entity = "Booking", time_field = "slot"
token_validA token is valid and unrevoked.token_source = "refresh_token"
entity_existsAn entity row identified by id_field exists.entity = "Order", id_field = "order_id"
entity_activeThe resolved entity row is active (active_field defaults to is_active).entity = "User", active_field = "is_active"
relationship_existsA join-table row links the subject to the object (e.g. reviewer assigned to submission).subject_entity, via_entity, object_entity, subject_field, object_field
self_accessThe acting principal owns the row (owner_field == context user id).entity = "Profile", owner_field = "user_id"
collection_in_stateCross-entity collection-state guard over a JSON-array-of-natural-keys (mode = all_in_state | no_referents_in_state). Lowers to the CountRelatedInState per-tenant aggregate. Carries error_message / error_code.mode, catalog_entity, list_field, key_field, key_value, scoped_entity, state_field, state_value, scope_field
trigger_marker (marker)Non-productive: flags an API/routing-layer trigger handled at the route, not as a guard. No emit.label = "..."
session_scope_marker (marker)Non-productive: flags an auth-service-layer session concern. No emit.session_scope = single | global | current

kind omitted on the first block → field_value_compare. error_message / error_code apply to field_value_compare and collection_in_state; other kinds derive their fail-closed response from the kind.

Two guards on a command
command CloseInvoice {
  entity = Invoice
  verb   = "close"
  precondition {                       // kind omitted → field_value_compare
    field         = "status"
    operator      = eq
    value         = "open"
    error_message = "Invoice must be open to close"
    error_code    = "INV_409"
  }
  precondition {
    kind  = role_required_any_of
    roles = ["billing_admin", "finance_manager"]
  }
}

Trigger Functions

Functions used in @triggers annotations and event routing.

NameDescriptionSyntax
event()Emit a domain event@triggers(event("user_created"))
job()Enqueue a background job@triggers(job("send_welcome_email"))
state_change()Trigger a state machine transition@triggers(state_change("activate"))

Platform Spec

A platform spec declares the architecture of a multi-service application. It lives in a separate platform.dmx file and is never compiled directly — it provides context when compiling individual service specs.

Two file types in DMX: - Platform spec (platform.dmx): Declares shared infrastructure, lists all services/modules, defines compilation order, cross-service interactions, Celery workers, event bus, and infrastructure config. ONE per application. - Service spec (service_name.dmx): Defines ONE microservice — its entities, APIs, flows, security, state machines. The service "name" must match the module key in the platform's modules {} block. The compiler compiles service specs one at a time, using the platform spec as infrastructure context.

Platform-Level Blocks

NameDescriptionSyntax
versionPlatform version stringversion = "1.0.0"
platform_roles { }Compile-time RBAC role definitions (optional)
modules { }REQUIRED: service/module declarations. Each module's dependencies = [...] drive the compilation order — no explicit ordering block
interactions { }Cross-service communication: async event(...) and/or sync http(method=, path=) (optional)
celery { }Task queue configuration with named workers (optional)
event_bus { }Event streaming configuration (optional)
shared_kernel { }Shared kernel module selection (ADR-317 INV-317-1.b closed taxonomy 2026-05-15). modules accepts the 22 canonical productive plugin names: audit_decorator, base_repository, compliance_policy, encryption_policy, errors, event_bus, event_envelope, jwt_utils, module_gate, observability, protocol_counter, response_envelope, rls_policies, s2s_client, secrets, soft_delete, soft_delete_policy, sqlalchemy_ext, tenant_context, test_fixtures, traceability, trinity_policy. Non-canonical values rejected at parse time
feature_flags { }Platform-wide feature toggles (optional)
output { }Output directory configuration (optional)
validation { }Compilation validation rules (optional)
infrastructure { }API gateway, JWT, Redis, databases, ports (optional)
compliance { }Spec-declared compliance profiles per platform jurisdictional scope. Canonical registry (ADR-317 INV-317-1.b Op α Phase 1.B 2026-05-15): CCPA / FEDRAMP / GDPR / HIPAA / HITRUST / ISO-27001 / NIST-800-53 / OWASP-API / OWASP-WEB / PCI-DSS / SOC2 / SOX + enterprise jurisdictional extensions ISO_15189 / AFIP_ARCA / FABA_AOL / LEY_25_326 / LEY_17_132. Non-canonical framework names rejected at validate-time (COMPLIANCE_PROFILE_UNKNOWN) via registry-loader lookup. Each profile carries retention, encryption, audit, residency, subject-rights and 17 framework-detail attrs (ADR-255 / ADR-257 / ADR-317 E.2)
platform_scope_entitiesEntities shared across all tenants (no tenant_id)platform_scope_entities = [Currency, Country]
append_only_entitiesImmutable audit/log entitiesappend_only_entities = [AuditEvent, SecurityLog]
abac_scope_policy { }ABAC row-filter scope catalog: declares scope columns once (claim → strategy). A per-entity field @scoped(claim="<key>") activates a catalog entry. Optional required_for_roles = [role, ...] makes a scope-dim role-conditional (#294-B).abac_scope_policy { branch_ids { strategy = scoped; required_for_roles = [receptionist] } }

Module Attributes

Every module declared in modules {} supports these fields.

NameDescriptionSyntax
specREQUIRED. Path to the service spec filespec = "filename.dmx"
nameHuman-readable display name for UI/reportsname = "Billing Service"
layerDependency layer: 0 = foundation, higher depends on lowerlayer = 0
entitiesEntity count (informational, for validation/UI)entities = 10
dependenciesCompile-time deps: these must compile before this moduledependencies = [mod_a, mod_b]
service_typestandard | consumer | reader | stateless | aggregatorservice_type = standard
database_engineDatabase backend: postgresql (default) | clickhousedatabase_engine = postgresql
clickhouse_tablesClickHouse table names (when database_engine = clickhouse)clickhouse_tables = [fact_orders, dim_tenants]
runtime_dependenciesRuntime services beyond default postgresruntime_dependencies = [redis, clickhouse]
websocket_enabledService needs WebSocket supportwebsocket_enabled = true
token_also_issuesAdditional JWT token types this service issuestoken_also_issues = [patient_token, provider_token]
module_categoryDDD Strategic Design classification (closed taxonomy). Industry-agnostic. See module_category Values section for the 6 canonical keywords.module_category = core_domain

module_category Values (DDD Strategic Design)

Closed taxonomy from Domain-Driven Design Strategic Design (Eric Evans, Domain-Driven Design, 2003; Vaughn Vernon, Implementing Domain-Driven Design, 2013). Industry-agnostic — applies to any platform regardless of business vertical. Validated parse-time by the grammar; non-canonical keywords surface a parse error inline.

NameDescription
core_domainThe strategic differentiator. Where the business invests the most because it provides competitive advantage. Custom-built, evolves with strategy.
supporting_subdomainNecessary for the business but not differentiating. Usually built in-house when no off-the-shelf option fits; can sometimes be outsourced.
generic_subdomainSolved problem with widely available solutions (auth, notifications, audit, document storage). Buy / adopt / standardise rather than build.
integration_layerBridges to external systems / third-party adapters / gateway concerns. Maps the bounded contexts of partners to the platform's ubiquitous language.
presentation_layerUser-facing surfaces (web UI, portals, dashboards, BFFs that exist only to serve the UX). Composes responses from other modules; usually no own entities.
infrastructure_layerPlatform plumbing: control planes, deploy automation, observability scaffolding, secrets / cert / identity management at the platform level (not business-domain auth).

service_type Values

NameDescription
standardDefault. Normal microservice with entities, APIs, business logic
consumerEvent consumer with no REST API (e.g., data lake ingestor)
readerRead-only service querying external data (e.g., analytics API over ClickHouse)
statelessNo database, real-time processing (e.g., WebSocket dashboard)
aggregatorPortal API that composes responses from multiple services (no own entities)

Compilation Order

Derived automatically from each module's dependencies. There is no explicit ordering keyword — the compiler builds a topological DAG and rejects any legacy phase/ordering block.

control_plane has no deps so compiles first; identity depends on control_plane; registry and catalog depend on identity and compile in parallel; lab waits for both connectivity and preanalytic.

Dependency-driven order
modules {
  control_plane { spec = "control_plane.dmx"; dependencies = [] }
  identity      { spec = "identity.dmx";      dependencies = [control_plane] }
  registry      { spec = "registry.dmx";      dependencies = [identity] }
  catalog       { spec = "catalog.dmx";       dependencies = [identity] }
  connectivity  { spec = "connectivity.dmx";  dependencies = [registry] }
  preanalytic   { spec = "preanalytic.dmx";   dependencies = [connectivity] }
  lab           { spec = "lab.dmx";           dependencies = [connectivity, preanalytic] }
}

Celery Workers

Named worker definitions — each gets its own process with dedicated queues.

Define multiple workers with dedicated queue assignments.

Named workers
celery {
  broker_url      = "redis://redis:6379/0"
  result_backend  = "redis://redis:6379/1"
  task_serializer = "json"
  concurrency     = 2

  worker worker_fast {
    queues = ["fast", "ocr", "notifications"]
  }
  worker worker_standard {
    queues = ["default", "pdf", "billing"]
  }
  worker worker_beat {
    role = scheduler    // Celery Beat scheduler
  }
}

Interactions

Cross-service event-driven communication with tiered guarantees.

NameDescription
tier1 = always generated, 2 = generated if both modules compiled, 3 = optional/audit
patternfire_and_forget (default) or request_reply (bidirectional)
producer = *Wildcard: any module can produce this event (broadcast)

Standard one-way event between two modules.

Fire-and-forget event
interactions {
  "order-created" {
    tier     = 1
    producer = billing
    consumer = fulfillment
    trigger  = event("billing.order.created")
    task     = "process_order"
    queue    = "fulfillment-queue"
    payload {
      order_id UUID @required
      status   String
    }
  }
}

Infrastructure

API gateway, JWT, Redis, databases, pagination, and service ports.

Platform-wide infrastructure configuration.

Full infrastructure block
infrastructure {
  api_gateway {
    enabled = true
    type    = kong
    rate_limiting { enabled = true; requests_per_minute = 100 }
  }
  service_ports {
    control_plane = 8010
    identity      = 8001
  }
  jwt {
    algorithm              = "HS256"
    token_lifetime_minutes = 30
    issuer                 = "platform-name"
  }
  redis { host = "redis"; port = 6379 }
  databases {
    control_plane = "postgresql"
    datalake      = "clickhouse"
  }
  pagination {
    default_limit = 100
    max_limit     = 1000
  }
}

Canonical list-response pagination defaults. Drives the wrapper class, route Query() ge/le constraints, service signature, and BaseRepository.list defaults end-to-end (α-PAGINATION program / ADR-329). When omitted, the compiler falls back to default_limit = 100 + max_limit = 1000.

Pagination block
infrastructure {
  pagination {
    default_limit = 50    // default page size when client omits ?limit
    max_limit     = 500   // hard cap enforced via Query(le=...)
  }
}

Compliance

Platform-scope compliance profiles (ADR-255 / ADR-257 / ADR-317 INV-317-1.b Op α Phase 1.B 2026-05-15). Each profile is a registered framework identifier the spec author binds to the platform per its jurisdictional scope; the compiler treats the name as opaque taxonomy and reads concrete obligations from the canonical compliance_frameworks registry + the attributes declared inside the block. The Workshop validator queries `registry_loader.is_supported_compliance_framework`; non-canonical names are rejected at validate-time as COMPLIANCE_PROFILE_UNKNOWN. The full canonical set as of 2026-05-15: CCPA / FEDRAMP / GDPR / HIPAA / HITRUST / ISO-27001 / NIST-800-53 / OWASP-API / OWASP-WEB / PCI-DSS / SOC2 / SOX + enterprise jurisdictional extensions ISO_15189 / AFIP_ARCA / FABA_AOL / LEY_25_326 / LEY_17_132 (5 entries Op α Phase 1.B added). ADR-317 E.2 extends the per-profile attribute surface so the spec is the source of truth for the full ComplianceFrameworkIR shape — when an attribute is omitted the compiler falls back to the canonical default registry shipped under `src/dsl/compliance_framework_default_registry.py`.

NameDescriptionSyntax
scopeFree-form scope description (e.g. data residency rule).scope = "protected_health_information"
data_classesData classes governed by the profile.data_classes = ["PHI", "PII"]
retention_daysHard retention floor (days).retention_days = 2190
encryption_at_restMandate encryption-at-rest.encryption_at_rest = true
encryption_in_transitMandate TLS-everywhere.encryption_in_transit = true
audit_requiredMandate audit-trail emission.audit_required = true
access_review_daysPeriodic access-review interval (days).access_review_days = 90
subject_rightsSubject rights honoured (access / rectification / erasure / portability / restriction / objection).subject_rights = ["access", "rectification", "erasure"]
breach_notification_hoursBreach notification SLA (hours).breach_notification_hours = 72
residencyData residency jurisdiction code.residency = "US"
validation_controlsRequired control families (e.g. technical_safeguards, administrative).validation_controls = ["technical_safeguards", "administrative"]
legal_basisCitable regulatory anchor.legal_basis = "HIPAA-45-CFR-164"
full_nameHuman-readable framework name.full_name = "Health Insurance Portability and Accountability Act"
mode_aliasCompatibility alias for legacy SaaS payload modes.mode_alias = "covered-entity"
phi_keywordsPHI keyword set used by data-classification gates.phi_keywords = ["diagnosis", "patient_id", "mrn"]
phi_segmentsPath-segment patterns hinting PHI (e.g. URL or table fragments).phi_segments = ["/medical_records/", "/clinical/"]
pii_keywordsPII keyword set used by data-classification gates.pii_keywords = ["ssn", "passport", "drivers_license"]
pii_segmentsPath-segment patterns hinting PII.pii_segments = ["/profile/", "/identity/"]
financial_keywordsFinancial keyword set used by data-classification gates.financial_keywords = ["card_number", "iban", "swift"]
financial_segmentsPath-segment patterns hinting financial data.financial_segments = ["/payment/", "/wallet/"]
min_retention_daysMinimum retention floor when distinct from retention_days.min_retention_days = 365
requires_read_auditMandate read-side audit logging.requires_read_audit = true
requires_pii_maskingMandate PII masking in non-production environments.requires_pii_masking = true
requires_extended_retentionMandate retention beyond the platform default.requires_extended_retention = true
requires_immutable_auditMandate append-only audit storage (no UPDATE/DELETE).requires_immutable_audit = true
requires_encrypted_auditMandate encryption of the audit-trail at rest.requires_encrypted_audit = true
requires_actor_infoMandate actor identity capture in audit records.requires_actor_info = true
requires_digital_signatureMandate digital signature on audit records.requires_digital_signature = true
requires_request_metadataMandate request-metadata capture (IP, UA, correlation_id).requires_request_metadata = true
test_frameworksRequired compliance test framework labels (informational).test_frameworks = ["hipaa-test-suite", "gdpr-rights"]

Spec-driven profile declaration; compiler reads every attribute below and stops consulting the default registry when the value is present.

Compliance profile (full feature-set)
compliance {
  profile "covered-entity" {
    scope                     = "protected_health_information"
    data_classes              = ["PHI", "PII"]
    retention_days            = 2190
    encryption_at_rest        = true
    encryption_in_transit     = true
    audit_required            = true
    access_review_days        = 90
    subject_rights            = ["access", "rectification", "erasure"]
    breach_notification_hours = 72
    residency                 = "US"
    validation_controls       = ["technical_safeguards", "administrative"]
    legal_basis               = "HIPAA-45-CFR-164"
    full_name                 = "Health Insurance Portability and Accountability Act"
    mode_alias                = "covered-entity"
    phi_keywords              = ["diagnosis", "patient_id", "mrn"]
    pii_keywords              = ["ssn", "passport"]
    min_retention_days        = 2190
    requires_read_audit       = true
    requires_pii_masking      = true
    requires_extended_retention = true
    requires_immutable_audit  = true
    requires_encrypted_audit  = true
    requires_actor_info       = true
    requires_digital_signature = false
    requires_request_metadata = true
    test_frameworks           = ["hipaa-test-suite"]
  }
}

Complete Examples

Full spec examples from simple to complex.

A minimal spec: one entity, REST API, basic security.

Day 1 — Simple User Service
service UserService {
  version = "1.0.0"
  port = 8000
  api_prefix = "/api/v1"
  database = "postgresql"

  entity User @audit @soft_delete {
    id       UUID     @pk @default($computed.uuid)
    email    String   @unique @not_null
    name     String   @not_null
    role     Enum(ADMIN, MEMBER) @default(MEMBER)
    is_active Boolean @default(true)
  }

  api REST @entity(User) @paginated {
    POST   "/users"       @status(201)
    GET    "/users"
    GET    "/users/{id}"
    PUT    "/users/{id}"
    DELETE "/users/{id}"  @roles(ADMIN)
  }

  security {
    auth_scheme = "jwt"
    jwt_algorithm = "HS256"
    token_expiry_minutes = 60
    password_hashing = "bcrypt"
    roles = [ADMIN, MEMBER]
  }
}

A more complex spec with flows, state machines, jobs, and notifications.

Day 5 — E-commerce with Flows and State Machines
service OrderService {
  version = "2.0.0"
  port = 8001
  api_prefix = "/api/v1"
  database = "postgresql"

  entity Order @audit @soft_delete @aggregate_root {
    id          UUID     @pk @default($computed.uuid)
    customer_id UUID     @fk(Customer.id) @not_null @indexed
    total       Decimal  @not_null @check("total >= 0")
    currency    String   @default("USD")
    status      Enum(DRAFT, PENDING, PAID, SHIPPED, DELIVERED, CANCELLED) @default(DRAFT)
    notes       Text     @nullable
  }

  entity OrderItem @audit {
    id         UUID    @pk @default($computed.uuid)
    order_id   UUID    @fk(Order.id) @not_null
    product_id UUID    @not_null
    quantity   Integer @not_null @check("quantity > 0")
    unit_price Decimal @not_null
  }

  state_machine OrderStatus {
    initial = DRAFT

    DRAFT -> PENDING       @roles(MEMBER, ADMIN)
    PENDING -> PAID        @guard("payment_confirmed == true")
    PAID -> SHIPPED        @roles(ADMIN)
    SHIPPED -> DELIVERED
    PENDING -> CANCELLED   @roles(MEMBER, ADMIN)
    DRAFT -> CANCELLED
  }

  flow PlaceOrder {
    step validate {
      action = validate
      target = Order
    }
    step create_order {
      action = create
      target = Order
      mapping {
        id = $computed.uuid
        customer_id = $context.user_id
        status = DRAFT
        total = $input.total
        created_at = $computed.now
      }
    }
    step submit {
      action = state_change
      target = Order
      condition = "$result.id != null"
      mapping {
        status = PENDING
      }
    }
  }

  api REST @entity(Order) @paginated {
    POST   "/orders"          @status(201) @triggers(order_created)
    GET    "/orders"
    GET    "/orders/{id}"
    POST   "/orders/{id}/pay" @triggers(order_paid)
  }

  jobs {
    cancel_stale_orders {
      schedule = "0 * * * *"
      timeout = "2m"
      retry { max_attempts = 3, backoff = "exponential" }
    }
  }

  notifications {
    providers {
      email { adapter = "smtp" }
    }
    order_confirmed {
      channels = [email]
      recipient = $instance.customer.email
      template = "order_confirmed"
    }
  }

  errors {
    ORDER_NOT_FOUND   { status = 404, category = "not_found", message = "Order not found" }
    INSUFFICIENT_STOCK { status = 409, category = "conflict", message = "Insufficient stock" }
  }

  security {
    auth_scheme = "jwt"
    jwt_algorithm = "HS256"
    token_expiry_minutes = 60
    roles = [ADMIN, MEMBER]
    rate_limit { policy = "sliding_window", allow = 100, for = "1m" }
  }

  infrastructure {
    docker { compose_version = "3.8", base_image = "python:3.12-slim" }
    redis { broker = true, cache = true }
    celery { concurrency = 4 }
  }
}
DevMatrix DMX Language Reference. Built for developers who compile, not configure.