Pupsourcing¶
Pupsourcing is a component-based stack for event sourcing in Go. It provides the building blocks to implement event-sourced systems backed by PostgreSQL — an event store, a distributed worker for consumer processing, and an encryption library for protecting sensitive data.
Pupsourcing does not creep into your domain layer. There are no base structs to embed, no aggregate root interfaces to implement, and no framework types leaking into your domain events. Your domain stays pure Go — pupsourcing only shows up in the infrastructure layer where persistence happens.
Quick Start¶
Install the event store:
go get github.com/pupsourcing/store
Append your first event:
import (
"github.com/pupsourcing/store"
storepostgres "github.com/pupsourcing/store/postgres"
)
eventStore := storepostgres.NewStore(storepostgres.DefaultStoreConfig())
tx, _ := db.BeginTx(ctx, nil)
defer tx.Rollback()
result, err := eventStore.Append(ctx, tx, store.NoStream(), []store.Event{{
AggregateType: "User",
AggregateID: userID,
EventID: uuid.New(),
EventType: "UserRegistered",
EventVersion: 1,
Payload: payload,
Metadata: []byte("{}"),
CreatedAt: time.Now(),
}})
if err != nil {
return err
}
tx.Commit()
Read it back:
tx, _ := db.BeginTx(ctx, nil)
defer tx.Rollback()
stream, _ := eventStore.ReadAggregateStream(ctx, tx, "User", userID, nil, nil)
tx.Commit()
for _, event := range stream.Events {
fmt.Printf("v%d %s\n", event.AggregateVersion, event.EventType)
}
The Stack¶
🗃️ Store¶
The event store is an append-only log backed by PostgreSQL. It provides optimistic concurrency control, aggregate versioning, and sequential event reading. Consumers and projections are defined as interfaces that the worker processes.
Get started with the event store →
⚙️ Worker¶
The worker is a distributed consumer processor. It coordinates multiple worker nodes using PostgreSQL for leader election, consumer assignment, and checkpointing. No external coordination services required.
🔐 Encryption¶
The encryption library provides envelope encryption for keeping PII and secrets out of the event store. It supports crypto-shredding for GDPR compliance and key rotation for secret management.
Design Principles¶
- Library, not framework — You control the transaction boundaries and application structure.
- PostgreSQL-native — All coordination, storage, and communication use PostgreSQL. No Kafka, no Redis, no ZooKeeper.
- Explicit over magic — No reflection, no runtime registration. Generated code is readable and inspectable.
- Component-based — Use only what you need. The store, worker, and encryption libraries are independent modules.