# Aether Bounded Contexts - Visual Map ## Context Relationship Diagram ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ AETHER SYSTEM │ │ │ │ │ │ ┌──────────────────────┐ │ │ │ EVENT SOURCING │◄─────────────────┐ │ │ │ (Source of Truth) │ │ │ │ │ │ writes │ │ │ │ • Events (immutable) │ events │ │ │ │ • Versions │ │ Application │ │ │ • Snapshots │ │ Business Logic │ │ │ • Replay │ │ │ │ └──────────┬───────────┘ │ │ │ │ │ │ │ │ publishes │ │ │ │ events └──────────────────────────────┘ │ │ │ ▼ │ ┌──────────────────────────────┐ │ │ EVENT BUS │ ◄────────────┐ │ │ (Pub/Sub Distribution) │ │ │ │ │ uses │ │ │ • Local subscriptions │ namespace │ │ │ • NATS cross-node │ patterns │ │ │ • Event filtering │ │ │ │ • Non-blocking delivery │ │ │ └──────────┬───────────────────┘ │ │ │ ┌───────────┴──────────────┐ │ │ │ NAMESPACE ISOLATION │ │ ▼ distributes │ (Logical Boundaries) │ │ to │ │ │ ┌─────────────────┐ │ • Namespace patterns │ │ │ Subscribers │ │ • Stream prefixing │ │ │ (Listeners) │ │ • Wildcard matching │ │ └─────────────────┘ │ • Storage isolation │ │ └─────────────────────────┘ │ │ │ ┌──────────────────────────────┐ │ │ OPTIMISTIC CONCURRENCY │ │ │ (Conflict Detection) │ │ │ │ │ │ • Version validation │ │ │ • Conflict detection │ │ │ • Detailed error info │ │ │ • (App handles retry) │ │ └──────────▲───────────────────┘ │ │ │ │ nested in │ │ EventStore.SaveEvent() │ │ │ ┌──────────┴────────────────────┐ │ │ CLUSTER COORDINATION │ │ │ (Distributed Control) │ │ │ │ │ │ • Node discovery │ │ │ • Leader election │ │ │ • Consistent hash ring │ │ │ • Shard assignment │ │ │ • Rebalancing logic │ │ │ │ │ │ Coordinates via: │ │ │ - LeaderElection (NATS KV) │ │ │ - ShardManager (placement) │ │ │ - EventBus (topology changes) │ │ └───────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ## Detailed Context Interactions ### Single-Node System (Testing/Development) ``` Application │ ├─► SaveEvent() ──► InMemoryEventStore │ │ │ └─► GetLatestVersion() [Optimistic Concurrency] │ └─► Publish() ──► EventBus (local subscriptions) │ └─► Subscriber 1 receives event ``` ### Multi-Node Cluster ``` Node A Node B ┌─────────────────────────┐ ┌─────────────────────────┐ │ ClusterManager │ │ ClusterManager │ │ - NodeInfo: A │◄─NATS──►│ - NodeInfo: B │ │ - LeaderElection │ KV │ - LeaderElection │ │ - ShardMap: {...} │ │ - ShardMap: {...} │ │ │ │ │ └─────────────────────────┘ └─────────────────────────┘ │ │ │ publishes to │ publishes to │ ShardAssigned event │ ShardAssigned event │ │ ▼ ▼ ┌─────────────────────────┐ ┌─────────────────────────┐ │ JetStreamEventStore │ │ JetStreamEventStore │ │ - Stream: events │ │ - Stream: events │ │ - GetEvents() │ │ - GetEvents() │ │ - SaveEvent() │ │ - SaveEvent() │ └─────────────────────────┘ └─────────────────────────┘ │ │ │ SaveEvent │ SaveEvent ▼ triggers ▼ triggers ┌─────────────────────────┐ ┌─────────────────────────┐ │ NATSEventBus │ │ NATSEventBus │ │ Publish(ns, event) │◄─NATS──►│ Subscribe(ns, filter) │ │ │ subject │ │ │ aether.events.{ns} │ routing │ │ └─────────────────────────┘ └─────────────────────────┘ Shared (via NATS JetStream): - Event persistence (durable across nodes) - Leader election state (KV store) - Event distribution (NATS pub/sub) ``` ### Multi-Tenant Scenario ``` Application manages multiple namespaces: Tenant A Tenant B │ │ ├─ JetStreamEventStore ├─ JetStreamEventStore │ namespace: "tenant-a" │ namespace: "tenant-b" │ stream: "tenant-a_events" │ stream: "tenant-b_events" │ │ └─► EventBus └─► EventBus Publish("tenant-a", event) Publish("tenant-b", event) │ │ ▼ ▼ Subscribe("tenant-a") Subscribe("tenant-b") (sees only tenant-a events) (sees only tenant-b events) Optional: Admin wildcard subscription receives both: Subscribe("*") or Subscribe(">") (sees tenant-a AND tenant-b events) ⚠️ SECURITY: Only grant to trusted components ``` ## Context Ownership Matrix | Context | Internal Owner | Data Owner | Responsibility | |---------|---|---|---| | **Event Sourcing** | EventStore | Application (writes events); Aether (persists) | Event persistence, versioning, replay | | **Optimistic Concurrency** | SaveEvent() | Version tracking | Conflict detection; app retries | | **Namespace Isolation** | EventBus + JetStreamEventStore | Application (defines semantics) | Isolation routing; wildcard warnings | | **Cluster Coordination** | ClusterManager + LeaderElection | ClusterManager | Discovery, election, shard assignment | | **Event Bus** | EventBus/NATSEventBus | Aether | Event distribution; filtering; delivery | ## Lifecycle Timelines ### Event Lifetime (per context) ``` Event Sourcing Context: Create ──SaveEvent()─► Store ──Replay()─► Rebuild State ──Forever──► Retention Expires │ │ │ └─► GetEvents() (available for subscribers) │ └─► GetLatestVersion() (for new writers) │ Optimistic Concurrency (during SaveEvent): └─► Check version ──Conflict?── Yes ──► VersionConflictError (app retries) │ No ──► Success Event Bus Context (after SaveEvent succeeds): Event ──Publish(namespace, event)── Matched Subscribers ──► Each gets event │ │ Namespace pattern match Filter match (EventType, ActorPattern) (exact or wildcard) │ Non-blocking delivery ``` ### Shard Lifetime (per context) ``` Cluster Coordination Context: Cluster Formation: Node Joins ──LeaderElection()─► Leader Elected ──ShardAssignment()─► ShardMap Created │ └─► LeadershipLease (TTL: 10s) │ ├─ Heartbeat every 3s (renew lease) │ └─ Lease expires ──► New election Node Failure: Node Fails ──Detection timeout──► ShardMap Updated ──Rebalancing─► NewShardMap (90s implied) │ │ └─► ShardMigrated events ─► Actors replay on new nodes Event Sourcing Integration (during shard migration): Source Node Destination Node │ │ └─ Stop serving ◄───┘ Start accepting │ │ └─ Events still in JetStream (durable) │ └─► Replay from snapshot/event 1 ``` ### Subscription Lifetime (per context) ``` Event Bus Context: Application calls Subscribe(namespace, filter): ──────────────────────────────────────────── Channel created (100-element buffer) │ ├─ Exact match: added to exactSubscribers[namespace] │ └─ Wildcard: added to wildcardSubscribers ├─ If first subscriber for pattern: │ └─ NATSEventBus: create NATS subscription (aether.events.{pattern}) │ Return channel to caller Event arrives (via Publish or NATS): ──────────────────────────────────────────── EventBus.Publish(namespace, event) │ ├─ Deliver to exactSubscribers[namespace] (if matches filter) │ └─ Deliver to wildcardSubscribers (if pattern matches; if filter matches) ├─ Non-blocking send (may drop if channel full) │ └─ Metrics recorded (delivered or dropped) Application calls Unsubscribe(namespace, channel): ──────────────────────────────────────────── Remove from exactSubscribers or wildcardSubscribers │ └─ If last subscriber for pattern: └─ NATSEventBus: unsubscribe from NATS Close channel ``` ## Key Invariants per Context | Context | Invariant | How Enforced | Verified | |---------|-----------|--------------|----------| | **Event Sourcing** | Version strictly monotonic per actor | SaveEvent checks version > current | Error if violated | | **Event Sourcing** | Event immutable after persist | Event struct only read after store | Code review | | **Namespace Isolation** | No cross-namespace reads | Separate JetStream streams | Integration test | | **Cluster Coordination** | Only one leader at a time | Lease-based election (NATS KV) | Lease expiry | | **Cluster Coordination** | All nodes have consistent shard map | Published via ShardAssigned event | Periodic sync | | **Event Bus** | Non-blocking delivery | Select with default (drop) | Code review | | **Optimistic Concurrency** | Conflict detected synchronously | Version check in SaveEvent | Fast-fail | ## Dependency Summary ``` Aether Core (no dependencies between contexts; all dependencies are inbound): ├─ Application │ ├─ Writes to Event Sourcing (SaveEvent) │ ├─ Publishes to Event Bus (Publish) │ ├─ Subscribes to Event Bus (Subscribe) │ └─ Handles Optimistic Concurrency errors (retry logic) │ ├─ Event Sourcing │ ├─ Provides events to Event Bus │ ├─ Provides events to Cluster Coordination (on failover) │ └─ Implements Optimistic Concurrency (SaveEvent validation) │ ├─ Event Bus │ ├─ Routes events from Event Sourcing │ ├─ Routes events from Cluster Coordination │ ├─ Uses Namespace Isolation patterns │ └─ Delivers to all subscribers │ ├─ Namespace Isolation │ ├─ Scopes Event Sourcing (JetStreamConfig.Namespace) │ ├─ Scopes Event Bus (Subscribe pattern) │ └─ Optional; can be omitted (single global namespace) │ ├─ Cluster Coordination │ ├─ Reads from Event Sourcing (GetLatestVersion, replay on failover) │ ├─ Publishes to Event Bus (ShardAssigned, etc.) │ ├─ Can use Namespace Isolation (optional) │ └─ Independent election loop │ └─ Optimistic Concurrency ├─ Embedded in Event Sourcing (SaveEvent) ├─ Errors drive Application retry logic └─ Version state managed by Event Sourcing ``` ## When Contexts Interact | Interaction | Trigger | Duration | Frequency | |-------------|---------|----------|-----------| | App → Event Sourcing | Domain event occurs | Synchronous (ms) | Per business event | | Event Sourcing → Event Bus | SaveEvent succeeds | Async (app controls) | Per business event | | Optimistic Concurrency → App | Version conflict | Synchronous (us) | Per concurrent write | | Cluster → Event Sourcing | Node fails / rebalances | Async (minutes) | Per topology change | | Cluster → Event Bus | Shard assignment changes | Async (seconds) | Per election or failure | | Event Bus → Subscribers | Event published | Non-blocking (drops) | Per business event | | Namespace Isolation → All | Routing decision | Synchronous (us) | Per publish/subscribe | --- ## Testing Strategy by Context ### Event Sourcing (Unit + Integration) ``` Unit: SaveEvent with various versions, GetLatestVersion, GetEvents Integration: Snapshot + replay, corruption recovery, concurrent writes ``` ### Optimistic Concurrency (Unit) ``` Unit: Conflict detection, error details, version semantics Integration: Concurrent writers, contention metrics ``` ### Namespace Isolation (Integration) ``` Integration: Multi-tenant isolation, wildcard safety, cross-namespace verification ``` ### Cluster Coordination (Integration) ``` Integration: Node join/fail, leader election, shard rebalancing, split-brain prevention ``` ### Event Bus (Unit + Integration) ``` Unit: Subscribe/unsubscribe, filtering, exact vs wildcard Integration: NATS distribution, cross-node delivery, dropped event handling ``` --- ## Non-Interactions (Important!) The following are **NOT** direct dependencies (avoid creating them): - **Namespace Isolation ↔ Cluster Coordination**: Cluster works across all namespaces; namespace is orthogonal - **Event Sourcing ↔ Cluster Coordination**: Cluster doesn't own event storage; queries it, doesn't manage it - **Optimistic Concurrency ↔ Event Bus**: Conflicts are events; not bus subscribers - **Application ↔ Cluster**: App doesn't directly manage cluster; uses ClusterManager API - **Event Bus ↔ Event Sourcing**: One-way (sourcing publishes; bus delivers); no feedback loop