use ddd_cqrs_es::{EventEnvelope, Projection};
impl Projection<BankAccountEvent, String> for AccountDashboard {
type Error = std::convert::Infallible;
/// A unique name identifier for the projection, used to isolate its checkpoint.
fn name(&self) -> &'static str {
"account_dashboard_view"
}
/// Applies events sequentially to update the read model state.
/// This method must be idempotent (safe to run multiple times for the same event).
fn apply(&mut self, envelope: &EventEnvelope<BankAccountEvent, String>) -> Result<(), Self::Error> {
let account_id = envelope.aggregate_id.clone();
match &envelope.payload {
BankAccountEvent::AccountOpened { .. } => {
// Initialize balance idempotently
self.balances.entry(account_id).or_insert(0);
}
BankAccountEvent::MoneyDeposited { amount } => {
let balance = self.balances.entry(account_id).or_insert(0);
*balance += amount;
}
BankAccountEvent::MoneyWithdrawn { amount } => {
let balance = self.balances.entry(account_id).or_insert(0);
// Prevent arithmetic underflows defensively
*balance = balance.saturating_sub(*amount);
}
}
Ok(())
}
}