Events
Events are immutable facts about something that happened in the past. They are the fundamental building blocks of event sourcing — the source of truth from which all state is derived.
What Makes an Event
In Sourcing, events are plain Raku classes:
class AccountOpened {
has UInt $.account-id;
has Rat $.initial-balance;
}
class AmountDeposited {
has UInt $.account-id;
has Rat $.amount;
has Str $.description;
}
class AmountWithdrawn {
has UInt $.account-id;
has Rat $.amount;
}
Events have no methods — they are pure data. They carry the information needed to reconstruct state.
Naming Conventions
Events should be named in the past tense, as facts that have already happened:
| Good ✅ | Bad ❌ |
|---|---|
AccountOpened | OpenAccount (imperative) |
AmountWithdrawn | WithdrawAmount (command-like) |
OrderShipped | ShipOrder (action) |
CustomerNameChanged | ChangeCustomerName (verb) |
An event name describes what happened, not what should happen. Commands are imperative ("Withdraw $100"); events are declarative ("$100 was withdrawn").
Event Structure
Events should contain:
- Identity — Which aggregate instance this event belongs to (e.g.,
$.account-id) - Data — The information needed to apply the event (e.g.,
$.amount) - Context — Optional metadata (e.g.,
$.description,$.timestamp)
Events should not contain:
- Computed values that can be derived from other data
- References to other aggregates (use IDs only)
- Business logic or validation
Immutability
Events are never modified or deleted. Once an event is emitted, it is a permanent fact. If something was wrong, you emit a correcting event — you don't change the original:
# Wrong: modifying history
$event.amount = 200; # NEVER do this
# Right: emit a correcting event
$.amount-corrected: :old-amount(100), :new-amount(200);
Event Emission
Events are emitted by aggregations through auto-generated methods. For an event AmountDeposited, the method $.amount-deposited(:$amount) is automatically generated:
aggregation BankAccount {
has UInt $.account-id is projection-id;
method deposit(Rat $amount) is command {
# This auto-generated method:
# 1. Creates AmountDeposited with :account-id from self
# 2. Passes :$amount to the event constructor
# 3. Emits with optimistic locking
$.amount-deposited: :$amount;
}
}
Event Application
Events are applied through apply methods on projections and aggregations:
method apply(AmountDeposited $e) {
$!balance += $e.amount; # Derive new state from the event
}
The apply method should be idempotent — applying the same event twice should produce the same state.
Event Schema Evolution
As your domain evolves, event schemas will change. Sourcing currently doesn't have built-in upcasting (transforming old event formats to new ones). For production use, plan how you'll handle schema changes — typically through version fields on events or upcasting during replay.
Next Steps
- Learn how Aggregations emit events
- Learn how Projections consume events
- Follow the Quick Start Guide