Mutations
A mutation is the rule that connects an event to a projection. It says: "when this event happens, change that field of the projection in this way". Mutations are declared through the dashboard — you don't need to write boilerplate event handlers.
Anatomy of a mutation
- Trigger event — the event name (and optional version) that fires this mutation.
- Target field — the projection field that should change.
- Action — what to do (set, increment, append, etc.).
- Source — where the new value comes from (event payload, a literal, an expression).
- Filter (optional) — a condition the event must satisfy.
Built-in actions
Set— replace the field with a value.Increment/Decrement— adjust numeric fields.Append— add an item to a list field.Remove— remove a matching item from a list.Merge— deep-merge an object into a JSON field.Compute— apply a small expression (e.g.totalAmount + event.amount).
Example: a basket summary
Projection BasketSummary with fields itemCount: Integer and total: Integer:
- On
ItemAddedToBasket→IncrementitemCountby1. - On
ItemAddedToBasket→Computetotal = total + event.amount. - On
ItemRemovedFromBasket→DecrementitemCountby1. - On
BasketCleared→SetitemCount = 0andtotal = 0.
Custom mutation actions
You aren't limited to the built-ins. Through the extension API you can register a custom mutation action that runs inside the EventSourcerer server. This lets you encode domain-specific logic (e.g. tax calculation, currency conversion) once and reuse it across projections.
final class ApplyDiscount implements MutationAction
{
public function name(): string { return 'apply_discount'; }
public function apply(mixed $current, array $event, array $args): mixed
{
$rate = (float) ($args['rate'] ?? 0);
return (int) round($current * (1 - $rate));
}
}
Mutations execute in event order and are deterministic — the same events always produce the same projection state, which makes rebuilds safe and predictable.