Skip to content

chronicles-net/chronicles

Chronicles

Event sourcing and CQRS for .NET, backed by Azure Cosmos DB. Chronicles gives you append-only event streams, command handlers, state projections, and document projections — production-ready infrastructure you'd otherwise spend months building yourself.

CI NuGet Branch Coverage Line Coverage Method Coverage

Install

dotnet add package Chronicles

Quick start

// Define your events
record OrderPlaced(string OrderId, string CustomerId, decimal Total);
record OrderShipped(string OrderId, DateTimeOffset ShippedAt);

// Write events to a stream
var streamId = new StreamId("order", orderId);

await writer.WriteAsync(streamId, [
    new OrderPlaced(orderId, customerId, 49.99m),
]);

// Later: append more events
await writer.WriteAsync(streamId, [
    new OrderShipped(orderId, DateTimeOffset.UtcNow),
]);

// Read events back
await foreach (var evt in reader.ReadAsync(streamId))
{
    Console.WriteLine($"{evt.EventType} at {evt.Metadata.Timestamp}");
}

Three pillars

Event Store

Append-only, versioned event streams persisted to Cosmos DB with optimistic concurrency.

Type Role
StreamId Identifies a stream by category + id (e.g. "order.42")
IEventStreamWriter Appends events, closes streams, manages checkpoints
IEventStreamReader Reads events, queries streams, retrieves checkpoints

CQRS

Command handlers validate business rules, emit events, and rebuild state from event history.

Type Role
ICommandHandler<TCommand, TState> Handles a command with full state projection
IStatelessCommandHandler<TCommand> Handles a command without replaying all events
IStateProjection<TState> Folds a stream of events into aggregate state
// Implement a stateful command handler
public class ShipOrderHandler : ICommandHandler<ShipOrder, OrderState>
{
    public OrderState CreateState(StreamId streamId) => new();

    public OrderState? ConsumeEvent(StreamEvent evt, OrderState state) =>
        evt.Data switch
        {
            OrderPlaced p => state with { CustomerId = p.CustomerId, Total = p.Total },
            _ => null,
        };

    public async ValueTask ExecuteAsync(
        ICommandContext<ShipOrder> context,
        OrderState state,
        CancellationToken cancellationToken)
    {
        if (state.CustomerId is null)
            throw new InvalidOperationException("Order does not exist.");

        context.AddEvent(new OrderShipped(context.Command.OrderId, DateTimeOffset.UtcNow));
    }
}

Document Store

Read-model projections driven by the Cosmos DB change feed.

Type Role
IDocumentProjection<TDocument> Projects events into a Cosmos read-model document
IDocumentWriter<T> Creates, updates, replaces, and deletes documents
IDocumentReader<T> Queries documents from Cosmos

Sample

The sample/ directory contains a food-delivery microservices demo built with .NET Aspire. It covers three bounded contexts — Orders, Restaurants, and Couriers — and shows how to wire up event streams, command handlers, and document projections end-to-end.

Documentation

Guide Description
Getting Started Install Chronicles, configure DI, and write your first events
Event Store Streams, versioning, optimistic concurrency, and checkpoints
Command Handlers Stateless, stateful, and selective-state handler patterns
Projections State projections, document projections, and commit actions
Document Store Reading and writing Cosmos documents with IDocumentReader / IDocumentWriter
Event Subscriptions Change-feed subscriptions and event processors
Dependency Injection Full DI reference: AddChronicles, event store, CQRS, subscriptions
Testing AddFakeChronicles, in-memory fakes, and xUnit test patterns

Contributing & community

About

Chronicles is a comprehensive document and event store C# library specifically designed for seamless integration with Cosmos DB

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages