goes

module
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: Apache-2.0

README

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?

  • Typed aggregates, events, commands, and repositories with less boilerplate
  • Backend-agnostic application code that works with in-memory, MongoDB, PostgreSQL, and NATS backends
  • Streaming-first query and subscription APIs that return channels, keeping large workloads incremental and low-memory
  • Production-oriented features like optimistic concurrency, snapshots, and continuous projections
  • Durable, event-driven workflows (sagas) that coordinate long-running processes with timeouts and compensation
  • A practical path from local development to distributed systems without changing your core domain model

Install

goes currently targets Go 1.25+.

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:

  • List is an aggregate that raises events instead of mutating persisted state directly
  • aggregate.Next records and applies events immediately
  • repository.Typed(...) saves uncommitted events and reconstructs aggregates on fetch
  • eventstore.New() keeps everything in memory, so you can prototype and test without infrastructure

Core Concepts

  • aggregate - define consistency boundaries that own state and business rules
  • event - describe immutable facts and store or publish them
  • aggregate/repository - save and rehydrate aggregates from an event store
  • projection - build read-optimized views from event streams
  • command - coordinate intent across aggregates and services when needed
  • codec - register event and command types for serialization across backends

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.

Event Store UI

The repository includes a standalone web console for MongoDB and PostgreSQL event stores. It supports multiple named connections, event and aggregate filters, stream inspection, server-side payload decoding, and optional static login protection. A single Docker image serves both the Nuxt frontend and Go API.

docker pull ghcr.io/modernice/goes-ui:latest

Configuration is supplied through GOES_UI_* environment variables or Docker secrets. The stock image handles JSON event payloads; applications with custom codecs can inject their existing codec.Encoding through the public eventstoreui package. See the Event Store UI guide for configuration and Docker Swarm examples.

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

Directories

Path Synopsis
ref
snapshot/mocks
Package mock_snapshot is a generated GoMock package.
Package mock_snapshot is a generated GoMock package.
api
proto module
backend
nats
Package nats provides an event bus that uses NATS to publish and subscribe to events over a network with support for both NATS Core and NATS JetStream.
Package nats provides an event bus that uses NATS to publish and subscribe to events over a network with support for both NATS Core and NATS JetStream.
natsjs
Package natsjs provides a combined event store and event bus backed by NATS JetStream.
Package natsjs provides a combined event store and event bus backed by NATS JetStream.
testing/eventbustest
Package eventbustest tests event bus implementations.
Package eventbustest tests event bus implementations.
cli
cmd
goes command
goes-ui command
cmdbus
Package cmdbus provides a distributed & event-driven Command Bus.
Package cmdbus provides a distributed & event-driven Command Bus.
contrib
query
Package query provides an event query builder.
Package query provides an event query builder.
query/time
Package time provides time constraints for queries.
Package time provides time constraints for queries.
query/version
Package version provides version constraints for queries.
Package version provides version constraints for queries.
Package eventstoreui provides the standalone web UI server for goes event stores.
Package eventstoreui provides the standalone web UI server for goes event stores.
examples
todo module
exp
helper
env
xevent/etree
Package etree provides a red-black tree for events.
Package etree provides a red-black tree for events.
persistence
Package saga provides an in-process SAGA coordinator that runs a predefined sequence of actions and compensates completed actions when a later action fails.
Package saga provides an in-process SAGA coordinator that runs a predefined sequence of actions and compensates completed actions when a later action fails.
mock_test
Package mock_test is a generated GoMock package.
Package mock_test is a generated GoMock package.
Package workflow provides an event-driven, distributed workflow runtime, also known as a saga or process manager.
Package workflow provides an event-driven, distributed workflow runtime, also known as a saga or process manager.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL