Skip to main content

Event Handlers in Aevatar: [EventHandler] & [AllEventHandler]

Introduction​

Event handlers are a cornerstone of the Aevatar framework's event-driven model. They allow GAgents to react to domain events, trigger workflows, and coordinate distributed logic. In Aevatar, event handler events are distinct from event sourcing events: the former are messages that flow between agents and should inherit from EventBase, while the latter are used for state mutation and may inherit from StateLogEventBase<T>.

For a broader context, see the Overview and GAgent documentation.


Why Event Handlers Matter​

  • Business Logic Encapsulation: Event handlers let you define how your agent responds to domain events, keeping business logic modular and testable.
  • Distributed Communication: Handlers process events sent from other agents, enabling decoupled, scalable workflows.
  • State Change Triggers: Handlers can raise new events (including event sourcing events) to mutate state in a durable, auditable way.

How Event Handlers Are Discovered​

Aevatar uses reflection (see GAgentBase.Observers.cs) to automatically discover methods decorated with [EventHandler] or [AllEventHandler] attributes. When an event is received, the framework routes it to the appropriate handler method based on the event type.

  • [EventHandler]: Marks a method as the handler for a specific event type (inheriting from EventBase).
  • [AllEventHandler]: Marks a method that can handle all events (useful for logging, auditing, or generic processing).

Discovery Logic:

  • The framework scans all methods in your GAgent for these attributes.
  • When an event arrives, it matches the event type (must inherit from EventBase) to the handler's parameter type.
  • If multiple handlers match, all are invoked.
  • If no specific handler is found, an [AllEventHandler] method (if present) is called.

Defining an Event Handler​

1. Define Your Event Type (for Event Handlers)​

Event handler events should inherit from EventBase. This ensures they are compatible with the event routing and pub/sub system.

public class MyDomainEvent : EventBase {
public string Data { get; set; }
}

2. Implement the Handler Method​

  • Decorate the method with [EventHandler].
  • The method must accept a single parameter of your event type (inheriting from EventBase).
  • The method can be async or synchronous.
[EventHandler]
public async Task HandleMyDomainEventAsync(MyDomainEvent evt)
{
// Business logic here
// Optionally raise new events or update state
}

3. Using AllEventHandler​

If you want to handle all events generically (e.g., for logging):

[AllEventHandler]
public Task HandleAllEventsAsync(EventBase evt)
{
// This will be called for any event if no specific handler is found
return Task.CompletedTask;
}

Persisting State Changes with RaiseEvent (Event Sourcing)​

To change the GAgent's state in a durable, event-sourced way, use RaiseEvent inside your handler. Note: The event you pass to RaiseEvent should typically inherit from StateLogEventBase<T>, not EventBase.

[EventHandler]
public async Task HandleMyDomainEventAsync(MyDomainEvent evt)
{
// Apply business logic
var stateEvent = new MyStateLogEvent { ... };
RaiseEvent(stateEvent); // This will persist the event and trigger a state transition
await ConfirmEvents(); // Ensures the event is committed
}
  • RaiseEvent: Persists the event (of type StateLogEventBase<T>) and schedules a state transition.
  • ConfirmEvents: Commits the event, making the state change durable.

The state transition logic should be implemented in your override of GAgentTransitionState:

protected override void GAgentTransitionState(MyAgentState state, StateLogEventBase<MyStateLogEvent> evt)
{
if (evt is MyStateLogEvent myEvent)
{
state.SomeProperty = myEvent.Data;
}
}

Best Practices​

  • Always use strongly-typed event classes for clarity and safety.
  • Event handler events should inherit from EventBase.
  • State mutation events (for event sourcing) should inherit from StateLogEventBase<T>.
  • Keep handler methods focused—one event, one responsibility.
  • Use [AllEventHandler] sparingly for cross-cutting concerns.
  • Always call RaiseEvent and ConfirmEvents to ensure state changes are persisted.
  • Document your event types and handlers for maintainability.

Further Reading​


By following these patterns, you can build robust, maintainable, and scalable event-driven agents with Aevatar.