State Stored Aggregate

In Domain-Driven Design (DDD), an aggregate is a cluster of related objects that are treated as a single unit. There are two main types of aggregates: state stored and event sourced.

A state stored aggregate is an aggregate that is based on the current state of its objects. The current state is stored in a database, and updates to the state are made by modifying the object’s properties directly. The state stored aggregate is usually implemented using an object-relational mapper (ORM) or a similar data access technology.

In contrast, an event sourced aggregate is based on a sequence of events that have occurred in the system. The events are stored in an event store, and the current state of the aggregate is derived by replaying the events. Each event represents a change to the state of the aggregate, and the events are used to reconstruct the current state of the aggregate.

The main difference between the two types of aggregates is the way in which they store their state. State stored aggregates store their state in a database, while event sourced aggregates store their state in a sequence of events. State stored aggregates are typically easier to implement and provide better performance for simple use cases. However, event sourced aggregates are more resilient to change and can provide a better audit trail of changes made to the system over time.

Example

Suppose we have an e-commerce application, and we want to model a shopping cart. We can create a Cart aggregate that maintains the state of the cart, including the items in the cart and the total price. The Cart aggregate can have methods to add and remove items, as well as to calculate the total price.

public class Cart {
  private Map<String, Integer> items = new HashMap<>();
  private BigDecimal total = BigDecimal.ZERO;

  public void addItem(String productId, int quantity, BigDecimal price) {
    if (items.containsKey(productId)) {
      int existingQuantity = items.get(productId);
      items.put(productId, existingQuantity + quantity);
    } else {
      items.put(productId, quantity);
    }
    total = total.add(price.multiply(BigDecimal.valueOf(quantity)));
  }

  public void removeItem(String productId, int quantity, BigDecimal price) {
    if (!items.containsKey(productId)) {
      return;
    }

    int existingQuantity = items.get(productId);
    if (existingQuantity < quantity) {
      return;
    }

    int newQuantity = existingQuantity - quantity;
    if (newQuantity == 0) {
      items.remove(productId);
    } else {
      items.put(productId, newQuantity);
    }
    total = total.subtract(price.multiply(BigDecimal.valueOf(quantity)));
  }

  public BigDecimal getTotal() {
    return total;
  }
}

This Cart aggregate is a state-stored aggregate because it maintains its state in memory. When an operation is performed on the Cart, such as adding or removing an item, the state is updated in memory. There is no persistence of the events that led to the current state.