Reactors
A Chronicle reactor is an observer that runs side effects when events are appended.
Reactors are observers that react to events and execute side effects. They are ideal for integrating with external systems, sending notifications, or emitting follow-up actions when events occur.
Key Concepts
Section titled “Key Concepts”- Event-driven side effects - Reactors run when events are appended and can call external services or enqueue work
- Convention-based methods - Public methods with supported signatures are discovered automatically
- Event context access - Optional
EventContextprovides metadata like timestamps and identifiers - Event-source isolation - Events are processed per event source, in order, to keep behavior consistent
When to Use Reactors
Section titled “When to Use Reactors”Reactors are a good fit when you need to:
- Trigger notifications or workflows when specific events occur
- Synchronize with external systems
- Run validations or side effects that do not belong in projections or reducers
- Emit follow-up events based on patterns in the event stream
Basic Example
Section titled “Basic Example”using Cratis.Chronicle.Events;using Cratis.Chronicle.Reactors;
[EventType]public record ReactorsIndexEmailConfirmed(string Email);
public class ReactorsIndexEmailNotificationsReactor : IReactor{ public Task Confirmed(ReactorsIndexEmailConfirmed @event, EventContext context) => SendConfirmationAsync(@event.Email, context.Occurred);
Task SendConfirmationAsync(string email, DateTimeOffset occurred) => Task.CompletedTask;}import io.cratis.chronicle.events.EventContextimport io.cratis.chronicle.events.EventTypeimport io.cratis.chronicle.observation.Reactor
@EventTypedata class ReactorsIndexEmailConfirmed(val email: String)
@Reactorclass ReactorsIndexEmailNotificationsReactor { fun confirmed(event: ReactorsIndexEmailConfirmed, context: EventContext) { sendConfirmation(event.email, context.occurred) }
private fun sendConfirmation(email: String, occurred: java.time.Instant) {}}import io.cratis.chronicle.events.EventContext;import io.cratis.chronicle.events.EventType;import io.cratis.chronicle.observation.Reactor;import java.time.Instant;
@EventTyperecord ReactorsIndexEmailConfirmed(String email) {}
@Reactorclass ReactorsIndexEmailNotificationsReactor { void confirmed(ReactorsIndexEmailConfirmed event, EventContext context) { sendConfirmation(event.email(), context.getOccurred()); }
private void sendConfirmation(String email, Instant occurred) {}}defmodule MyApp.Events.ReactorsIndexEmailConfirmed do use Chronicle.Events.EventType, id: "reactors-index-email-confirmed"
defstruct [:email]end
defmodule MyApp.Reactors.ReactorsIndexEmailNotificationsReactor do use Chronicle.Reactors.Reactor
alias MyApp.Events.ReactorsIndexEmailConfirmed
@handles ReactorsIndexEmailConfirmed
@impl true def handle(%ReactorsIndexEmailConfirmed{} = event, context) do send_confirmation(event.email, Map.get(context, :occurred))
:ok end
defp send_confirmation(_email, _occurred), do: :okendimport { eventType, onceOnly, reactor, EventContext } from '@cratis/chronicle';import { field } from '@cratis/fundamentals';
@eventType()class ReactorsIndexEmailConfirmed { @field(String) readonly email: string;
constructor(email: string) { this.email = email; }}
@reactor()class ReactorsIndexEmailNotificationsReactor { // Method name must be the exact camelCase of the event's class name - // Chronicle discovers handlers by name, not by parameter type. @onceOnly() async reactorsIndexEmailConfirmed(event: ReactorsIndexEmailConfirmed, context: EventContext): Promise<void> { await this.sendConfirmation(event.email, context.occurred); }
private async sendConfirmation(email: string, occurred: Date): Promise<void> {}}For composition-root registration, the .NET client can nominate a plain instance-method reactor without IReactor. Dispatch and dependency injection stay the same; see explicit artifact registration.
Topics
Section titled “Topics”- Getting Started
- Subscribe to External Event Stores
- Event Processing
- Returning Side Effects
- Event Sequence
- Once-Only Processing
- Start at the Tail
- Replay
- Delivery Identity
- Filtering by appended event metadata
- Tagging Reactors
Reactors react to events. To react instead to a read model being added, modified or removed — with the same convention-based methods, dependencies and side effects — see Reacting to Read Model Changes.