Skip to content

Filter reducers and reactors by tag

Use [FilterEventsByTag] when a reducer or reactor should only handle events that carry specific tags.

Chronicle compares the filter tag against the tags on the appended event. Those tags can come from:

  • [Tag] or [Tags] on the event type
  • The tags: argument when you append the event

[Tag] and [Tags] on the reducer or reactor do not filter anything. They only label the observer itself.

using Cratis.Chronicle;
using Cratis.Chronicle.Events;
using Cratis.Chronicle.EventSequences;
using Cratis.Chronicle.Reactors;
[EventType]
[Tag("customer-lifecycle")]
public record FilterByTagCustomerRegistered(string EmailAddress);
public class FilterByTagCustomerRegistrationService(IEventLog eventLog)
{
public Task Register(string emailAddress) =>
eventLog.Append(
EventSourceId.New(),
new FilterByTagCustomerRegistered(emailAddress),
tags: ["vip", "onboarding"]);
}
[FilterEventsByTag("vip")]
public class FilterByTagVipWelcomeReactor : IReactor
{
public Task Registered(FilterByTagCustomerRegistered @event, EventContext context) => Task.CompletedTask;
}

The reactor receives the event because the append call added the vip tag.

using Cratis.Chronicle;
using Cratis.Chronicle.Events;
using Cratis.Chronicle.EventSequences;
using Cratis.Chronicle.Reducers;
[EventType]
public record FilterByTagOrderPlaced(decimal TotalAmount);
public record FilterByTagPriorityOrderTotals(decimal TotalAmount);
[FilterEventsByTag("priority")]
public class FilterByTagPriorityOrderTotalsReducer : IReducerFor<FilterByTagPriorityOrderTotals>
{
public FilterByTagPriorityOrderTotals Placed(FilterByTagOrderPlaced @event, FilterByTagPriorityOrderTotals? current, EventContext context) =>
new((current?.TotalAmount ?? 0m) + @event.TotalAmount);
}
public class FilterByTagCheckoutService(IEventLog eventLog)
{
public Task PlacePriorityOrder(decimal totalAmount) =>
eventLog.Append(
EventSourceId.New(),
new FilterByTagOrderPlaced(totalAmount),
tags: ["priority"]);
}

The reducer only updates when the appended event carries the priority tag.

Multiple [FilterEventsByTag] attributes widen the match:

using Cratis.Chronicle.Events;
using Cratis.Chronicle.Reactors;
[EventType]
public record FilterByTagMultiCustomerRegistered(string EmailAddress);
[FilterEventsByTag("vip")]
[FilterEventsByTag("priority")]
public class FilterByTagMultiPriorityNotificationsReactor : IReactor
{
public Task Registered(FilterByTagMultiCustomerRegistered @event) => Task.CompletedTask;
}

Chronicle dispatches the event if it has either vip or priority.