Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Commands

Commands request a state change. They can be rejected.

Basic Usage

  1. Define a command struct
  2. Implement Handle<C> on your aggregate
  3. Return events or a domain error
#[derive(Debug)]
pub struct Deposit {
    pub amount: i64,
}

impl Handle<Deposit> for Account {
    fn handle(&self, cmd: &Deposit) -> Result<Vec<Self::Event>, Self::Error> {
        if cmd.amount <= 0 {
            return Err(AccountError::InvalidAmount);
        }
        Ok(vec![FundsDeposited { amount: cmd.amount }.into()])
    }
}

Executing Commands

repository
    .execute_command::<Account, Deposit>(&account_id, &Deposit { amount: 100 }, &metadata)
    .await?;

Validation Patterns

  • Reject invalid input (Result::Err)
  • Emit one or more events when valid
  • Return vec![] for valid no-op commands
if cmd.amount == 0 {
    return Ok(vec![]);
}

Trait Reference

The Handle<C> Trait

pub trait Handle<C>: Aggregate {
    /// Handle a command and produce events.
    ///
    /// # Errors
    ///
    /// Returns `Self::Error` if the command is invalid for the current
    /// aggregate state.
    fn handle(&self, command: &C) -> Result<Vec<Self::Event>, Self::Error>;
}

Key points:

  • Takes &self (validate against current state)
  • Returns Vec<Event> (0..n events)
  • Returns Result (commands can fail)

Command Naming

Commands are imperative:

GoodBad
DepositFundsDeposited
PlaceOrderOrderPlaced
RegisterUserUserRegistered
ChangePasswordPasswordChanged

Next

Projections — Building read models from events