Event Sequence
By default, reducers observe the default event log. You can override this by specifying a different event sequence using either the [EventSequence] attribute or the eventSequence parameter on the [Reducer] attribute.
Using the [EventSequence] Attribute
Section titled “Using the [EventSequence] Attribute”Apply [EventSequence] directly to your reducer class to pin it to a specific event sequence:
using Cratis.Chronicle.Events;using Cratis.Chronicle.EventSequences;using Cratis.Chronicle.Reducers;
[EventType]public record ReducersEventSequenceShipmentDispatched(string TrackingNumber, string Carrier);
public record ReducersEventSequenceShipmentSummary(string TrackingNumber, string Carrier, DateTimeOffset DispatchedAt);
[EventSequence("fulfillment-events")]public class ReducersEventSequenceShipmentSummaryReducer : IReducerFor<ReducersEventSequenceShipmentSummary>{ public ReducersEventSequenceShipmentSummary Dispatched(ReducersEventSequenceShipmentDispatched @event, ReducersEventSequenceShipmentSummary? current, EventContext context) => new(@event.TrackingNumber, @event.Carrier, context.Occurred);}import io.cratis.chronicle.events.EventTypeimport io.cratis.chronicle.observation.EventSequenceimport io.cratis.chronicle.observation.Reducerimport io.cratis.chronicle.readModels.ReadModel
@EventType(id = "reducers-event-sequence-refund-issued")data class ReducersEventSequenceRefundIssued(val amount: Double)
@ReadModeldata class ReducersEventSequenceRefundSummary(val refunded: Double = 0.0)
@Reducer@EventSequence("outbox")class ReducersEventSequenceRefundSummaryReducer { fun issued( event: ReducersEventSequenceRefundIssued, current: ReducersEventSequenceRefundSummary? ): ReducersEventSequenceRefundSummary = ReducersEventSequenceRefundSummary((current?.refunded ?: 0.0) + event.amount)}import io.cratis.chronicle.events.EventType;import io.cratis.chronicle.observation.EventSequence;import io.cratis.chronicle.observation.Reducer;import io.cratis.chronicle.readModels.ReadModel;
@EventType(id = "reducers-event-sequence-refund-issued")record ReducersEventSequenceRefundIssued(double amount) {}
@ReadModelrecord ReducersEventSequenceRefundSummary(double refunded) { ReducersEventSequenceRefundSummary() { this(0.0); }}
@Reducer@EventSequence("outbox")class ReducersEventSequenceRefundSummaryReducer { ReducersEventSequenceRefundSummary issued( ReducersEventSequenceRefundIssued event, ReducersEventSequenceRefundSummary current) { double refunded = current == null ? 0.0 : current.refunded(); return new ReducersEventSequenceRefundSummary(refunded + event.amount()); }}TypeScript reaches the same result through the reducer() decorator’s event-sequence parameter, and Kotlin and Java through the eventSequence parameter on @Reducer — all shown under Using the [Reducer] Attribute below, rather than as a separate attribute. Elixir doesn’t currently support targeting a non-default event sequence for a reducer at all.
Using the [Reducer] Attribute
Section titled “Using the [Reducer] Attribute”When you are already using the [Reducer] attribute to set a custom identifier or other options, use its eventSequence parameter instead of adding a separate [EventSequence] attribute:
using Cratis.Chronicle.Events;using Cratis.Chronicle.Reducers;
[EventType]public record ReducersEventSequenceReducerAttributeShipmentDispatched(string TrackingNumber, string Carrier);
public record ReducersEventSequenceReducerAttributeShipmentSummary(string TrackingNumber, string Carrier, DateTimeOffset DispatchedAt);
[Reducer(id: "shipment-summary", eventSequence: "fulfillment-events")]public class ReducersEventSequenceReducerAttributeShipmentSummaryReducer : IReducerFor<ReducersEventSequenceReducerAttributeShipmentSummary>{ public ReducersEventSequenceReducerAttributeShipmentSummary Dispatched(ReducersEventSequenceReducerAttributeShipmentDispatched @event, ReducersEventSequenceReducerAttributeShipmentSummary? current, EventContext context) => new(@event.TrackingNumber, @event.Carrier, context.Occurred);}import io.cratis.chronicle.events.EventTypeimport io.cratis.chronicle.observation.Reducerimport io.cratis.chronicle.readModels.ReadModel
@EventType(id = "reducers-event-sequence-payment-settled")data class ReducersEventSequencePaymentSettled(val amount: Double)
@ReadModeldata class ReducersEventSequencePaymentSummary(val settled: Double = 0.0)
@Reducer(id = "payment-summary", eventSequence = "outbox")class ReducersEventSequencePaymentSummaryReducer { fun settled( event: ReducersEventSequencePaymentSettled, current: ReducersEventSequencePaymentSummary? ): ReducersEventSequencePaymentSummary = ReducersEventSequencePaymentSummary((current?.settled ?: 0.0) + event.amount)}import io.cratis.chronicle.events.EventType;import io.cratis.chronicle.observation.Reducer;import io.cratis.chronicle.readModels.ReadModel;
@EventType(id = "reducers-event-sequence-payment-settled")record ReducersEventSequencePaymentSettled(double amount) {}
@ReadModelrecord ReducersEventSequencePaymentSummary(double settled) { ReducersEventSequencePaymentSummary() { this(0.0); }}
@Reducer(id = "payment-summary", eventSequence = "outbox")class ReducersEventSequencePaymentSummaryReducer { ReducersEventSequencePaymentSummary settled( ReducersEventSequencePaymentSettled event, ReducersEventSequencePaymentSummary current) { double settled = current == null ? 0.0 : current.settled(); return new ReducersEventSequencePaymentSummary(settled + event.amount()); }}import { eventType, reducer } from '@cratis/chronicle';
@eventType()class ReducersEventSequenceReducerAttributeShipmentDispatched { trackingNumber = ''; carrier = '';}
class ReducersEventSequenceReducerAttributeShipmentSummary { trackingNumber = ''; carrier = ''; dispatchedAt: Date = new Date(0);}
@reducer('shipment-summary', 'fulfillment-events', ReducersEventSequenceReducerAttributeShipmentSummary)class ReducersEventSequenceReducerAttributeShipmentSummaryReducer { // Method name must be the exact camelCase of the event's class name reducersEventSequenceReducerAttributeShipmentDispatched( event: ReducersEventSequenceReducerAttributeShipmentDispatched, _current: ReducersEventSequenceReducerAttributeShipmentSummary | undefined ): ReducersEventSequenceReducerAttributeShipmentSummary { return { trackingNumber: event.trackingNumber, carrier: event.carrier, dispatchedAt: new Date() }; }}Both approaches produce the same result. Prefer [Reducer(eventSequence: ...)] when you are already customizing the reducer with other parameters on that attribute.
Convenience: [EventLog]
Section titled “Convenience: [EventLog]”Use [EventLog] when you want to be explicit that the reducer reads from the default event log, even when event types in the assembly carry a [EventStore] attribute pointing elsewhere:
using Cratis.Chronicle.Events;using Cratis.Chronicle.EventSequences;using Cratis.Chronicle.Reducers;
[EventType]public record ReducersEventSequenceLogOrderPlaced(Guid OrderId);
public record ReducersEventSequenceLocalOrderSummary(int OrderCount, DateTimeOffset LastOrderAt);
[EventLog]public class ReducersEventSequenceLocalOrderSummaryReducer : IReducerFor<ReducersEventSequenceLocalOrderSummary>{ public ReducersEventSequenceLocalOrderSummary Placed(ReducersEventSequenceLogOrderPlaced @event, ReducersEventSequenceLocalOrderSummary? current, EventContext context) { var count = current?.OrderCount ?? 0; return new ReducersEventSequenceLocalOrderSummary(count + 1, context.Occurred); }}Kotlin does not support this workflow yet.Java does not support this workflow yet.This convenience is C#-only — it exists to disambiguate against C#‘s [EventStore] attribute on event types, which the other clients don’t have.
External Event Store Inbox Routing
Section titled “External Event Store Inbox Routing”For outbox-to-inbox subscriptions between event stores, including automatic inbox routing and observer-level [EventStore] usage, see Subscribe Reducers to External Event Stores.
When to Use [EventSequence] Explicitly
Section titled “When to Use [EventSequence] Explicitly”- When you need to read from a specific sequence other than the inferred one
- To suppress automatic inbox routing for a reducer that handles foreign events
- For reducers that target a specialized or partitioned event stream
- When an explicit sequence name improves readability and makes intent clear