Clients
The Angzarr SDK provides several client types for different use cases. All clients support both TCP endpoints and Unix Domain Sockets.
DomainClient
The recommended entry point for most applications. Combines QueryClient and AggregateClient into a single unified interface.
Benefits:
- Single connection — one endpoint, one channel, reduced resource usage
- Unified API — both queries and commands through one object
- Builder access — fluent builders attached to the client instance
- Simpler DI — inject one client instead of two
- Rust
- Go
- Python
- Java
- C#
- C++
use angzarr_client::DomainClient;
// Connect to coordinator
let client = DomainClient::connect("http://localhost:1310").await?;
// Send a command
let response = client.aggregate.handle(command).await?;
// Query events
let events = client.query.get_events(query).await?;
import angzarr "github.com/benjaminabbitt/angzarr/client/go"
// Connect to coordinator
client, err := angzarr.NewDomainClient("localhost:1310")
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Send a command
response, err := client.Aggregate.Handle(ctx, command)
// Query events
events, err := client.Query.GetEventBook(ctx, query)
from angzarr_client import DomainClient
# Connect to coordinator
client = DomainClient.connect("localhost:1310")
# Send a command
response = client.aggregate.handle(command)
# Query events
events = client.query.get_event_book(query)
client.close()
import dev.angzarr.client.DomainClient;
// Connect to coordinator
try (DomainClient client = DomainClient.connect("localhost:1310")) {
// Send a command
CommandResponse response = client.execute(command);
// Query events
EventBook events = client.getQuery().getEventBook(query);
}
using Angzarr.Client;
// Connect to coordinator
using var client = DomainClient.Connect("http://localhost:1310");
// Send a command
var response = client.Execute(command);
// Query events
var events = client.Query.GetEventBook(query);
#include <angzarr/client.hpp>
// Connect to coordinator
auto client = angzarr::DomainClient::connect("localhost:1310");
// Send a command
auto response = client->aggregate()->handle(command);
// Query events
auto events = client->query()->get_event_book(query);
QueryClient
Provides read access to aggregate event streams. Use for:
- State reconstruction: Fetch events to rebuild aggregate state locally
- Audit trails: Read complete history for debugging and compliance
- Projections: Feed events to read-model projectors
- Testing: Verify events were persisted correctly after commands
- Rust
- Go
- Python
- Java
- C#
- C++
use angzarr_client::QueryClient;
let client = QueryClient::connect("http://localhost:1310").await?;
// Query events for an aggregate
let events = client.get_events(query).await?;
for page in events.pages {
println!("Event {}: {}", page.sequence, page.event.type_url);
}
client, err := angzarr.NewQueryClient("localhost:1310")
if err != nil {
log.Fatal(err)
}
defer client.Close()
events, err := client.GetEventBook(ctx, query)
for _, page := range events.Pages {
log.Printf("Event %d: %s", page.Sequence, page.Event.TypeUrl)
}
from angzarr_client import QueryClient
client = QueryClient.connect("localhost:1310")
events = client.get_event_book(query)
for page in events.pages:
print(f"Event {page.sequence}: {page.event.type_url}")
client.close()
QueryClient client = QueryClient.connect("localhost:1310");
EventBook events = client.getEventBook(query);
for (EventPage page : events.getPagesList()) {
System.out.printf("Event %d: %s%n", page.getSequence(), page.getEvent().getTypeUrl());
}
client.close();
using var client = QueryClient.Connect("http://localhost:1310");
var events = client.GetEventBook(query);
foreach (var page in events.Pages)
{
Console.WriteLine($"Event {page.Sequence}: {page.Event.TypeUrl}");
}
auto client = angzarr::QueryClient::connect("localhost:1310");
auto events = client->get_event_book(query);
for (const auto& page : events.pages()) {
std::cout << "Event " << page.sequence() << ": "
<< page.event().type_url() << std::endl;
}
AggregateClient
Sends commands to aggregates through the coordinator. Supports multiple execution modes:
| Mode | Method | Description |
|---|---|---|
| Async | handle() | Fire-and-forget, returns immediately after acceptance |
| Sync | handle_sync() | Waits for persistence, returns resulting events |
| Speculative | handle_sync_speculative() | What-if execution without persistence |
- Rust
- Go
- Python
- Java
- C#
- C++
use angzarr_client::AggregateClient;
let client = AggregateClient::connect("http://localhost:1310").await?;
// Async execution (fire-and-forget)
let response = client.handle(command).await?;
// Sync execution (wait for persistence)
let response = client.handle_sync(sync_command).await?;
client, err := angzarr.NewAggregateClient("localhost:1310")
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Async execution
response, err := client.Handle(ctx, command)
// Sync execution
response, err := client.HandleSync(ctx, syncCommand)
from angzarr_client import AggregateClient
client = AggregateClient.connect("localhost:1310")
# Async execution
response = client.handle(command)
# Sync execution
response = client.handle_sync(sync_command)
client.close()
AggregateClient client = AggregateClient.connect("localhost:1310");
// Async execution
CommandResponse response = client.handle(command);
// Sync execution
CommandResponse response = client.handleSync(syncCommand);
client.close();
using var client = AggregateClient.Connect("http://localhost:1310");
// Async execution
var response = client.Handle(command);
// Sync execution
var response = client.HandleSync(syncCommand);
auto client = angzarr::AggregateClient::connect("localhost:1310");
// Async execution
auto response = client->handle(command);
// Sync execution
auto response = client->handle_sync(sync_command);
Environment Variables
All clients support connecting via environment variables for deployment flexibility:
- Rust
- Go
- Python
- Java
- C#
- C++
// Connect using env var with fallback
let client = DomainClient::from_env("ANGZARR_ENDPOINT", "http://localhost:1310").await?;
// Connect using env var with fallback
client, err := angzarr.DomainClientFromEnv("ANGZARR_ENDPOINT", "localhost:1310")
# Connect using env var with fallback
client = DomainClient.from_env("ANGZARR_ENDPOINT", "localhost:1310")
// Connect using env var with fallback
DomainClient client = DomainClient.fromEnv("ANGZARR_ENDPOINT", "localhost:1310");
// Connect using env var with fallback
var client = DomainClient.FromEnv("ANGZARR_ENDPOINT", "http://localhost:1310");
// Connect using env var with fallback
auto client = angzarr::DomainClient::from_env("ANGZARR_ENDPOINT", "localhost:1310");
Unix Domain Sockets
All clients support Unix Domain Sockets for local communication with reduced overhead:
- Rust
- Go
- Python
- Java
- C#
- C++
// Absolute path
let client = DomainClient::connect("/var/run/angzarr.sock").await?;
// Relative path
let client = DomainClient::connect("./angzarr.sock").await?;
// URI format
let client = DomainClient::connect("unix:///var/run/angzarr.sock").await?;
// Absolute path - auto-detected
client, err := angzarr.NewDomainClient("/var/run/angzarr.sock")
// Relative path
client, err := angzarr.NewDomainClient("./angzarr.sock")
// URI format
client, err := angzarr.NewDomainClient("unix:///var/run/angzarr.sock")
# Absolute path - auto-detected
client = DomainClient.connect("/var/run/angzarr.sock")
# Relative path
client = DomainClient.connect("./angzarr.sock")
# URI format
client = DomainClient.connect("unix:///var/run/angzarr.sock")
// UDS paths are prefixed with unix://
DomainClient client = DomainClient.connect("unix:///var/run/angzarr.sock");
// UDS requires http:// prefix with host for .NET gRPC
// Configure channel options for UDS transport
// UDS paths are prefixed with unix://
auto client = angzarr::DomainClient::connect("unix:///var/run/angzarr.sock");
Next Steps
- Builders — Fluent API for constructing commands and queries
- Error Handling — Error types and introspection methods
- Speculative Execution — What-if scenarios without persistence