Event Logs and Command Dispatch

Factos stores events, and it also provides helpers for command dispatch.

Those two ideas are related but not identical:

The backend stores the accepted events. The Decider and dispatch helpers are a standard way to process commands on top of that event log.

What the backend persists

A backend such as factos_pog persists event records:

It does not persist commands. It does not persist materialized views. It does not execute side effects.

What dispatch does

A dispatch function combines an event log with a command handler:

command + previous events -> new events or domain error

For factos_pog.dispatch_with_query, the previous events are selected by a factos.Query:

factos.query([
  factos.query_item(
    types: [factos.event_type("TicketSold")],
    tags: [factos.tag("event:gleamconf-2026")],
  ),
])

The backend reads those events, folds them into state, calls the decider, and appends the resulting events only if the query context is still stable.

What makes the append safe

A context read observes a global event-log position. The backend then protects the append with:

factos.FailIfEventsMatch(query, after: position)

That means:

do not append these new events if another event matching the same query was accepted after the position used for the decision.

This is how Factos lets the consistency boundary follow the rule. The boundary can be a tag, a set of event types, one stream, many streams, or all events.

What views are

A View is not a durable projection table. It is a pure fold:

factos.view(initial: 0, evolve: fn(count, event) {
  case event {
    TicketSold(_) -> count + 1
  }
})

You can run the fold over any list of events. To make a materialized view durable, your application stores the folded result.

Views can be recomputed as long as the stored event history can still be decoded. That makes event versioning and codec compatibility an application responsibility.

What reactors are

A Reactor is also pure. It maps committed recorded events to effect values:

factos.react_all(ticket_reactor(), dispatch.events)

Factos does not execute those effects. That is deliberate: replaying old events should not accidentally resend emails, charge cards, or publish webhooks.

Where Factos is opinionated

Factos is low-level about storage and high-level enough to standardize command dispatch.

It is opinionated that:

It is not opinionated about:

Search Document