Organize all product strategy and domain modeling documentation into a dedicated .product-strategy directory for better separation from code. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
366 lines
18 KiB
Markdown
366 lines
18 KiB
Markdown
# 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
|