Table of Contents

Quickstart Worker Service

Pre-requisites

Objective

In this quickstart, you will set up Chronicle in a .NET worker service (generic host) application — without ASP.NET Core. Worker services are ideal for background processing: reacting to events, running scheduled jobs, or maintaining derived data in message-processing pipelines.

Docker

Chronicle is available as a Docker Image. For local development, we recommend using the development images. The latest-development tag will get you the most recent version.

The development image includes a MongoDB server, so you don't need any additional setup.

To run the server as a daemon, execute the following command in your terminal:

docker run -d -p 27017:27017 -p 8080:8080 -p 35000:35000 cratis/chronicle:latest-development

If you prefer to have a Docker Compose file, we recommend the following setup with Aspire to give you open telemetry data:

services:
  chronicle:
    image: cratis/chronicle:latest-development
    environment:
      - OTEL_EXPORTER_OTLP_ENDPOINT=http://aspire-dashboard:18889
    ports:
      - 27017:27017
      - 8080:8080
      - 11111:11111
      - 30000:30000
      - 35000:35000

  aspire-dashboard:
    image: mcr.microsoft.com/dotnet/aspire-dashboard:latest
    environment:
      - DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true
      - DOTNET_DASHBOARD_OTLP_ENDPOINT_URL=http://chronicle:18889
      - ALLOW_UNSECURED_TRANSPORT=true
      - DOTNET_ENVIRONMENT=Development
    ports:
      - 18888:18888
      - 4317:18889

Snippet source

Setup project

Start by creating a folder for your project and then create a .NET worker service project inside this folder:

dotnet new worker

Add a reference to the Chronicle client package:

dotnet add package Cratis.Chronicle

Note: For worker services you only need the base Cratis.Chronicle package — the Cratis.Chronicle.AspNetCore package is for web applications only.

Host setup

Open your Program.cs and configure Chronicle using AddCratisChronicle on the IHostApplicationBuilder:

var builder = Host.CreateApplicationBuilder(args);

builder.AddCratisChronicle(options =>
{
    options.EventStore = "MyWorkerApp";
});

builder.Services.AddHostedService<Worker>();

var host = builder.Build();
await host.RunAsync();

The AddCratisChronicle call:

  • Registers IChronicleClient, IEventStore, and all the event store components (IEventLog, IReactors, IReducers, IProjections, IReadModels) in the DI container.
  • Automatically discovers and registers all artifacts (Reactors, Reducers, Projections) from the loaded assemblies.
  • Reads configuration from the Cratis:Chronicle section of appsettings.json (connection string, timeouts, etc.).

Configuration

Chronicle reads its connection settings from appsettings.json. Add the following to yours:

{
  "Cratis": {
    "Chronicle": {
      "ConnectionString": "chronicle://localhost:35000",
      "EventStore": "MyWorkerApp"
    }
  }
}

You can also configure the event store name inline (as shown above) and keep the connection string in configuration.

Worker implementation

Inject IEventStore or any of the event store sub-services into your hosted service:

public class Worker(IEventStore eventStore) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // Connect to Chronicle and start processing
        await eventStore.Connection.Connect();

        // Keep running until the host shuts down
        await Task.Delay(Timeout.Infinite, stoppingToken);
    }
}

Structural dependencies

For custom identity providers, correlation ID accessors, or namespace resolvers, use the configure callback:

builder.AddCratisChronicle(
    configureOptions: options => options.EventStore = "MyWorkerApp",
    configure: b => b
        .WithIdentityProvider(new MyServiceIdentityProvider())
        .WithNamespaceResolver(new MyTenantResolver()));

See Structural Dependencies for a full list of configurable dependencies.

Namespace resolution

By default the worker uses the default namespace for all operations. To support multi-tenant scenarios, provide a custom IEventStoreNamespaceResolver via ChronicleClientOptions:

builder.AddCratisChronicle(options =>
{
    options.EventStore = "MyWorkerApp";
    options.EventStoreNamespaceResolverType = typeof(MyTenantNamespaceResolver);
});

Or pass a resolver instance directly through the builder:

builder.AddCratisChronicle(
    configureOptions: options => options.EventStore = "MyWorkerApp",
    configure: b => b.WithNamespaceResolver(new MyTenantNamespaceResolver(config)));

See Namespace resolution for details on built-in resolvers.

Services

Chronicle uses the DI container to create instances of Reactors, Reducers, and Projections it discovers. Register them as services in Program.cs:

builder.Services.AddTransient<MyReactor>();
builder.Services.AddTransient<MyReducer>();

For larger solutions, the Cratis Fundamentals convention-based registration helpers keep this manageable:

builder.Services
    .AddBindingsByConvention()
    .AddSelfBindings();