Skip to content

Subscribe Reducers to External Event Stores

This page shows automatic inbox selection and subscription from event-store metadata on events or observers. For a subscription you configure explicitly, use explicit event-store subscriptions.

Self reducers use the same inbox routing. Their subscription event set comes from the owning registry’s selected static transitions; query methods and other helpers do not add subscription events.

When all event types reduced by a reducer come from the same source event store, Chronicle routes the reducer to the matching inbox sequence automatically:

  • Source event store: fulfillment-service
  • Inbox sequence observed by the reducer: inbox-fulfillment-service

This works when the source is declared on event types (or assembly) using [EventStore("...")], and no explicit event sequence is configured on the reducer.

using Cratis.Chronicle.Events;
using Cratis.Chronicle.Reducers;
[EventType]
[EventStore("fulfillment-service")]
public record ExternalReducerShipmentDispatched(Guid OrderId, string TrackingNumber);
public record ExternalReducerFulfillmentStatus(string Status, string TrackingNumber);
public class ExternalReducerFulfillmentStatusReducer : IReducerFor<ExternalReducerFulfillmentStatus>
{
public ExternalReducerFulfillmentStatus Dispatched(ExternalReducerShipmentDispatched @event, ExternalReducerFulfillmentStatus? current, EventContext context) =>
new("Dispatched", @event.TrackingNumber);
}

View C# snippet source on GitHub

Observer-Level EventStore for Missing Event Metadata

Section titled “Observer-Level EventStore for Missing Event Metadata”

When event contracts cannot carry [EventStore] metadata, apply [EventStore] directly on the reducer.

Chronicle resolves the reducer to the inbox for that source event store and implicitly creates or reuses the outbox-to-inbox subscription.

using Cratis.Chronicle.Events;
using Cratis.Chronicle.Reducers;
[EventType]
public record ExternalReducerUserInvited(Guid UserId);
public record ExternalReducerUserTotals(int Count);
[EventStore("identity-service")]
[Reducer]
public class ExternalReducerUserTotalsReducer : IReducerFor<ExternalReducerUserTotals>
{
public ExternalReducerUserTotals Invited(ExternalReducerUserInvited @event, ExternalReducerUserTotals? current, EventContext context) =>
new((current?.Count ?? 0) + 1);
}

View C# snippet source on GitHub

For each source event store, Chronicle combines all reducer and reactor event types into one implicit subscription filter.

This means:

  • You get one subscription per source event store.
  • The filter is the union of handled event types from all participating observers.
  • Only those event types are forwarded from source outbox to target inbox.

Explicit Event Sequence Is Not Allowed with Observer EventStore

Section titled “Explicit Event Sequence Is Not Allowed with Observer EventStore”

When a reducer has [EventStore("...")], do not combine it with:

  • [EventSequence("...")]
  • [EventLog]
  • [Reducer(eventSequence: "...")]

This is invalid and fails:

  • At runtime with EventStoreCannotBeCombinedWithExplicitEventSequence.
  • At compile time with analyzer rule CHR0014.

Inbox forwarding now preserves append tags along with event-source and stream-route metadata. Tag filters can select forwarded events for classic and self reducers alike. Events forwarded by earlier releases that dropped tags remain untagged; upgrading does not backfill them.