Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Appendix A: Quick Reference

Derive macros

MacroApplies toPurpose
#[derive(Event)]StructMakes a struct a persisted event
#[derive(EventSet)]EnumCreates a typed event set for queries
#[derive(DomainIds)]StructGenerates domain_ids() method
#[derive(FromDomainIds)]StructGenerates constructor from domain ID bindings

Attribute macros

AttributePlacementPurpose
#[event_type("...")]Event structSets the event type string
#[domain_id]FieldMarks a field as a domain ID tag
#[domain_id("alt_name")]FieldDomain ID with alternate tag name
#[crypto_scope]Field on EventEncrypts the event (must be on a #[domain_id] field)
#[scope(field)]EventSet variantFilter by a single domain ID field
#[scope(field = "value")]EventSet variantHardcoded tag filter
#[from_domain_id(default)]Fold fieldUse default value, don’t bind from domain IDs
#[validate(...)]Input fieldValidation rules (validator crate)

Export macros

MacroUsage
#[export_command]Annotate the command function
export_projector!(Name);Wire up projector WASM interface
export_effect!(Name);Wire up effect WASM interface

Command builder API

#![allow(unused)]
fn main() {
Command::new(input, context)           // Create builder
    .fold::<T>()                        // Register fold (no args)
    .fold_args::<T>(args)               // Register fold with args
    .fold_with(|input| MyFold { .. })   // Register fold manually
    .execute(|input, states| { .. })    // Run with fold states
}

Emit and reject

#![allow(unused)]
fn main() {
emit![]                                // No events
emit![Event { field: val }]            // Single event
emit![EventA { .. }, EventB { .. }]    // Multiple events

// Business rejections — use anyhow::ensure! / bail!:
anyhow::ensure!(balance >= amount, "insufficient funds");
anyhow::bail!("shop not connected");
}

SQLite API

#![allow(unused)]
fn main() {
// Connection-level
execute(sql, params)       -> Result<usize, SqliteError>
execute_batch(sql)         -> Result<(), SqliteError>
query_one(sql, params)     -> Row              // traps on 0 or >1 rows
query_row(sql, params)     -> Option<Row>
last_insert_rowid()        -> Option<i64>

// Prepared statements (built with prepare(sql) -> Statement)
stmt.execute(params)       -> Result<usize, SqliteError>
stmt.query(params)         -> Vec<Row>
stmt.query_one(params)     -> Row              // traps on 0 or >1 rows
stmt.query_row(params)     -> Option<Row>

// Parameters
params![]                        // no params
params![val1, val2, val3]        // positional

// Reading
row.get::<&str, String>("column_name")
row.get::<usize, i64>(0)
row.tuple::<(String, String, i64)>()
}

Built-in fold types

TypeStateUse for
EventFold<E>EventState<E> (vec of all events)Full history
LatestEvent<E>Option<StoredEvent<E>>Most recent event
EventCounter<E>u64Counting events
EventToggle<A, B>ToggleState<A, B>Paired opposing events
SingleEvent<E>N/A (it’s an EventSet)Single event type queries

Event envelope fields

FieldTypeDescription
idUuidEvent unique ID
positionu64Global log position
event_typeStringEvent type identifier
tagsVec<String>Domain ID tags
timestampDateTime<Utc>When written
correlation_idUuidOriginating action
causation_idUuidCommand execution
triggering_event_idOption<Uuid>Causal event
idempotency_keyOption<Uuid>Deduplication
encryption_scopeOption<String>Encryption scope
encryption_key_idOption<Uuid>Key identifier

CommandContext

#![allow(unused)]
fn main() {
CommandContext::new()                           // Auto-detect (effect or external)
    .with_correlation_id(id)                    // Set correlation ID
    .with_triggering_event_id(id)               // Set triggering event
    .with_idempotency_key(key)                  // Set idempotency key
}

Environment variables

Server (umari binary)

VariableDefaultDescription
UMARI_DATA_DIR./umari-dataruntime database directory
UMARI_EVENT_STORE_URLhttp://localhost:50051UmaDB event store URL
UMARI_API_ADDR127.0.0.1:3000HTTP API bind address
UMARI_API_KEY(none)required Authorization: Bearer <key>
UMARI_LOGumari=infotracing-subscriber filter
UMARI_VERBOSEfalseset log level to trace
UMARI_NO_BANNERfalsehide the startup banner
UMARI_SHUTDOWN_TIMEOUT10sgraceful shutdown deadline

CLI (umari-cli / umari client)

VariableDefaultDescription
UMARI_URLhttp://localhost:3000server URL
UMARI_API_KEY(none)bearer token sent with each request

Essential imports

#![allow(unused)]
fn main() {
use umari::prelude::*;           // Everything you need
use serde::{Serialize, Deserialize};
use validator::Validate;
use schemars::JsonSchema;        // Optional, for OpenAPI docs
}

Naming conventions

ItemConvention
Event structPascalCase past tense
Event type string"object.verb"
Command cratekebab-case imperative
Command input structInput
Projector cratekebab-case plural noun
Effect cratekebab-case verb phrase
Fold structPascalCase + Fold
Fold statePascalCase + State
EventSet enumQuery