goes
Type-safe aggregates, commands, and projections - from prototyping to production.

What is goes?
goes is an event sourcing framework for Go that provides the building blocks for distributed, event-driven applications. Type-safe aggregates, commands, and projections - with swappable backends for every scale.
Unlike many event sourcing libraries that are either too academic or too opinionated, goes embraces idiomatic Go. No code generation, no magic annotations - just interfaces, generics, and composition. You define your aggregates as plain Go structs, register event handlers via typed functions, and the framework takes care of persistence, replay, and concurrency.
goes is built for Go teams that want to integrate event sourcing and CQRS into existing or new systems without locking themselves into a specific infrastructure setup. Whether you start with a simple in-memory prototype or run a distributed system with NATS, MongoDB, and PostgreSQL, your domain logic stays identical.
// Define your aggregate type Order struct { *aggregate.Base Items []LineItem Status OrderStatus } // Register typed event handlers func NewOrder(id uuid.UUID) *Order { o := &Order{Base: aggregate.New("order", id)} event.ApplyWith(o, o.placed, "order.placed") event.ApplyWith(o, o.paid, "order.paid") return o } // Produce events from commands func (o *Order) Place(items []LineItem) error { aggregate.Next(o, "order.placed", OrderPlaced{Items: items}, ) return nil } // Rebuild state from events func (o *Order) placed(e event.Of[OrderPlaced]) { o.Items = e.Data().Items o.Status = Placed }
No code generation, no annotations - just Go interfaces, generics, and composition. Your domain logic stays clean and portable.
Why Event Sourcing?
The Problem
Traditional CRUD systems only store the current state. When something goes wrong or an audit is needed, the history is gone. Data gets overwritten, context is lost. Who changed what, when, and why? A plain UPDATE statement can't answer those questions.
The Approach
Event sourcing stores every state change as an event. Current state is reconstructed from the event history. Nothing is lost: full auditability, time-travel debugging, and the ability to build new read models retroactively from existing events. Your event log becomes the single source of truth.
What goes does differently
goes takes the complexity out of event sourcing in Go. No boilerplate, no DSLs - just Go interfaces and generics. You start with in-memory backends and switch to MongoDB, PostgreSQL, or NATS without changing a single line of domain code. The framework handles event routing, aggregate hydration, snapshot management, and projection scheduling, so you can focus on your domain.
The Event Pipeline
Every state change flows through a well-defined pipeline. Commands trigger aggregate logic, which produces events. Events are persisted and projected into read models.
Core Concepts
Event-Sourced Aggregates
Embed the base type, register typed event handlers. The framework handles versioning, persistence, and replay. Aggregates are plain Go structs with an embedded aggregate.Base. Event handlers are registered via generic functions and called automatically during hydration. Soft deletes, aggregate versioning, and optimistic concurrency control are built in.
Distributed Events
Publish and subscribe to events via NATS JetStream. MongoDB and PostgreSQL as event stores. Swap backends without code changes. The event bus abstracts transport and delivery. Your code works with typed events regardless of whether they're local, in-process, or distributed across a cluster.
Type-Safe Commands
Dispatch and handle commands with full generic type safety. Synchronous or asynchronous, depending on the use case. The command bus supports middleware chains, context propagation, and error handling. Async commands are dispatched over the event bus, sync commands are executed directly in-process.
Projection Toolkit
Build read models with continuous or periodic schedules. Automatic progress tracking, debouncing, and startup catch-up. Projections can read from any event store and write to any read database. The scheduler handles error retry, backoff, and guaranteed processing.
Codec Registry
Map event names to Go types for automatic serialization. JSON by default, MessagePack or Protobuf as a one-line swap. The codec registry is central to type-safe event handling. It ensures events are correctly serialized, deserialized, and versioned across your system.
Snapshots
Capture aggregate state at a point in time. Only replay recent events instead of the full history. Snapshots are created automatically at configurable intervals. Supported stores include MongoDB, PostgreSQL, and in-memory, using the same pluggable backend architecture as the event store.
Testable by Design
Fluent test assertions for aggregates, in-memory backends for integration tests, and conformance suites for custom implementations. The testing package provides expect-style assertions: given events, when command, then events. Conformance suites verify that custom event store implementations behave correctly.
Modular Design
Use only what you need - event system, commands, projections - and add more components as required. Each package has clearly defined interfaces and minimal dependencies. You can integrate goes incrementally into an existing project without adopting everything at once.
Production-Ready Backends
goes follows the principle of swappable backends: every event store, snapshot store, and event bus implements a shared interface. You develop against abstractions, not implementations. This makes it possible to test locally with in-memory backends, use MongoDB in staging, and run PostgreSQL with NATS in production, without touching a single line of domain code.
MongoDB
Document-based event store with native change stream support for real-time event subscriptions. Ideal for event-driven architectures where projections need to react to new events in real time. Also supports snapshots and optimistic concurrency via document versioning.
PostgreSQL
Relational event store with ACID transactions and optimistic concurrency control. Perfect for teams that already have a PostgreSQL setup and don't want to introduce additional infrastructure. Serializable isolation guarantees consistent event streams.
NATS JetStream
Distributed event bus with at-least-once delivery, consumer groups, and horizontal scaling. NATS is particularly well suited for microservice architectures where events need to be distributed between services. Persistence via JetStream, replay via consumer offsets.
In-Memory
Zero-config backend for prototyping and testing. Same API as production backends, so your tests and prototypes use identical code to your production environment. No setup, no dependencies, ready to go.
Microservice Architecture
goes provides the backbone for event-driven microservice systems. Each service communicates through the central event bus. Scroll to explore each component.
Domain entities that encapsulate business logic and enforce invariants. The aggregate is the consistency boundary for your events.
Scroll to explore the architecture
1 / 8
Tech Stack
Battle-tested in real projects
goes is not academic. The framework was born from the need to model complex domain logic cleanly, and has been running in production since 2021.
Crovillas
Luxury villa platform with an event-sourced booking engine, multi-app architecture, and gRPC microservices.
DepositDirect
Rental deposit platform with event-sourced payment flows, aggregate-based deposit management, and CQRS read models.
Prestige Cars
Luxury car rental with event-sourced fleet management, booking aggregates, and real-time projections.
... and many more production systems built with goes
FAQ
Frequently asked questions about goes
Yes. goes is used in multiple production environments and actively maintained. The framework has a stable API and is used internally at modernice for client projects. Projects like Crovillas, DepositDirect, and Prestige Cars have been running on goes since 2021.
goes supports MongoDB, PostgreSQL, and NATS JetStream as production backends. For testing and prototyping, there are complete in-memory implementations. All backends implement the same interfaces, making it possible to switch without code changes.
Basic Go knowledge is sufficient. The getting-started guide and the 12-chapter tutorial walk you through all concepts step by step. goes is designed so you can learn and apply event sourcing patterns incrementally.
Yes. goes is modular by design. You can use individual components like the event system or commands independently and integrate more as needed. There's no all-or-nothing dependency.
goes is designed for high-throughput scenarios. Snapshots drastically reduce replay time, projections run asynchronously and in parallel, and the event store supports batching. In production environments, millions of events are processed without noticeable latency.
EventStoreDB is a standalone database, while goes uses your existing infrastructure (MongoDB, PostgreSQL, NATS). Axon is Java-based and significantly more opinionated. goes gives you the building blocks and lets you decide how to assemble them. No runtime dependencies beyond your chosen backend.
goes requires Go 1.23 or newer. The framework makes extensive use of Go generics for type safety across commands, events, and aggregates. We recommend always using the latest stable Go version.
Contributions are welcome. The best starting point is the GitHub repository. Issues labeled 'good first issue' are a great way to get started. PRs are reviewed and merged in a timely manner. For larger changes, we recommend opening an issue first to align on the approach.
Get in Touch
Now that you've learned about our process, why not take the next step and let us help you take your online presence to the next level? Don't wait – schedule a call with us today and let's discuss how we can give your business the recognition it deserves.