DMX Specification Language
The complete reference for writing DevMatrix specifications. From data types and annotations to flows, state machines, and infrastructure.
Overview
.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
{}.Strings
\", \\, \n.Numbers
42, 0. Float literals: 3.14. No hex/octal support.Identifiers
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
@ followed by an identifier. Some accept parenthesized arguments: @default("active"), @fk(User.id).Dollar Tokens
$. 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.
| Name | Description |
|---|---|
UUID | Universally unique identifier (v4) |
String | Variable-length text, up to 255 characters |
Text | Unlimited-length text (CLOB/TEXT) |
Integer | Whole number (32-bit signed) |
Float | IEEE 754 floating-point |
Decimal | Arbitrary-precision decimal |
Boolean | true or false |
Date | Calendar date without time (YYYY-MM-DD) |
DateTime | Date and time with timezone (ISO 8601) |
Time | Time of day without date |
JSON | Arbitrary JSON object or array |
Bytes | Binary data (BLOB) |
Array | Ordered collection of a single type |
Enum | Enumerated set of named values |
Char | Fixed-length character string |
Point | Geographic point (latitude, longitude) |
Polygon | Geographic polygon boundary |
Geometry | Arbitrary geometric shape |
Every entity field has a name, a type, and optional annotations.
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
| Name | Description | Syntax |
|---|---|---|
@pk | Primary key | @pk |
@not_null | NOT NULL constraint | @not_null |
@unique | Unique constraint | @unique |
@nullable | Allows NULL values | @nullable |
@default | Default value | @default("value") or @default($computed.now) |
@fk | Foreign key reference | @fk(Entity.field) |
@check | Check constraint expression | @check("age >= 18") |
@required | Required in API input | @required |
@sensitive | PII / secrets — excluded from logs | @sensitive |
@computed | Server-computed, not in API input | @computed |
@json_list | Store as JSON array column | @json_list |
@immutable | Cannot be updated after creation | @immutable |
@indexed | Create a database index | @indexed |
Entity Annotations
| Name | Description | Syntax |
|---|---|---|
@aggregate_root | DDD aggregate root — owns child entities | @aggregate_root |
@tenancy | Tenant isolation strategy | @tenancy("row") or @tenancy("schema") |
@description | Human-readable description | @description("User accounts") |
@audit | Auto-generate created_at / updated_at / deleted_at | @audit |
@soft_delete | Soft delete (set deleted_at) instead of hard delete | @soft_delete |
@platform_scope | Not tenant-scoped (platform-level entity) | @platform_scope |
@append_only | Insert-only table (no UPDATE or DELETE) | @append_only |
@append_only_after | Append-only after a condition is met | @append_only_after("status = CLOSED") |
@unique_together | Composite unique constraint | @unique_together(field_a, field_b) |
@check | Entity-level check constraint | @check("start_date < end_date") |
@relation | Define relationship to another entity | @relation("has_many", Target) |
API Annotations
| Name | Description | Syntax |
|---|---|---|
@entity | Bind API to an entity | @entity(User) |
@paginated | Enable pagination | @paginated |
@status | Override HTTP status code | @status(201) |
@triggers | Emit events on action | @triggers(user_created) |
State Machine Annotations
| Name | Description | Syntax |
|---|---|---|
@roles | Restrict transition to roles | @roles(ADMIN, MANAGER) |
@guard | Guard condition for transition | @guard("amount > 0") |
ClickHouse Annotations
| Name | Description | Syntax |
|---|---|---|
@low_cardinality | ClickHouse LowCardinality optimization | @low_cardinality |
@pii | Mark 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.
| Name | Description | Syntax |
|---|---|---|
$input | Current request/step input data | $input.email, $input.password |
$context | Execution context (authenticated user, tenant, request metadata) | $context.user_id, $context.tenant_id |
$instance | Current entity instance (in flows and state machines) | $instance.status, $instance.id |
$result | Result from the previous flow step | $result.id, $result.token |
$computed.now | Current UTC timestamp | @default($computed.now) |
$computed.uuid | Generate a new UUID v4 | @default($computed.uuid) |
$computed.hash_password | Hash a password with bcrypt | password = $computed.hash_password($input.password) |
$computed.verify_hash | Verify a bcrypt hash | $computed.verify_hash($input.password, $instance.password) |
$computed.hash | Generic SHA-256 hash | $computed.hash($input.data) |
$computed.sign_jwt | Sign a JWT token | $computed.sign_jwt($instance.id, $instance.role) |
$computed.decode_jwt | Decode and verify a JWT token | $computed.decode_jwt($input.token) |
$computed.random_token | Generate a random token string | $computed.random_token(32) |
$computed.now_plus_hours | Current time plus N hours | $computed.now_plus_hours(24) |
$computed.now_plus_days | Current time plus N days | $computed.now_plus_days(30) |
Top-Level Blocks
DMX specs are composed of top-level blocks. Each block defines a different concern of the service.
service
service UserService {
version = "1.0.0"
port = 8000
api_prefix = "/api/v1"
database = "postgresql"
// ... entities, api, flows, etc.
}entity
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
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
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
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
validations {
email {
format = "email"
uniqueness = true
}
age {
range = "0..150"
presence = true
}
username {
format = "^[a-zA-Z0-9_]{3,30}$"
uniqueness = true
}
}security
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]
}errors
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
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
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_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
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"]
}
}platform
platform MyPlatform {
modules = [UserService, OrderService, NotificationService]
shared_kernel = [BaseEntity, AuditMixin]
generation_order = [UserService, OrderService, NotificationService]
interactions {
OrderService -> NotificationService {
pattern = "fire_and_forget"
event = "order_confirmed"
}
}
}events
tenant_config
task_queue
row_level_isolation
sequences
external_adapters
service_observability
object_storage
permissions
sagas
Flow Actions
Actions available in flow steps. Each action maps to a specific operation on the target entity or system.
| Name | Description | Syntax |
|---|---|---|
create | Create a new entity instance | action = create |
update | Update an existing entity | action = update |
delete | Delete an entity instance | action = delete |
query | Query entities with filters | action = query |
validate | Run validation rules | action = validate |
extract | Extract fields from data | action = extract |
calculate | Compute derived values | action = calculate |
state_change | Trigger a state machine transition | action = state_change |
external_call | Call an external service | action = external_call |
batch | Batch multiple operations | action = batch |
export | Export data to a format | action = export |
Trigger Functions
Functions used in @triggers annotations and event routing.
| Name | Description | Syntax |
|---|---|---|
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")) |
Complete Examples
Full spec examples from simple to complex.
A minimal spec: one entity, REST API, basic security.
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.
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 }
}
}
Comments
//. Block comments use/* ... */.