Events & Event handlers
Events are classes that describe facts that have occurred within your domain. In an event-sourced system, events represent immutable pieces of historical data that record what has happened. These events are the primary means by which aggregates communicate changes in state.
Creating an Event
Section titled “Creating an Event”Events are simple objects that represent a fact that has occurred. They are implemented by decorating the class with the @Event() decorator and by implementing the IEvent interface from the library.
The decorator can take an optional name argument, which is used internally to identify the event and map it within your application.
To create an event using this library you will need to make your event class:
- implement the
IEventinterface - apply the
@Event()decorator
import { Event, type IEvent } from '@ocoda/event-sourcing';
@Event('account-opened')export class AccountOpenedEvent implements IEvent { constructor( public readonly accountId: string, public readonly openedOn: string, public readonly accountOwnerIds?: string[], ) {}}The IEvent interface is primarily for typing purposes, while the @Event() decorator marks the class as an event and optionally specifies what the name of that event should look like in the event store. For example, @Event(‘account-opened’) would create the following event-name:
account-openedIf the event name isn’t provided in the decorator, the name of the class will be used as is, so it isn’t converted to lowercase: @Event() on the AccountOpenedEvent class above results in the event-name AccountOpenedEvent.
Registering events
Section titled “Registering events”Events need to be registered in the module before they can be stored and read, otherwise an UnregisteredEventException is thrown. List them in the events of EventSourcingModule.forRoot() or register them per feature module with EventSourcingModule.forFeature(). See Module configuration for more information.
Creating an Event Handler
Section titled “Creating an Event Handler”An EventHandler is responsible for applying the event on the aggregate. When an event is applied to an aggregate, the aggregate looks for a method that matches the provided event. To register a method as being responsible for handling a specific event, you need to decorate the method with the @EventHandler() decorator. This only works in a class that extends AggregateRoot, see Aggregates.
import { Aggregate, AggregateRoot, EventHandler } from '@ocoda/event-sourcing';
@Aggregate({ streamName: 'account' })export class Account extends AggregateRoot { public balance = 0;
public credit(amount: number) { this.applyEvent(new AccountCreditedEvent(amount)); }
@EventHandler(AccountCreditedEvent) applyAccountCreditedEvent(event: AccountCreditedEvent) { this.balance += event.amount; }}Bear in mind this is not the same as an Event Subscriber, which is a separate concept that listens for events and reacts to them.
Event Serialization
Section titled “Event Serialization”By default events are serialized to plain JSON-like payloads and deserialized again by JsonEventSerializer, which needs no decorators. This works well for simple events that only contain plain values.
If you need more advanced events, for example containing Value Objects, it’s possible to provide your own serialization logic by creating an EventSerializer. Events that use class-transformer decorators can use ClassTransformerEventSerializer instead.
For more information on how to create an Event Serializer, please refer to the Event Serialization documentation.
Guidelines for creating events
Section titled “Guidelines for creating events”-
Events should describe domain facts
Section titled “Events should describe domain facts”An event should express a fact that occurred within the domain. Instead of naming events like
BalanceChangedEvent, consider using domain-specific names such asAccountCreditedEventorAccountDebitedEvent. -
Events are immutable
Section titled “Events are immutable”Once an event has been created and persisted, it cannot be changed. This immutability ensures that the system can replay events to rebuild the aggregate’s state exactly as it was.
-
Avoid logic in events
Section titled “Avoid logic in events”Events should be simple data structures that describe what happened. Avoid adding logic to events, as this can lead to unexpected behavior when replaying events.
MIT 2026 © Ocoda.