Add namespace-scoped event stores for storage isolation
All checks were successful
CI / build (pull_request) Successful in 15s
CI / build (push) Successful in 16s

Add support for optional namespace prefixes on JetStreamEventStore streams
to enable complete namespace isolation at the storage level:

- Add Namespace field to JetStreamConfig
- Add NewJetStreamEventStoreWithNamespace convenience constructor
- Prefix stream names with sanitized namespace when configured
- Add GetNamespace and GetStreamName accessor methods
- Add unit tests for namespace functionality
- Document namespace-scoped stores in CLAUDE.md

The namespace prefix is sanitized (spaces, dots, wildcards converted to
underscores) and prepended to the stream name, ensuring events from one
namespace cannot be read from another namespace's store while maintaining
full backward compatibility for non-namespaced stores.

Closes #19

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit was merged in pull request #48.
This commit is contained in:
2026-01-10 19:01:03 +01:00
parent 484e3ced2e
commit f62964bf3b
3 changed files with 192 additions and 4 deletions

View File

@@ -124,7 +124,11 @@ if errors.Is(err, aether.ErrVersionConflict) {
### Namespace Isolation
Namespaces provide logical boundaries for events and subscriptions:
Namespaces provide logical boundaries for events and subscriptions.
#### Event Bus Namespaces
The event bus supports namespace-scoped pub/sub:
```go
// Subscribe to events in a namespace
@@ -134,6 +138,36 @@ ch := eventBus.Subscribe("tenant-abc")
eventBus.Publish("tenant-abc", event) // Only tenant-abc subscribers see this
```
#### Namespace-Scoped Event Stores
JetStreamEventStore supports optional namespace prefixes for complete storage isolation:
```go
// Create a namespaced event store (convenience function)
store, err := store.NewJetStreamEventStoreWithNamespace(natsConn, "events", "tenant-abc")
// Or configure via JetStreamConfig
config := store.JetStreamConfig{
Namespace: "tenant-abc",
StreamRetention: 30 * 24 * time.Hour,
ReplicaCount: 3,
}
store, err := store.NewJetStreamEventStoreWithConfig(natsConn, "events", config)
// The actual stream name becomes "tenant-abc_events"
// Events from one namespace cannot be read from another namespace's store
```
Namespace isolation at the storage level ensures:
- **Complete data isolation**: Events stored with one namespace prefix are invisible to stores with different namespaces
- **Backward compatibility**: Empty namespace (default) works exactly as before
- **Safe characters**: Namespace names are sanitized (spaces, dots, wildcards become underscores)
Use namespace-scoped stores when you need strong isolation guarantees at the persistence layer, such as:
- Multi-tenant deployments where tenant data must be completely separated
- Logical boundaries between different domains or bounded contexts
- Test isolation in integration tests
### Clustering
Aether handles node discovery, leader election, and shard distribution: