Skip to content

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.


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 DefineYou ImplementWe Handle
Commands in .protoAggregate handlersEvent persistence
Events in .protoProjector handlersOptimistic concurrency
State in .protoSaga handlersSnapshot 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.


⍼ 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.


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:

LanguageClient LibraryExample
Pythonangzarr-clientexamples/python/
Gogithub.com/benjaminabbitt/angzarr/clientexamples/go/
Rustangzarr-clientexamples/rust/
Javadev.angzarr:clientexamples/java/
C#Angzarr.Clientexamples/csharp/
C++header-onlyexamples/cpp/

All six implementations share the same Gherkin specifications, ensuring identical behavior across languages.


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 = saved

Handler 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.


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

Ready to build:

  • Getting Started — Prerequisites, installation, first aggregate
  • Components — Aggregates, sagas, projectors, process managers
  • Examples — Code samples in all six languages

  1. Understand the patternsCQRS & Event Sourcing Explained
  2. See the architectureArchitecture
  3. Get hands-onGetting Started