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 ❌
AccountOpenedOpenAccount (imperative)
AmountWithdrawnWithdrawAmount (command-like)
OrderShippedShipOrder (action)
CustomerNameChangedChangeCustomerName (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:

Events should not contain:

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

Future Consideration

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