Event-Sourced Aggregate Store

Event-Sourced Aggregate Store

The event-sourced aggregate store loads and saves aggregates using an event store.

Aggregates are hydrated by streaming events from an event store and applying them to an aggregate’s root entity to reconstruct its state. Changes to aggregates are saved by appending events to the event store representing incremental state changes.

This functionality – the loading and saving of aggregates using event streams – is central to event-sourcing using Estoria.

Usage

import (
    "context"
    "github.com/go-estoria/estoria/aggregatestore"
    "github.com/go-estoria/estoria/eventstore/memory"
    "github.com/google/uuid"
)

func main() {
    // create an event store
    eventStore, _ := memory.NewEventStore()

    // create the aggregate store
    store, _ := aggregatestore.New(eventStore, NewThing,
        aggregatestore.WithEventTypes(
            &NameChangedEvent{},
        ),
    )

    id := uuid.Must(uuid.NewV4())

    // create an aggregate
    aggregate, _ := store.New(id)

    // append some events
    _ = aggregate.Append(&NameChangedEvent{NewName: "Juliette"})

    // save the aggregate
    _ = store.Save(context.Background(), aggregate, nil)
}

API Overview

Creating an Event Sourced Aggregate Store

Creating a new event sourced aggregate store requires two things:

  • an event store, so that it can save and load events for the aggregate;
  • an entity factory function, so that it can create new instances of the aggregate’s root entity type.

Each aggregate store works specifically with a single aggregate type. Applications working with multiple aggregates should create aggregate stores for each aggregate type.

Creating a New Aggregate

The New() method creates a new aggregate with a default version of 0 (no applied events) and a default root entity using the configured entity factory function.

func New(id uuid.UUID) *Aggregate[E]

Loading an Aggregate

The Load() method is a convenience method that instantiates a new aggregate using New() and then hydrates it by calling Hydrate().

func Load(ctx context.Context, id uuid.UUID, opts *LoadOptions) (*Aggregate[E], error)

Hydrating an Aggregate

Hydrating refers to the process of bringing an aggregate from its current state to some desired latter state.

The Hydrate() method streams events from the event store beginning at version N+1, where N is the current version of the aggregate, and applies them to the aggregate root entity.

func Hydrate(ctx context.Context, agg *Aggregate[E], opts *HydrateOptions) error

Saving an Aggregate

The Save() method appends new events to the event store representing changes to the aggregate root entity.

func Save(ctx context.Context, agg *Aggregate[E], opts *SaveOptions) error