Table of Contents

Sinks

A sink is the storage backend where Chronicle writes read model instances produced by projections and reducers. Chronicle ships with two built-in sink types.

MongoDB Sink

The default sink. Read model instances are stored as MongoDB documents, one document per instance. The container name defaults to the read model type name (camelCase), controlled by the INamingPolicy in use.

This requires no additional setup — MongoDB is the default when you configure Chronicle.

SQL Sink

The SQL sink treats the database as a document store. Each read model type gets its own table with two columns:

Column Type Description
Id nvarchar / varchar The read model key
Document nvarchar(max) / text The serialized JSON of the instance

Tables are created automatically on first use via IReadModelMigrator. You do not need to write migrations by hand.

This allows teams using SQL databases (SQL Server, PostgreSQL, SQLite) to take advantage of Chronicle projections without maintaining a MongoDB instance.

Enabling the SQL Sink

Set DefaultSinkTypeId in ChronicleOptions:

builder.AddCratisChronicle(configureOptions: options =>
{
    options.DefaultSinkTypeId = WellKnownSinkTypes.SQL;
});

Or in appsettings.json:

{
  "Cratis": {
    "Chronicle": {
      "DefaultSinkTypeId": "f7d3a1e2-4b5c-4d6e-8f9a-0b1c2d3e4f5a"
    }
  }
}

Prerequisites

The SQL sink is provided by the Cratis.Chronicle.Storage.Sql NuGet package. Add it to your Kernel host project and register it in the IChronicleBuilder:

builder.AddCratisChronicleKernel(chronicle =>
{
    chronicle.WithSqlStorage(connectionString);
});

Refer to the hosting documentation for full server setup details.

Choosing a Sink

MongoDB SQL
Setup complexity Requires MongoDB Requires SQL database
Schema management Schemaless Tables auto-created on first use
Query flexibility Rich aggregation pipeline Standard SQL queries
Best for Event-driven microservices, flexible schemas Existing SQL infrastructure, relational tooling

The sink type applies globally to all projections and reducers registered by the client. It cannot be set per-read-model from client configuration today — all read models share the same sink backend.