Clean Event Sourcing with Go

A pragmatic Go library for building event-sourced systems that handles infrastructure without creeping into your domain model.

🏗️ Mature Infrastructure

ES infrastructure that doesn't creep into your domain logic.

⚡ Simple Yet Powerful

Easy to start and use, with powerful capabilities when you need them.

🧠 Go Native

Feels idiomatic and explicit.

Pupsourcing

Event Sourcing infrastructure for Go that handles the complexity without creeping into your domain model.


Quick Start

1. Install

go get github.com/getpup/pupsourcing

Choose your database driver:

# PostgreSQL
go get github.com/lib/pq

Generate database schema:

# Generate SQL migrations for your database
go run github.com/getpup/pupsourcing/cmd/migrate-gen -output migrations

# Load the generated SQL into your database

2. Save an Event

import (
    "github.com/getpup/pupsourcing/es"
    "github.com/getpup/pupsourcing/es/adapters/postgres"
)

// Create event store
store := postgres.NewStore(postgres.DefaultStoreConfig())

// Define and save your event
event := es.Event{
    BoundedContext: "Identity",
    AggregateType:  "User",
    AggregateID:    userID,
    EventType:      "UserCreated",
    EventVersion:   1,
    Payload:        payload,
}

tx, _ := db.BeginTx(ctx, nil)
result, _ := store.Append(ctx, tx, es.NoStream(), []es.Event{event})
tx.Commit()

3. Read a Stream

// Read all events for an aggregate
stream, err := store.ReadAggregateStream(
    ctx, tx,
    "Identity",  // bounded context
    "User",      // aggregate type
    userID,      // aggregate ID
    nil, nil,    // version range
)

// Process events
for _, event := range stream.Events {
    // Apply event to rebuild state
}

Why Pupsourcing?

Mature Infrastructure — Battle-tested event sourcing that handles persistence, concurrency, and projections without touching your domain logic.

Simple to Start — Get running quickly with sensible defaults, then unlock powerful capabilities like partitioned projections and temporal queries.

Go Native — Pure Go with idiomatic APIs. No annotations, no magic—just clean, explicit code.


Learn More