Builders
Fluent builders simplify command and query construction. Instead of manually building protobuf messages, use method chaining for readable, type-safe construction.
CommandBuilder
Constructs commands with fluent method chaining. Handles correlation ID generation, sequence management, and protobuf serialization.
Basic Usage
- Rust
- Go
- Python
- Java
- C#
- C++
use angzarr_client::{CommandBuilder, DomainClient};
use uuid::Uuid;
let client = DomainClient::connect("http://localhost:1310").await?;
let root_id = Uuid::new_v4();
// Build and execute a command
let response = CommandBuilder::new(&client.aggregate, "orders", root_id)
.with_correlation_id("order-123")
.with_sequence(5)
.with_command("type.googleapis.com/examples.UpdateOrder", &update_cmd)?
.execute()
.await?;
client, _ := angzarr.NewAggregateClient("localhost:1310")
// Build and execute a command
response, err := client.Command("orders", rootID).
WithCorrelationID("order-123").
WithSequence(5).
WithCommand("type.googleapis.com/examples.UpdateOrder", updateCmd).
Execute(ctx)
from angzarr_client import DomainClient
client = DomainClient.connect("localhost:1310")
# Build and execute a command
response = client.command("orders", root_id) \
.with_correlation_id("order-123") \
.with_sequence(5) \
.with_command("type.googleapis.com/examples.UpdateOrder", update_cmd) \
.execute()
DomainClient client = DomainClient.connect("localhost:1310");
// Build and execute a command
CommandResponse response = client.command("orders", rootId)
.withCorrelationId("order-123")
.withSequence(5)
.withCommand("type.googleapis.com/examples.UpdateOrder", updateCmd)
.execute();
using var client = DomainClient.Connect("http://localhost:1310");
// Build and execute a command
var response = client.Command("orders", rootId)
.WithCorrelationId("order-123")
.WithSequence(5)
.WithCommand("type.googleapis.com/examples.UpdateOrder", updateCmd)
.Execute();
auto client = angzarr::DomainClient::connect("localhost:1310");
// Build and execute a command
auto response = angzarr::CommandBuilder::create(client->aggregate(), "orders", root_id)
.with_correlation_id("order-123")
.with_sequence(5)
.with_command("type.googleapis.com/examples.UpdateOrder", update_cmd)
.execute();
Creating New Aggregates
When creating a new aggregate, omit the root UUID. The coordinator generates one and returns it in the response:
- Rust
- Go
- Python
- Java
- C#
- C++
// Create a new aggregate
let response = CommandBuilder::create_new(&client.aggregate, "orders")
.with_correlation_id("order-123")
.with_command("type.googleapis.com/examples.CreateOrder", &create_cmd)?
.execute()
.await?;
// Extract the generated root ID
let new_root_id = angzarr_client::root_uuid(&response.events)?;
// Create a new aggregate
response, err := client.CommandNew("orders").
WithCorrelationID("order-123").
WithCommand("type.googleapis.com/examples.CreateOrder", createCmd).
Execute(ctx)
// Extract the generated root ID
newRootID := angzarr.RootUUID(response.Events)
# Create a new aggregate
response = client.command_new("orders") \
.with_correlation_id("order-123") \
.with_command("type.googleapis.com/examples.CreateOrder", create_cmd) \
.execute()
# Extract the generated root ID
new_root_id = helpers.root_uuid(response.events)
// Create a new aggregate
CommandResponse response = client.commandNew("orders")
.withCorrelationId("order-123")
.withCommand("type.googleapis.com/examples.CreateOrder", createCmd)
.execute();
// Extract the generated root ID
UUID newRootId = Helpers.rootUUID(response.getEvents());
// Create a new aggregate
var response = client.CommandNew("orders")
.WithCorrelationId("order-123")
.WithCommand("type.googleapis.com/examples.CreateOrder", createCmd)
.Execute();
// Extract the generated root ID
var newRootId = Helpers.RootGuid(response.Events);
// Create a new aggregate
auto response = angzarr::CommandBuilder::create_new(client->aggregate(), "orders")
.with_correlation_id("order-123")
.with_command("type.googleapis.com/examples.CreateOrder", create_cmd)
.execute();
// Extract the generated root ID
auto new_root_id = angzarr::root_uuid(response.events());
Auto-Generated Correlation IDs
If you don't specify a correlation ID, the builder generates a UUID automatically:
// These are equivalent:
.withCorrelationId(UUID.randomUUID().toString())
// vs just omitting it - builder generates one
QueryBuilder
Constructs event queries with support for temporal queries, sequence ranges, and filtering.
Basic Query
- Rust
- Go
- Python
- Java
- C#
- C++
use angzarr_client::{QueryBuilder, DomainClient};
let client = DomainClient::connect("http://localhost:1310").await?;
// Query all events for an aggregate
let events = QueryBuilder::new(&client.query, "orders", root_id)
.get_event_book()
.await?;
client, _ := angzarr.NewQueryClient("localhost:1310")
// Query all events for an aggregate
events, err := client.Query("orders", rootID).
GetEventBook(ctx)
from angzarr_client import DomainClient
client = DomainClient.connect("localhost:1310")
# Query all events for an aggregate
events = client.query("orders", root_id).get_event_book()
DomainClient client = DomainClient.connect("localhost:1310");
// Query all events for an aggregate
EventBook events = client.query("orders", rootId).getEventBook();
using var client = DomainClient.Connect("http://localhost:1310");
// Query all events for an aggregate
var events = client.QueryEvents("orders", rootId).GetEventBook();
auto client = angzarr::DomainClient::connect("localhost:1310");
// Build query manually (C++ uses proto directly)
angzarr::Query query;
query.mutable_cover()->set_domain("orders");
// Set root UUID...
auto events = client->query()->get_event_book(query);
Temporal Queries
Reconstruct state as it existed at a specific point in time:
- Rust
- Go
- Python
- Java
- C#
- C++
// As of a specific sequence number
let events = QueryBuilder::new(&client.query, "orders", root_id)
.as_of_sequence(10)
.get_event_book()
.await?;
// As of a specific timestamp (RFC3339)
let events = QueryBuilder::new(&client.query, "orders", root_id)
.as_of_time("2024-01-15T10:30:00Z")?
.get_event_book()
.await?;
// As of a specific sequence number
events, err := client.Query("orders", rootID).
AsOfSequence(10).
GetEventBook(ctx)
// As of a specific timestamp (RFC3339)
events, err := client.Query("orders", rootID).
AsOfTime("2024-01-15T10:30:00Z").
GetEventBook(ctx)
# As of a specific sequence number
events = client.query("orders", root_id) \
.as_of_sequence(10) \
.get_event_book()
# As of a specific timestamp (RFC3339)
events = client.query("orders", root_id) \
.as_of_time("2024-01-15T10:30:00Z") \
.get_event_book()
// As of a specific sequence number
EventBook events = client.query("orders", rootId)
.asOfSequence(10)
.getEventBook();
// As of a specific timestamp (RFC3339)
EventBook events = client.query("orders", rootId)
.asOfTime("2024-01-15T10:30:00Z")
.getEventBook();
// As of a specific sequence number
var events = client.QueryEvents("orders", rootId)
.AsOfSequence(10)
.GetEventBook();
// As of a specific timestamp (RFC3339)
var events = client.QueryEvents("orders", rootId)
.AsOfTime("2024-01-15T10:30:00Z")
.GetEventBook();
// Set temporal query in proto directly
query.mutable_temporal_query()->set_sequence(10);
// Or with timestamp
query.mutable_temporal_query()->mutable_timestamp()->set_seconds(timestamp_seconds);
Sequence Ranges
Fetch a subset of events for pagination or incremental sync:
- Rust
- Go
- Python
- Java
- C#
- C++
// Events from sequence 5 onwards
let events = QueryBuilder::new(&client.query, "orders", root_id)
.range(5)
.get_event_book()
.await?;
// Events in range [5, 15]
let events = QueryBuilder::new(&client.query, "orders", root_id)
.range_to(5, 15)
.get_event_book()
.await?;
// Events from sequence 5 onwards
events, err := client.Query("orders", rootID).
Range(5).
GetEventBook(ctx)
// Events in range [5, 15]
events, err := client.Query("orders", rootID).
RangeTo(5, 15).
GetEventBook(ctx)
# Events from sequence 5 onwards
events = client.query("orders", root_id) \
.range(5) \
.get_event_book()
# Events in range [5, 15]
events = client.query("orders", root_id) \
.range_to(5, 15) \
.get_event_book()
// Events from sequence 5 onwards
EventBook events = client.query("orders", rootId)
.range(5)
.getEventBook();
// Events in range [5, 15]
EventBook events = client.query("orders", rootId)
.rangeTo(5, 15)
.getEventBook();
// Events from sequence 5 onwards
var events = client.QueryEvents("orders", rootId)
.Range(5)
.GetEventBook();
// Events in range [5, 15]
var events = client.QueryEvents("orders", rootId)
.RangeTo(5, 15)
.GetEventBook();
// Set sequence range in proto directly
query.mutable_sequence_range()->set_lower(5);
query.mutable_sequence_range()->set_upper(15);
Query by Correlation ID
Fetch events across aggregates that share a correlation ID (workflow tracking):
- Rust
- Go
- Python
- Java
- C#
- C++
// Query by correlation ID
let events = QueryBuilder::by_correlation_id(&client.query, "orders", "corr-456")
.get_event_book()
.await?;
// Query by correlation ID
events, err := client.QueryDomain("orders").
ByCorrelationID("corr-456").
GetEventBook(ctx)
# Query by correlation ID
events = client.query_domain("orders") \
.by_correlation_id("corr-456") \
.get_event_book()
// Query by correlation ID
EventBook events = client.queryDomain("orders")
.byCorrelationId("corr-456")
.getEventBook();
// Query by correlation ID
var events = client.QueryDomain("orders")
.ByCorrelationId("corr-456")
.GetEventBook();
// Set correlation ID in proto directly
query.mutable_cover()->set_correlation_id("corr-456");
Builder Methods Reference
CommandBuilder
| Method | Description |
|---|---|
with_correlation_id(id) | Set correlation ID for distributed tracing |
with_sequence(seq) | Set expected sequence for optimistic locking |
with_command(type_url, msg) | Set the command payload |
build() | Build the CommandBook without executing |
execute() | Build and execute the command |
QueryBuilder
| Method | Description |
|---|---|
by_correlation_id(id) | Query by correlation ID across aggregates |
with_edition(edition) | Filter by schema edition |
range(lower) | Events from sequence lower onwards |
range_to(lower, upper) | Events in sequence range |
as_of_sequence(seq) | Temporal query at sequence |
as_of_time(rfc3339) | Temporal query at timestamp |
build() | Build the Query without executing |
get_event_book() | Execute and return EventBook |
get_pages() | Execute and return just the pages |
Next Steps
- Error Handling — Error types and introspection methods
- Speculative Execution — What-if scenarios without persistence