Files
aether/CLAUDE.md
Hugo Nijhuis e9e50c021f
All checks were successful
CI / build (push) Successful in 1m13s
Initial aether repository structure
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>
2026-01-08 19:30:02 +01:00

2.9 KiB

Aether

Distributed actor system with event sourcing for Go, powered by NATS.

Organization Context

This repo is part of Flowmade. See:

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