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
- State validation — Cannot add/remove items after checkout
- Empty cart check — Cannot checkout an empty cart
- Multiple projections — The same events feed both the aggregation and CartSummary
Next Steps
- See the Bank Account Example for a different domain
- Learn about Commands and automatic retry