Skip to main content

Architecture

This document covers ⍼ Angzarr's core architectural concepts: event sourcing data model, coordinator pattern, deployment model, and synchronization modes.


Event Sourcing Data Model

⍼ Angzarr stores aggregate history as an EventBook—the complete event stream for a single aggregate root:

ComponentPurpose
CoverIdentity: domain, aggregate root ID, correlation ID
SnapshotPoint-in-time state for replay optimization
EventPagesOrdered sequence of domain events
message Cover {
string domain = 2;
UUID root = 1;
string correlation_id = 3; // Workflow correlation - flows through all commands/events
Edition edition = 4; // Edition for diverged timelines; empty name = main timeline
}
message EventPage {
uint32 sequence = 1;
google.protobuf.Timestamp created_at = 2;
oneof payload {
google.protobuf.Any event = 3;
PayloadReference external = 4; // Claim check: payload stored externally
}
}
// Snapshot of aggregate state at a given sequence number.
// State must be a protobuf Message to serialize into Any.
message Snapshot {
uint32 sequence = 2;
google.protobuf.Any state = 3;
SnapshotRetention retention = 4; // Controls cleanup behavior
}
message EventBook {
Cover cover = 1;
Snapshot snapshot = 2; // Snapshot state; sequence computed by framework on persist
repeated EventPage pages = 3;
// Field 4 removed: correlation_id moved to Cover
// Field 5 removed: snapshot_state unified into snapshot field
uint32 next_sequence = 6; // Computed on load, never stored: (last page seq OR snapshot seq if no pages) + 1
}

Commands follow the same pattern—a CommandBook contains one or more CommandPages targeting a single aggregate.


Coordinator Pattern

⍼ Angzarr uses coordinators to route messages between external clients and your business logic. This separation keeps domain code focused while the framework handles:

  • Event persistence and retrieval
  • Optimistic concurrency via sequence numbers
  • Snapshot management
  • Event upcasting (schema evolution on read)
  • Synchronous vs. asynchronous processing

Component Types

CoordinatorRoutesPurpose
AggregateCoordinatorCommands → AggregatesCommand handling, event persistence
ProjectorCoordinatorEvents → ProjectorsRead model updates, side effects
SagaCoordinatorEvents → SagasCross-domain command orchestration
ProcessManagerCoordinatorEvents → PMsStateful multi-domain workflows

Sidecar Deployment

⍼ Angzarr runs as a sidecar container alongside your business logic. Each pod contains your code and the appropriate coordinator, communicating over localhost gRPC.

Benefits

  • Minimal attack surface: ~8MB distroless container, no shell, no package manager
  • No network exposure: Sidecar communicates over localhost only
  • Horizontal scaling: Follows your service scaling—no separate capacity planning
  • Local gRPC: Eliminates network latency between logic and framework

Synchronization Modes

⍼ Angzarr provides mechanisms for controlling sync vs async communication — when results return to callers.

SyncMode

// Controls synchronous processing behavior
enum SyncMode {
SYNC_MODE_UNSPECIFIED = 0; // Async: fire and forget (default)
SYNC_MODE_SIMPLE = 1; // Sync projectors only, no saga cascade
SYNC_MODE_CASCADE = 2; // Full sync: projectors + saga cascade (expensive)
}
ModeProjectorsSagasUse Case
NONEAsyncAsyncFire-and-forget, eventual consistency
SIMPLESyncAsyncRead-after-write for single aggregate
CASCADESyncSync (recursive)Synchronous cross-aggregate workflows

The Cascade Flow

When sync_mode = CASCADE, the framework orchestrates the complete cascade:

The dashed Domain B represents the target aggregate—sagas bridge events from one domain to commands in another. The cascade recursively waits for the target aggregate's events before returning.

Warning: CASCADE is expensive and does not provide ACID guarantees. Each step adds latency. Start with NONE, move to SIMPLE for read-after-write, reserve CASCADE for workflows requiring synchronous cross-aggregate coordination.

Event Streaming

For observing events as they happen rather than waiting:

// EventStreamService: streams events to registered subscribers
service EventStreamService {
// Subscribe to events matching correlation ID (required)
// Returns INVALID_ARGUMENT if correlation_id is empty
rpc Subscribe (EventStreamFilter) returns (stream EventBook);
}

Events are correlated via correlation_id on Cover, allowing clients to track causally-related events across aggregate boundaries.


Pluggable Infrastructure

⍼ Angzarr abstracts storage and messaging behind adapter interfaces.

Event Store Backends

BackendStatusUse Case
SQLiteTestedLocal development, standalone mode
PostgreSQLTestedProduction
RedisTestedHigh-throughput scenarios
BigtableTestedGCP deployments, petabyte scale
DynamoDBTestedAWS deployments, serverless
immudbImplementedImmutable audit requirements

Message Bus Backends

⍼ Angzarr uses publish/subscribe messaging to distribute events to sagas and projectors.

BackendStatusUse Case
Channel (in-process)TestedStandalone mode
RabbitMQ/AMQPTestedProduction
KafkaImplementedHigh-throughput streaming
GCP Pub/SubImplementedGCP deployments
AWS SNS/SQSImplementedAWS deployments

Configuration is declarative:

# Production
storage:
type: postgres
connection_string: ${DATABASE_URL}

bus:
type: amqp
url: ${RABBITMQ_URL}

# Local development
storage:
type: sqlite
path: ./data/events.db

bus:
type: channel

Observability

Every command, saga, and projector execution is traced and metered at the coordinator level—your code requires zero observability boilerplate.

PipelineTraced SpansMetrics
Aggregateaggregate.handle, aggregate.executeangzarr.command.duration
Sagasaga.orchestrate, orchestration.executeangzarr.saga.duration
Process Managerpm.orchestrateangzarr.pm.duration
Projectorprojector.handleangzarr.projector.duration

Every span carries the correlation_id, so distributed traces follow commands through aggregate execution, saga fan-out, and downstream projections without manual context propagation.

When built with --features otel, the sidecar exports traces, metrics, and logs via OTLP to any compatible backend (Grafana, Datadog, AWS X-Ray, GCP Cloud Trace).

See Observability for full details.


Further Reading

For visual explanations of event-driven architecture concepts (events, messaging patterns, event sourcing, choreography vs orchestration, schema management):


Next Steps