Quickstart Worker Service
Pre-requisites
- .NET 8 or higher
- Docker Desktop or compatible
- MongoDB client (e.g. MongoDB Compass)
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
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.Chroniclepackage — theCratis.Chronicle.AspNetCorepackage 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:Chroniclesection ofappsettings.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();