All checks were successful
CI / build (push) Successful in 1m13s
Distributed actor system with event sourcing for Go: - event.go - Event, ActorSnapshot, EventStore interface - eventbus.go - EventBus, EventBroadcaster for pub/sub - nats_eventbus.go - NATS-backed cross-node event broadcasting - store/ - InMemoryEventStore (testing), JetStreamEventStore (production) - cluster/ - Node discovery, leader election, shard distribution - model/ - EventStorming model types Extracted from arcadia as open-source infrastructure component. Co-Authored-By: Claude <noreply@anthropic.com>
2.9 KiB
2.9 KiB
Aether
Distributed actor system with event sourcing for Go, powered by NATS.
Organization Context
This repo is part of Flowmade. See:
- Organization manifesto - who we are, what we believe
- Repository map - how this fits in the bigger picture
- Vision - what this specific product does
Setup
git clone git@git.flowmade.one:flowmade-one/aether.git
cd aether
go mod download
Requires NATS server for integration tests:
# Install NATS
brew install nats-server
# Run with JetStream enabled
nats-server -js
Project Structure
aether/
├── event.go # Event, ActorSnapshot, EventStore interface
├── eventbus.go # EventBus, EventBroadcaster interface
├── nats_eventbus.go # NATSEventBus - cross-node event broadcasting
├── store/
│ ├── memory.go # InMemoryEventStore (testing)
│ └── jetstream.go # JetStreamEventStore (production)
├── cluster/
│ ├── manager.go # ClusterManager
│ ├── discovery.go # NodeDiscovery
│ ├── hashring.go # ConsistentHashRing
│ ├── shard.go # ShardManager
│ ├── leader.go # LeaderElection
│ └── types.go # Cluster types
└── model/
└── model.go # EventStorming model types
Development
make build # Build the library
make test # Run tests
make lint # Run linters
Architecture
Event Sourcing
Events are the source of truth. State is derived by replaying events.
// Create an event
event := &aether.Event{
ID: uuid.New().String(),
EventType: "OrderPlaced",
ActorID: "order-123",
Version: 1,
Data: map[string]interface{}{"total": 100.00},
Timestamp: time.Now(),
}
// Persist to event store
store.SaveEvent(event)
// Replay events to rebuild state
events, _ := store.GetEvents("order-123", 0)
Namespace Isolation
Namespaces provide logical boundaries for events and subscriptions:
// Subscribe to events in a namespace
ch := eventBus.Subscribe("tenant-abc")
// Events are isolated per namespace
eventBus.Publish("tenant-abc", event) // Only tenant-abc subscribers see this
Clustering
Aether handles node discovery, leader election, and shard distribution:
// Create cluster manager
manager := cluster.NewClusterManager(natsConn, nodeID)
// Join cluster
manager.Start()
// Leader election happens automatically
if manager.IsLeader() {
// Coordinate shard assignments
}
Key Patterns
- Events are immutable - Never modify, only append
- Snapshots for performance - Periodically snapshot state to avoid full replay
- Namespaces for isolation - Not multi-tenancy, just logical boundaries
- NATS for everything - Events, pub/sub, clustering all use NATS