Skip to main content

Introduction

⍼ Angzarr is a polyglot CQRS/Event Sourcing framework. You write business logic in your preferred language—Python, Go, Rust, Java, C#, or C++—while 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.


The Problem

CQRS and Event Sourcing deliver real architectural benefits: complete audit history, temporal queries, independent read/write scaling, and natural alignment with domain-driven design. The implementation cost, however, remains steep.

⍼ Angzarr's original inspiration was airline flight network repair after disruption—when weather or mechanical issues cascade through a schedule, operations teams need to see exactly what happened, why decisions were made, and how to unwind partial recoveries. That domain's requirements (audit, state machines, cross-domain coordination, temporal queries) recur across industries: billing systems, insurance claims, logistics. In the author's experience, roughly one-third of enterprise projects exhibit these patterns. Yet most teams can't justify the infrastructure investment.

Teams attempting CQRS/ES consistently face:

  • Infrastructure gravity: Event stores, message buses, projection databases, and their failure modes dominate early development. Business logic becomes entangled with persistence concerns.
  • Schema management: Events are append-only and permanent. Schema evolution requires discipline that frameworks rarely enforce.
  • Operational complexity: Snapshotting, projection rebuilds, idempotency, exactly-once delivery, and saga coordination demand specialized knowledge.
  • Language lock-in: Most frameworks assume a single ecosystem. Organizations with mixed stacks maintain parallel implementations or force standardization.

When ⍼ Angzarr Fits

⍼ Angzarr is not a do-everything framework. Your domain must fit these constraints:

RequirementWhy It Matters
Eventual consistency acceptableEvents propagate asynchronously. If you need synchronous, strongly-consistent responses, traditional CRUD is simpler.
Audit/validation importantEvent sourcing's overhead only pays off when you need to answer "what happened and why?"
State reconstructable from eventsIf your domain has external dependencies that can't be replayed, event sourcing won't help.

Probably NOT a Good Fit

  • Real-time games: Latency-sensitive gameplay where milliseconds matter. Event sourcing adds overhead without proportional benefit.
  • Simple CRUD: If your domain is "store this, retrieve that" without complex state transitions, use a database directly.
  • Strong consistency required: Banking ledgers that must never show intermediate states. (Though event sourcing can work here with careful design.)

The Poker Paradox

Yes, the example domain is a game. Poker works as an example because it exercises every pattern—but most games shouldn't use event sourcing in production. The author is developing a board game with ⍼ Angzarr, but primarily because event logs make game flow understandable during development, not because it's the optimal production architecture for games.


The ⍼ Angzarr Approach

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


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.

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.


For Decision Makers

If you're evaluating Angzarr for your organization:

  • PITCH.md — Complete architectural pitch (standalone document)
  • Architecture — Core concepts: data model, coordinators, sync modes
  • Why Poker — Why our example domain exercises every pattern

For Developers

Ready to build:

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

Language Support

Any language with gRPC support works. Your business logic communicates with ⍼ Angzarr coordinators via gRPC—if your language can generate code from .proto files and make gRPC calls, you can use it. The framework doesn't care what's behind the gRPC endpoint.

Client libraries simplify integration. For six languages, we provide client libraries that handle protobuf packing/unpacking, state reconstruction, router registration, and other boilerplate:

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.


Quick Example

The same handler across all six languages. Each follows the guard → validate → compute pattern:

# examples/python/player/agg/handlers/player.py
@handles(player_proto.DepositFunds)
def deposit(self, cmd: player_proto.DepositFunds) -> player_proto.FundsDeposited:
# Guard
if not self.exists:
raise CommandRejectedError("Player does not exist")

# Validate
amount = cmd.amount.amount if cmd.amount else 0
if amount <= 0:
raise CommandRejectedError("amount must be positive")

# Compute
new_balance = self.bankroll + amount
return player_proto.FundsDeposited(
amount=cmd.amount,
new_balance=poker_types.Currency(amount=new_balance, currency_code="CHIPS"),
deposited_at=now(),
)

View full source →

No database code. No message bus code. Just business logic.


Next Steps

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