All checks were successful
CI / build (push) Successful in 1m25s
- Update manifesto links to use absolute Gitea URLs Co-Authored-By: Claude <noreply@anthropic.com>
3.0 KiB
3.0 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