Command
Syntax
command <Name> { <fieldName>: <TypeName> ...
validate <expression> else "<error message>" ...}Commands carry intent. Each command declares typed fields and optional validation constraints.
Fields
Fields use name: TypeName syntax. The type references a type declaration within the same context.
Validation
validate constraints define value-level rules checked at command construction time. The grammar accepts validate syntax today — the parser builds AST nodes for each constraint.
The code generator produces Smart Constructors from validate constraints: a private constructor paired with a create() factory returning Result<Command, ValidationError[]>. Invalid commands cannot be instantiated directly.
Multiple validate clauses are allowed. All are checked, and all failures are collected (unlike require guards, which short-circuit).
A command with no validate clauses has a public constructor.
Field Bindings
When emitting events from a decide clause, field bindings pass command data to event fields:
decide(Register, Unregistered) -> [Registered { userId: userId, email: email }]Shorthand (field name matches command field):
decide(Register, Unregistered) -> [Registered { userId, email, name }]When the binding name matches a field on the event, the value is inferred from the command’s same-named field.
Example
context Ordering { type CartId = String type ProductId = String type Quantity = Integer
command AddItem { cartId: CartId productId: ProductId quantity: Quantity
validate quantity > 0 else "Quantity must be positive" validate quantity <= 99 else "Maximum 99 items per product" }
event ItemAdded { cartId: CartId productId: ProductId quantity: Quantity }
decider Cart { commands: AddItem events: ItemAdded state: Empty | HasItems
initial: Empty terminal: HasItems
decide(AddItem, Empty) -> [ItemAdded]
evolve(Empty, ItemAdded) -> HasItems }}