goes - Event-Sourcing Framework for Go

Go Reference Documentation

goes gopher logo

goes is an event-sourcing framework for Go. It gives you the building blocks to model domain logic with aggregates, persist state as events, build read models with projections, and wire the same application to in-memory or production backends.

Why goes?

Install

goes currently targets Go 1.24+.

go get github.com/modernice/goes/...

The /... suffix downloads the framework packages together with the backend implementations.

Quick Start

This is the smallest useful path: define an aggregate, save it to an in-memory event store, then fetch it back by replaying events.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/google/uuid"
	"github.com/modernice/goes/aggregate"
	"github.com/modernice/goes/aggregate/repository"
	"github.com/modernice/goes/event"
	"github.com/modernice/goes/event/eventstore"
)

const (
	listAggregate = "todo.list"
	listCreated   = "todo.list.created"
	itemAdded     = "todo.list.item_added"
)

type (
	listCreatedEvent = event.Of[string]
	itemAddedEvent   = event.Of[string]
)

type List struct {
	*aggregate.Base
	Title string
	Items []string
}

func NewList(id uuid.UUID) *List {
	l := &List{Base: aggregate.New(listAggregate, id)}
	event.ApplyWith(l, l.created, listCreated)
	event.ApplyWith(l, l.added, itemAdded)
	return l
}

func (l *List) Create(title string) {
	aggregate.Next(l, listCreated, title)
}

func (l *List) AddItem(item string) {
	aggregate.Next(l, itemAdded, item)
}

func (l *List) created(evt listCreatedEvent) {
	l.Title = evt.Data()
}

func (l *List) added(evt itemAddedEvent) {
	l.Items = append(l.Items, evt.Data())
}

func main() {
	ctx := context.Background()
	store := eventstore.New()
	lists := repository.Typed(repository.New(store), NewList)

	id := uuid.New()
	list := NewList(id)
	list.Create("Groceries")
	list.AddItem("Milk")
	list.AddItem("Eggs")

	if err := lists.Save(ctx, list); err != nil {
		log.Fatal(err)
	}

	fetched, err := lists.Fetch(ctx, id)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(fetched.Title, fetched.Items)
}

What happens here:

Core Concepts

Backends

Application code stays on framework interfaces like event.Store and event.Bus. Pick the backend at startup.

Component In-memory Production options
Event store event/eventstore backend/mongo, backend/postgres
Event bus event/eventbus backend/nats
Snapshots - backend/mongo
Read models backend/memory backend/mongo

Use the in-memory backends for tests and local experiments. Use MongoDB or PostgreSQL for persisted event streams and NATS for distributed event delivery.

Testing

Event-sourced aggregates are easy to test because they are in-memory state machines. goes also ships github.com/modernice/goes/exp/gtest for aggregate-focused assertions and provides in-memory backends for integration tests without external services.

Learn More

Community

License

Apache License, Version 2.0