applyEvent method
Applies event to current and returns the next state (Eventador
pattern; bead libspiffy-mmb).
current is never modified: the collaborators fill in a draft of it
that shares every collection the event does not change, and eventador's
eventHandler replaces the aggregate's state with the result only once
the whole event has applied. A state handed out earlier (a query, a
snapshot, a command handler) keeps its contents, and an event that fails
midway changes nothing. The appliers replace the draft's immutable
collections, never modify them, and never touch the aggregate's current
state.
Implementation
@override
WalletState applyEvent(WalletState current, Event event) {
if (event is! WalletEvent) {
throw ArgumentError('Expected WalletEvent, got ${event.runtimeType}');
}
final state = current.toBuilder();
switch (event) {
case final WalletCreatedEvent evt:
WalletLifecycle.applyWalletCreated(state, evt);
case final WalletDeletedEvent evt:
WalletLifecycle.applyWalletDeleted(state, evt);
case final WalletConfigurationUpdatedEvent evt:
WalletLifecycle.applyWalletConfigurationUpdated(state, evt);
case final AddressGeneratedEvent evt:
AddressBook.applyAddressGenerated(state, evt);
case final AddressLabelUpdatedEvent evt:
AddressBook.applyAddressLabelUpdated(state, evt);
case final WatchAddressAddedEvent evt:
AddressBook.applyWatchAddressAdded(state, evt);
case final UTXOReceivedEvent evt:
UtxoLedger.applyReceived(state, evt);
case final UTXOMarkedAvailableEvent evt:
UtxoLedger.applyMarkedAvailable(state, evt);
case final UTXOSpentEvent evt:
UtxoLedger.applySpent(state, evt);
case final UTXOConfirmationUpdatedEvent evt:
UtxoLedger.applyConfirmationUpdated(state, evt);
case TransactionSignedEvent() || TransactionBroadcastEvent():
// Transaction state is managed separately - just update version
state.version = event.version;
// Replay-only: nothing emits these three any more (reachability sweep
// 2026-09-18, section 2; audit 2026-09-14 M3 replaced them). The arm
// stays because a journal written by an earlier release contains them
// and must still replay without throwing.
// ignore: deprecated_member_use_from_same_package
case UTXOReservationPlacedEvent() || UTXOReservationReleasedEvent() || UTXOReservationExpiredEvent():
// Not applied to UTXOs (UTXOReservedEvent and UTXOReleasedEvent are)
_touch(state, event);
case final UTXOReservedEvent evt:
UtxoReservations.applyReserved(state, evt);
case final UTXOReleasedEvent evt:
UtxoReservations.applyReleased(state, evt);
case final UTXOReservationRenewedEvent evt:
UtxoReservations.applyRenewed(state, evt);
case final AddressDiscoveredEvent evt:
AddressBook.applyAddressDiscovered(state, evt);
case final AnchorKeyIssuedEvent evt:
Type42Book.applyAnchorKeyIssued(state, evt);
case final Type42AddressRecordedEvent evt:
Type42Book.applyAddressRecorded(state, evt);
case final Type42DestinationDerivedEvent evt:
Type42Book.applyDestinationDerived(state, evt);
case final TransactionImportedEvent evt:
OutgoingTransactions.applyImported(state, evt);
case final TransactionRecordedEvent evt:
OutgoingTransactions.applyRecorded(state, evt);
case final TransactionConfirmedEvent evt:
OutgoingTransactions.applyConfirmed(state, evt);
case TransactionStatusUpdatedEvent():
// Status update is projection-only — no aggregate state change needed
break;
case final TransactionConfirmationRevertedEvent evt:
OutgoingTransactions.applyConfirmationReverted(state, evt);
// All three are replay-only (reachability sweep 2026-09-18, section 2;
// UTXOSplitInitiatedEvent since bead libspiffy-lph4) and their arm
// stays so an older journal still replays without throwing.
// ignore: deprecated_member_use_from_same_package
case UTXOSplitInitiatedEvent() || UTXOSplitCompletedEvent() || AllUTXOsSplitCompletedEvent():
// Informational: BenfordCoordinatorActor orchestrates the split, and
// its spends, receipts and records arrive as their own commands.
_touch(state, event);
case final TransactionSpendDeferredEvent evt:
DeferredPayments.applySpendDeferred(state, evt);
case final TransactionNetworkStatusCheckedEvent evt:
DeferredPayments.applyNetworkStatusChecked(state, evt);
case final DeferredTransactionFailedEvent failed:
DeferredPayments.applyFailed(state, failed);
case final DeferredTransactionCancelledEvent cancelled:
DeferredPayments.applyCancelled(state, cancelled);
case final DeferredSpendReclaimedEvent reclaimed:
DeferredPayments.applyReclaimed(state, reclaimed);
case final DeferredSpendCompletedEvent completed:
DeferredPayments.applyCompleted(state, completed);
case final TransactionVoidedEvent voided:
DeferredPayments.applyVoided(state, voided);
case final TransactionAncestorsRecordedEvent recorded:
OutgoingTransactions.applyAncestorsRecorded(state, recorded);
default:
throw ArgumentError('Unknown event type: ${event.runtimeType}');
}
final next = state.build();
_deferred.stateApplied(current, next, event);
return next;
}