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 | Server timestamp (UTC) — 0 args | @default($computed.now) |
$computed.now_plus_minutes | Timestamp + N minutes — 1 arg | $computed.now_plus_minutes(15) |
$computed.now_minus_minutes | Timestamp − N minutes — 1 arg | $computed.now_minus_minutes(5) |
$computed.now_plus_hours | Timestamp + N hours — 1 arg | $computed.now_plus_hours(24) |
$computed.now_minus_hours | Timestamp − N hours — 1 arg | $computed.now_minus_hours(72) |
$computed.now_plus_days | Timestamp + N days — 1 arg | $computed.now_plus_days(7) |
$computed.now_minus_days | Timestamp − N days — 1 arg | $computed.now_minus_days(30) |
$computed.token_expiry | Default 24-hour expiry — 0 args | expires_at = $computed.token_expiry |
$computed.uuid | Generate UUID v4 — 0 args | @default($computed.uuid) |
$computed.random_token | 32-byte URL-safe random token — 0 args | reset_token = $computed.random_token |
$computed.recovery_codes | List of N 8-byte recovery codes — 1 arg | $computed.recovery_codes(10) |
$computed.token_hash | SHA-256 hex digest of token — 1 arg | refresh_hash = $computed.token_hash($result.refresh_plaintext) |
$computed.hash | Alias of token_hash; SHA-256 hex digest — 1 arg | $computed.hash($input.payload) |
$computed.verify_hash | Returns bool — sha256(value) == hash — 2 args | $computed.verify_hash($instance.token_hash, $input.token) |
$computed.hash_password | BCrypt hash via pwd_context — 1 arg | password_hash = $computed.hash_password($input.password) |
$computed.bcrypt | Alias of hash_password; BCrypt hash — 1 arg | password_hash = $computed.bcrypt($input.password) |
$computed.bcrypt_verify | Returns bool — pwd_context.verify — 2 args (crypto-safe for passwords) | $computed.bcrypt_verify($input.password, $instance.password_hash) |
$computed.totp_secret | Generate base-32 TOTP secret — 0 args | mfa_secret = $computed.totp_secret |
$computed.verify_totp | Returns bool — verify TOTP code — 2 args | $computed.verify_totp($instance.mfa_secret, $input.code) |
$computed.decode_jwt | Decode and validate JWT — 1 arg | claims = $computed.decode_jwt($input.token) |
$computed.jwt_sign | Issue 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_id | Tenant-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.nc_sequence | Tenant-scoped monotonic NC number — 1 arg (tenant_id). Each tenant's counter increments independently via _sequence_state UPSERT. | nc_number = $computed.nc_sequence($context.tenant_id) |
$computed.protocol_sequence | Tenant-scoped monotonic admission/protocol number — 1 arg (tenant_id). Each tenant's counter increments independently via _sequence_state UPSERT. | 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
service UserService {
version = "1.0.0"
port = 8000
api_prefix = "/api/v1"
database = "postgresql"
// ... entities, api, flows, etc.
}identity
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).
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.
service AuthService {
version = "1.0.0"
port = 8000
api_prefix = "/api"
identity {
service_version = "2.4.1"
}
}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"]
}
}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.
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.
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.
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
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"
}
}
}events
tenant_config
workers (platform-scope, ADR-260)
platform { workers { ... } }. Superseded the per-service task_queue block (ADR-211).row_level_isolation
sequences
service_observability
object_storage
permissions
sagas
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.
| 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")) |
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.
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
| Name | Description | Syntax |
|---|---|---|
version | Platform version string | version = "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_entities | Entities shared across all tenants (no tenant_id) | platform_scope_entities = [Currency, Country] |
append_only_entities | Immutable audit/log entities | append_only_entities = [AuditEvent, SecurityLog] |
Module Attributes
Every module declared in modules {} supports these fields.
| Name | Description | Syntax |
|---|---|---|
spec | REQUIRED. Path to the service spec file | spec = "filename.dmx" |
name | Human-readable display name for UI/reports | name = "Billing Service" |
layer | Dependency layer: 0 = foundation, higher depends on lower | layer = 0 |
entities | Entity count (informational, for validation/UI) | entities = 10 |
dependencies | Compile-time deps: these must compile before this module | dependencies = [mod_a, mod_b] |
service_type | standard | consumer | reader | stateless | aggregator | service_type = standard |
database_engine | Database backend: postgresql (default) | clickhouse | database_engine = postgresql |
clickhouse_tables | ClickHouse table names (when database_engine = clickhouse) | clickhouse_tables = [fact_orders, dim_tenants] |
runtime_dependencies | Runtime services beyond default postgres | runtime_dependencies = [redis, clickhouse] |
websocket_enabled | Service needs WebSocket support | websocket_enabled = true |
token_also_issues | Additional JWT token types this service issues | token_also_issues = [patient_token, provider_token] |
module_category | DDD 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.
| Name | Description |
|---|---|
core_domain | The strategic differentiator. Where the business invests the most because it provides competitive advantage. Custom-built, evolves with strategy. |
supporting_subdomain | Necessary for the business but not differentiating. Usually built in-house when no off-the-shelf option fits; can sometimes be outsourced. |
generic_subdomain | Solved problem with widely available solutions (auth, notifications, audit, document storage). Buy / adopt / standardise rather than build. |
integration_layer | Bridges to external systems / third-party adapters / gateway concerns. Maps the bounded contexts of partners to the platform's ubiquitous language. |
presentation_layer | User-facing surfaces (web UI, portals, dashboards, BFFs that exist only to serve the UX). Composes responses from other modules; usually no own entities. |
infrastructure_layer | Platform plumbing: control planes, deploy automation, observability scaffolding, secrets / cert / identity management at the platform level (not business-domain auth). |
service_type Values
| Name | Description |
|---|---|
standard | Default. Normal microservice with entities, APIs, business logic |
consumer | Event consumer with no REST API (e.g., data lake ingestor) |
reader | Read-only service querying external data (e.g., analytics API over ClickHouse) |
stateless | No database, real-time processing (e.g., WebSocket dashboard) |
aggregator | Portal 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.
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.
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.
| Name | Description |
|---|---|
tier | 1 = always generated, 2 = generated if both modules compiled, 3 = optional/audit |
pattern | fire_and_forget (default) or request_reply (bidirectional) |
producer = * | Wildcard: any module can produce this event (broadcast) |
Standard one-way event between two modules.
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.
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.
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`.
| Name | Description | Syntax |
|---|---|---|
scope | Free-form scope description (e.g. data residency rule). | scope = "protected_health_information" |
data_classes | Data classes governed by the profile. | data_classes = ["PHI", "PII"] |
retention_days | Hard retention floor (days). | retention_days = 2190 |
encryption_at_rest | Mandate encryption-at-rest. | encryption_at_rest = true |
encryption_in_transit | Mandate TLS-everywhere. | encryption_in_transit = true |
audit_required | Mandate audit-trail emission. | audit_required = true |
access_review_days | Periodic access-review interval (days). | access_review_days = 90 |
subject_rights | Subject rights honoured (access / rectification / erasure / portability / restriction / objection). | subject_rights = ["access", "rectification", "erasure"] |
breach_notification_hours | Breach notification SLA (hours). | breach_notification_hours = 72 |
residency | Data residency jurisdiction code. | residency = "US" |
validation_controls | Required control families (e.g. technical_safeguards, administrative). | validation_controls = ["technical_safeguards", "administrative"] |
legal_basis | Citable regulatory anchor. | legal_basis = "HIPAA-45-CFR-164" |
full_name | Human-readable framework name. | full_name = "Health Insurance Portability and Accountability Act" |
mode_alias | Compatibility alias for legacy SaaS payload modes. | mode_alias = "covered-entity" |
phi_keywords | PHI keyword set used by data-classification gates. | phi_keywords = ["diagnosis", "patient_id", "mrn"] |
phi_segments | Path-segment patterns hinting PHI (e.g. URL or table fragments). | phi_segments = ["/medical_records/", "/clinical/"] |
pii_keywords | PII keyword set used by data-classification gates. | pii_keywords = ["ssn", "passport", "drivers_license"] |
pii_segments | Path-segment patterns hinting PII. | pii_segments = ["/profile/", "/identity/"] |
financial_keywords | Financial keyword set used by data-classification gates. | financial_keywords = ["card_number", "iban", "swift"] |
financial_segments | Path-segment patterns hinting financial data. | financial_segments = ["/payment/", "/wallet/"] |
min_retention_days | Minimum retention floor when distinct from retention_days. | min_retention_days = 365 |
requires_read_audit | Mandate read-side audit logging. | requires_read_audit = true |
requires_pii_masking | Mandate PII masking in non-production environments. | requires_pii_masking = true |
requires_extended_retention | Mandate retention beyond the platform default. | requires_extended_retention = true |
requires_immutable_audit | Mandate append-only audit storage (no UPDATE/DELETE). | requires_immutable_audit = true |
requires_encrypted_audit | Mandate encryption of the audit-trail at rest. | requires_encrypted_audit = true |
requires_actor_info | Mandate actor identity capture in audit records. | requires_actor_info = true |
requires_digital_signature | Mandate digital signature on audit records. | requires_digital_signature = true |
requires_request_metadata | Mandate request-metadata capture (IP, UA, correlation_id). | requires_request_metadata = true |
test_frameworks | Required 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 "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.
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/* ... */.