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

The Aggregate Derive

#[derive(Aggregate)] removes aggregate boilerplate by generating the event enum and aggregate wiring.

Basic Usage

use sourcery::Aggregate;

#[derive(Debug, Default, Aggregate)]
#[aggregate(id = String, error = String, events(FundsDeposited, FundsWithdrawn))]
pub struct Account {
    balance: i64,
}

Then add:

  • Apply<E> impls for each event
  • Handle<C> impls for each command

Attribute Reference

AttributeRequiredDescription
id = TypeYesAggregate identifier type
error = TypeYesError type for command handling
events(E1, E2, ...)YesEvent types this aggregate produces
kind = "name"NoAggregate type identifier (default: lowercase struct name)
event_enum = "Name"NoGenerated enum name (default: {Struct}Event)
derives(T1, T2, ...)NoAdditional derives for generated event enum (always includes Clone)

Typical Customisation

Customising the Kind

#[derive(Aggregate)]
#[aggregate(
    kind = "bank-account",
    id = String,
    error = String,
    events(FundsDeposited)
)]
pub struct Account { /* ... */ }

Customising Event Enum Name

#[derive(Aggregate)]
#[aggregate(
    event_enum = "BankEvent",
    id = String,
    error = String,
    events(FundsDeposited)
)]
pub struct Account { /* ... */ }

Adding Derives to Event Enum

#[derive(Aggregate)]
#[aggregate(
    id = String,
    error = String,
    events(FundsDeposited),
    derives(Debug, PartialEq)
)]
pub struct Account { /* ... */ }

What Gets Generated (Reference)

Given:

#[derive(Aggregate)]
#[aggregate(id = String, error = AccountError, events(FundsDeposited, FundsWithdrawn))]
pub struct Account { balance: i64 }

The macro generates:

  • enum AccountEvent { ... }
  • impl Aggregate for Account
  • impl From<E> for AccountEvent per event
  • impl EventKind for AccountEvent
  • impl serde::Serialize for AccountEvent
  • impl ProjectionEvent for AccountEvent

Event Enum

#[derive(Clone)]
pub enum AccountEvent {
    FundsDeposited(FundsDeposited),
    FundsWithdrawn(FundsWithdrawn),
}

From Implementations

impl From<FundsDeposited> for AccountEvent {
    fn from(event: FundsDeposited) -> Self {
        AccountEvent::FundsDeposited(event)
    }
}

Aggregate Implementation

impl Aggregate for Account {
    const KIND: &'static str = "account";
    type Event = AccountEvent;
    type Error = AccountError;
    type Id = String;

    fn apply(&mut self, event: &Self::Event) {
        match event {
            AccountEvent::FundsDeposited(e) => Apply::apply(self, e),
            AccountEvent::FundsWithdrawn(e) => Apply::apply(self, e),
        }
    }
}

ProjectionEvent Implementation

The generated enum supports replay/deserialisation from stored events by kind.

Requirements

  • Aggregate struct must implement Default.
  • Event types must implement DomainEvent.
  • Serialize + DeserializeOwned on aggregate state is only required when using snapshots.

Next

Manual Implementation — Implementing without the macro