Skip to content

Get started

The fastest way to understand Chronicle is to watch one fact travel through it. So that’s what we’ll do here: install a template, scaffold a tiny app, and run it. In a couple of minutes you’ll have appended an event and seen it ripple out into both a read model and a live reaction — the loop that every Chronicle application is built on.

We won’t write much code yet — the template gives us a working example to read. Once it’s running and the shape makes sense, the tutorial picks up the thread and has you build a real domain from scratch.

  • The .NET SDK — .NET 8 or newer.
  • Docker with Compose. Chronicle runs as a small kernel next to your app, and the template wires it up with a docker-compose.yml so you don’t have to install anything else.
  1. Install the Cratis templates. This adds a family of dotnet new starters — a console one for learning the core, and a full-stack web one with a React frontend.

    Install the templates
    dotnet new install Cratis.Templates
  2. Scaffold a project. Start with the console template — it’s the smallest thing that exercises the entire event-sourcing loop, and it’s the one the tutorial builds on.

    Scaffold the console starter
    dotnet new cratis-chronicle-console -o Library
    cd Library
  3. Start Chronicle. The template ships a docker-compose.yml for the Chronicle kernel and its storage. Bring it up in the background:

    Start the Chronicle kernel
    docker compose up -d
  4. Run the app.

    Run it
    dotnet run

    The scaffolded program appends one event and reacts to it. You’ll see the reaction print, then the app waits for a keypress:

    Output
    Received event with message: Hello world!

That one line of output is the whole point — let’s unpack how it got there.

That one line came out of a complete Chronicle loop. Here it is as an event model — a fact is appended, a projection folds it into a read model, and a reactor acts on it:

UI/A: DemoC/RM: DemoStream: Demo
TestEvent



message: string
TestProjection
TestReactor

The template’s Program.cs is about twenty lines, and it wires up exactly those three blocks. Here’s the heart of it:

Program.cs
using var client = new ChronicleClient();
var eventStore = await client.GetEventStore("ChronicleConsole");
await eventStore.EventLog.Append("some-event-source", new TestEvent("Hello world!"));

Reading top to bottom: a ChronicleClient connects to the kernel you just started with Docker, you ask it for an event store by name, and you append a TestEvent to its event log. That Append is the only thing that changes anything — everything below reacts to it.

The event itself is just a record marked as a fact:

The event — an immutable fact
[EventType]
public record TestEvent(string Message);

And two artifacts sit waiting for that fact to happen. A projection folds the event into a read model — state you can query — declaratively, with no update code:

The projection — builds queryable state
[FromEvent<TestEvent>]
public record TestProjection(
string Message,
[SetFromContext<TestEvent>(nameof(EventContext.EventSourceId))] string EventSource);

A reactor does something when the event arrives — here, it writes the line you saw:

The reactor — does something when it happens
public class TestReactor : IReactor
{
public Task React(TestEvent @event)
{
Console.WriteLine($"Received event with message: {@event.Message}");
return Task.CompletedTask;
}
}

Notice what you didn’t write: no registration, no wiring, no “on startup, subscribe this handler to that event”. Chronicle discovers the event, projection, and reactor by convention — the [EventType] attribute, the IReactor marker, the [FromEvent<>] attribute — and routes the appended fact to each. That convention-over-configuration discovery is what keeps a growing Chronicle app from drowning in plumbing.

The Docker image includes the Chronicle workbench — a web UI for poking at your event store. Open http://localhost:8080, choose the ChronicleConsole event store, and look at Sequences: there’s your TestEvent, sitting at sequence number 0, permanent and in order. Append more and watch the log grow. This is the source of truth your projection and reactor were both reading from.