Introduction to Angzarr
import { Tabs, TabItem } from ‘@astrojs/starlight/components’;
⍼ Angzarr is a polyglot framework for building event-sourced systems. You write business logic in any language with gRPC support—the framework handles event persistence, saga coordination, projection management, and all the infrastructure complexity that typically derails CQRS/ES projects.
The symbol ⍼ (U+237C, “angzarr”) has existed in Unicode since 2002 without a defined purpose. The right angle represents the origin point—your event store. The zigzag arrow represents events cascading through your system. We gave it meaning.
What Angzarr Provides
Section titled “What Angzarr Provides”Angzarr inverts the typical framework relationship. Rather than providing libraries that applications import, Angzarr provides infrastructure that applications connect to via gRPC.
Your data model lives in .proto files, not code. Commands, events, and state are defined as Protocol Buffer messages—language-neutral, versionable, and shared across all implementations. This is what enables true polyglot support: the same event stream can be produced by a Rust aggregate and consumed by a Python projector.
| You Define | You Implement | We Handle |
|---|---|---|
Commands in .proto | Aggregate handlers | Event persistence |
Events in .proto | Projector handlers | Optimistic concurrency |
State in .proto | Saga handlers | Snapshot management |
| Event upcasting | ||
| Event distribution | ||
| Saga coordination | ||
| Schema evolution |
Your business logic receives commands with full event history and emits events. No database connections. No message bus configuration. No retry logic. Pure domain logic.
Architecture Preview
Section titled “Architecture Preview”⍼ Angzarr stores aggregate history as an EventBook—the complete event stream for a single aggregate root: its identity (the Cover), an optional Snapshot for efficient replay, and ordered EventPages representing domain events.
flowchart LR
subgraph Client
GW[Gateway]
end
subgraph AggPod[Domain A - Aggregate]
AGG_COORD[⍼ Sidecar]
AGG[Your Aggregate]
UPC[Your Upcaster]
AGG_COORD <--> AGG
AGG_COORD <--> UPC
end
subgraph Infra[Infrastructure]
ES[(Event Store)]
BUS[Message Bus]
end
subgraph Consumers[Event Consumers]
subgraph SagaPod[Saga Pod]
SAGA_COORD[⍼ Sidecar]
SAGA[Your Saga]
SAGA_COORD <--> SAGA
end
subgraph PrjPod[Projector Pod]
PRJ_COORD[⍼ Sidecar]
PRJ[Your Projector]
PRJ_DB[(Read Model)]
PRJ_COORD <--> PRJ
PRJ --> PRJ_DB
end
end
subgraph AggPod2[Domain B - Aggregate]
AGG_COORD2[⍼ Sidecar]
AGG2[Another Aggregate]
AGG_COORD2 <--> AGG2
end
GW -->|cmd| AGG_COORD
AGG_COORD <--> ES
AGG_COORD --> BUS
BUS --> SAGA_COORD
BUS --> PRJ_COORD
SAGA_COORD -->|cmd| AGG_COORD2
AGG_COORD2 <--> ES
style AggPod2 stroke-dasharray: 5 5
style AGG_COORD2 stroke-dasharray: 5 5
style AGG2 stroke-dasharray: 5 5
The dashed Domain B represents any additional domain(s)—sagas bridge events from one domain to commands in another. Real systems have multiple domains, each with its own aggregate.
Each component type runs in its own pod with an ⍼ Angzarr sidecar. Your code handles business logic; the sidecar handles persistence, messaging, and coordination.
Language Support
Section titled “Language Support”Any language with gRPC support works. Your business logic communicates with ⍼ Angzarr coordinators via gRPC—the framework doesn’t care what’s behind the endpoint. If your language appears on the gRPC supported languages matrix, you can use it with Angzarr. This includes C#, C++, Dart, Go, Java, Kotlin, Node.js, Objective-C, PHP, Python, Ruby, Rust, and more.
Client libraries are optional and minimal. For six languages (the top TIOBE languages), we provide thin client libraries that reduce boilerplate—protobuf packing/unpacking, state reconstruction, router registration. These libraries are intentionally kept lightweight; the real contract is just gRPC + protobuf. You can always work directly with the proto bindings if you prefer:
| Language | Client Library | Example |
|---|---|---|
| Python | angzarr-client | examples/python/ |
| Go | github.com/benjaminabbitt/angzarr/client | examples/go/ |
| Rust | angzarr-client | examples/rust/ |
| Java | dev.angzarr:client | examples/java/ |
| C# | Angzarr.Client | examples/csharp/ |
| C++ | header-only | examples/cpp/ |
All six implementations share the same Gherkin specifications, ensuring identical behavior across languages.
Quick Example
Section titled “Quick Example”Every aggregate — player, table, hand, tournament, etc. — is a class derived from the client library’s CommandHandler<State>. @handles(CommandType) methods produce events; @applies(EventType) methods reduce events into state. The same pattern in all six languages.
@handles(table_proto.CreateTable)def handle_create_table( self, cmd: table_proto.CreateTable, state: _TableState | None = None, seq: int | None = None,) -> table_proto.TableCreated: """Create a new table.""" router_mode = state is not None saved = self._router_bind(state) if router_mode else None try: if self.exists: raise CommandRejectedError("Table already exists") if not cmd.table_name: raise CommandRejectedError("table_name is required") if cmd.small_blind <= 0: raise CommandRejectedError.invalid_argument( "small_blind must be positive" ) if cmd.big_blind <= 0 or cmd.big_blind < cmd.small_blind: raise CommandRejectedError("big_blind must be >= small_blind") if cmd.min_buy_in <= 0: raise CommandRejectedError.invalid_argument( "min_buy_in must be positive" ) if cmd.max_buy_in < cmd.min_buy_in: raise CommandRejectedError("max_buy_in must be >= min_buy_in") if cmd.max_players < 2 or cmd.max_players > 10: raise CommandRejectedError("max_players must be 2-10")
event = table_proto.TableCreated( table_name=cmd.table_name, game_variant=cmd.game_variant, small_blind=cmd.small_blind, big_blind=cmd.big_blind, min_buy_in=cmd.min_buy_in, max_buy_in=cmd.max_buy_in, max_players=cmd.max_players, action_timeout_seconds=cmd.action_timeout_seconds or 30, created_at=now(), ) if not router_mode: self._emit(event) return event finally: if router_mode: self._state = savedHandler method name mirrors the proto command type in the language’s idiomatic casing: handle_create_table, HandleCreateTable, CreateTableHandler.
No database code. No message bus code. Just business logic.
For Decision Makers
Section titled “For Decision Makers”If you’re evaluating Angzarr for your organization:
- Technical Pitch — Complete architectural pitch with detailed rationale
- Architecture — Core concepts: data model, coordinators, sync modes
- Why Poker — Why our example domain exercises every pattern
For Developers
Section titled “For Developers”Ready to build:
- Getting Started — Prerequisites, installation, first aggregate
- Components — Aggregates, sagas, projectors, process managers
- Examples — Code samples in all six languages
Next Steps
Section titled “Next Steps”- Understand the patterns — CQRS & Event Sourcing Explained
- See the architecture — Architecture
- Get hands-on — Getting Started