Shopping Cart Example

A shopping cart aggregation demonstrating item management, checkout, and a cart summary projection.

Events

class ItemAdded    { has UInt $.cart-id; has Str $.item-name; has Rat $.price }
class ItemRemoved  { has UInt $.cart-id; has Str $.item-name }
class CartCheckedOut { has UInt $.cart-id; has Rat $.total }

Aggregation

aggregation ShoppingCart {
    has UInt $.cart-id is projection-id;
    has Rat  $.total = 0;
    has Str  @.items;
    has Bool $.checked-out = False;

    method apply(ItemAdded $e) {
        @!items.push: $e.item-name;
        $!total += $e.price;
    }
    method apply(ItemRemoved $e) {
        @!items .= grep(* ne $e.item-name);
    }
    method apply(CartCheckedOut $e) {
        $!checked-out = True;
    }

    method add-item(Str $item-name, Rat $price) is command {
        die "Cart already checked out" if $!checked-out;
        $.item-added: :$item-name, :$price;
    }

    method remove-item(Str $item-name) is command {
        die "Cart already checked out" if $!checked-out;
        $.item-removed: :$item-name;
    }

    method checkout() is command {
        die "Cart is empty" if @!items.elems == 0;
        die "Cart already checked out" if $!checked-out;
        $.cart-checked-out: total => $!total;
    }
}

Projection

projection CartSummary {
    has UInt $.cart-id is projection-id;
    has Int  $.item-count = 0;
    has Rat  $.total = 0;
    has Bool $.checked-out = False;

    method apply(ItemAdded $e) {
        $!item-count++;
        $!total += $e.price;
    }
    method apply(ItemRemoved $e) {
        $!item-count--;
    }
    method apply(CartCheckedOut $e) {
        $!checked-out = True;
    }
}

Usage

my $cart = sourcing ShoppingCart, :cart-id(1);

$cart.add-item: 'Laptop', 999.99;
$cart.add-item: 'Mouse', 29.99;
$cart.^update;

say "Items: {$cart.items.join(', ')}";  # Laptop, Mouse
say "Total: {$cart.total}";             # 1029.98

$cart.checkout;
$cart.^update;

my $summary = sourcing CartSummary, :cart-id(1);
say "Cart {$summary.cart-id}: {$summary.item-count} items, total {$summary.total}";
# Cart 1: 2 items, total 1029.98

Key Concepts

Next Steps