Skip to content

Transactions and Unit of Work

A unit of work groups event appends in the client and commits them as one append batch. Use it when one logical operation must produce multiple events and the operation should have one commit point, one correlation id, and one success or failure outcome.

The transaction is still an event append operation. Chronicle validates constraints and concurrency when the unit of work commits, then persists the accepted events atomically for each committed batch.

Use a unit of work when:

  • One command or workflow emits more than one event.
  • The events should share the same correlation id.
  • The operation should not send partial appends before the workflow is ready.
  • The appends should be committed or rejected together.

Do not use a unit of work to hide modeling problems. If two facts can happen independently, append them independently. If one fact depends on another, model that invariant with constraints, concurrency, or the command decision that creates the events.

Create a unit of work, append through the transactional event sequence, and commit when every event has been staged.

using Cratis.Chronicle;
using Cratis.Execution;
public static class TransactionalOrderWorkflow
{
public static async Task CommitOrder(IEventStore store)
{
var unitOfWork = store.UnitOfWorkManager.Begin(CorrelationId.New());
try
{
await store.EventLog.Transactional.Append(
"order-123",
new TransactionalOrderPlaced("order-123", 99.95m));
await store.EventLog.Transactional.Append(
"inventory-widget",
new TransactionalInventoryReserved("widget", 1));
await unitOfWork.Commit();
}
catch
{
await unitOfWork.Rollback();
throw;
}
}
}
public record TransactionalOrderPlaced(string OrderId, decimal TotalAmount);
public record TransactionalInventoryReserved(string Sku, int Quantity);

Rollback discards the events staged in the unit of work. Nothing is sent to Chronicle for those staged events.