Skip to content

Cratis Chronicle reducer batch methods

A batch method in Cratis Chronicle’s .NET client updates a read model from one partition’s delivered events in a single call. Use it when the transition needs to consider several events together rather than handle each event separately.

Chronicle delivers events in pages. A partition groups events for one event source, such as an account, and the client reduces each partition separately. The batch contains the observed event types from that partition in the current page, not its entire history. Your method folds those events in sequence order with the current read model and returns the next state.

using Cratis.Chronicle.Events;
using Cratis.Chronicle.Observation;
[EventType]
public record BatchMoneyDeposited(decimal Amount);
[EventType]
public record BatchMoneyWithdrawn(decimal Amount);
public record AccountBalance(decimal Balance)
{
[Observes<BatchMoneyDeposited>]
[Observes<BatchMoneyWithdrawn>]
public static AccountBalance? Reduce(EventBatch events, AccountBalance? current, BatchContext context) =>
events.Aggregate(current ?? new AccountBalance(0), (account, e) => e.Event switch
{
BatchMoneyDeposited deposited => account with { Balance = account.Balance + deposited.Amount },
BatchMoneyWithdrawn withdrawn => account with { Balance = account.Balance - withdrawn.Amount },
_ => account
});
}

View C# snippet source on GitHub

The account example applies deposits and withdrawals to the balance so far. Each item exposes the event through Event, allowing the method to choose the transition by event type.

A batch method takes an EventBatch first and the nullable current read model second, and returns the read model or a task of it. The current model is null before the partition has state; returning null removes the read model. Declare each observed event type with [Observes<TEvent>]. Optional parameters can receive BatchContext, CancellationToken, and services.

On a self-reducing read model the batch method is a public static method named Reduce. On a separate reducer implementing IReducerFor<TReadModel> it can have any name.

A reducer has per-event methods or one batch method, never both, because splitting its events between methods would break the fold. Chronicle refuses to register a reducer that declares both, more than one batch method, or a batch method that does not take and return the read model.

Throwing or calling Fail on the batch fails the partition without writing its new state. Calling Defer requests delivery again after the delay you supply, without failing the partition or writing state. Deferral requires kernel support; without it, the client reports a failure instead.

Other partitions in the page can still succeed. The client reduces partitions in parallel, while keeping each partition’s events in order. Keep state changes local to the partition rather than relying on another partition finishing first.

For the per-event alternative and replay behavior, continue with reducer event processing.