Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Eventuous/eventuous/llms.txt

Use this file to discover all available pages before exploring further.

The Eventuous.Projections.MongoDB package integrates Eventuous subscriptions with MongoDB, providing a strongly-typed projector base class and a checkpoint store so your subscription positions survive restarts. It is not an event store itself — it is designed to hold read models built from events stored elsewhere (typically KurrentDB or PostgreSQL).

Installation

dotnet add package Eventuous.Projections.MongoDB

Core types at a glance

TypeRole
MongoProjector<T>Abstract base class; register per-event handlers via On<>()
MongoCheckpointStorePersists subscription checkpoints in a MongoDB collection
ProjectedDocumentRequired base record for all MongoDB read-model documents
MongoCollectionNameConvention-based collection name resolver

Setting up IMongoDatabase

Register a configured IMongoDatabase instance in the DI container. The sample below shows a helper method that reads the connection string from configuration:
public static IMongoDatabase ConfigureMongo(IConfiguration configuration) {
    var settings = MongoClientSettings.FromConnectionString(
        configuration["Mongo:ConnectionString"]
    );
    var client = new MongoClient(settings);
    return client.GetDatabase(configuration["Mongo:Database"]);
}

// In Program.cs / Startup.cs
builder.Services.AddSingleton(Mongo.ConfigureMongo(configuration));

Projected documents

Every document stored by a MongoProjector<T> must extend ProjectedDocument:
public abstract record ProjectedDocument(string Id) : Document(Id) {
    public ulong StreamPosition { get; init; }
    public ulong Position       { get; init; }
}
Define your read model:
public record BookingDocument(string Id) : ProjectedDocument(Id) {
    public string  RoomId   { get; init; } = "";
    public string  GuestId  { get; init; } = "";
    public decimal Price    { get; init; }
    public bool    Paid     { get; init; }
}

Writing a projector

Extend MongoProjector<T> and call On<TEvent>() inside the constructor to register each event type you want to handle. The framework calls the registered handler whenever an event of that type passes through the subscription pipeline.
public class BookingStateProjection : MongoProjector<BookingDocument> {
    public BookingStateProjection(IMongoDatabase database)
        : base(database) {
        // Insert a new document when a booking is registered
        On<BookingRegistered>(
            b => b.InsertOne
                .Document(ctx => new BookingDocument(ctx.Message.BookingId) {
                    RoomId  = ctx.Message.RoomId,
                    GuestId = ctx.Message.GuestId,
                    Price   = ctx.Message.Price
                })
        );

        // Update the document when the booking is paid
        On<BookingPaid>(
            b => b.UpdateOne
                .Id(ctx => ctx.Message.BookingId)
                .UpdateFromContext((ctx, update) =>
                    update.Set(x => x.Paid, true)
                )
        );

        // Remove the document on cancellation
        On<BookingCancelled>(
            b => b.DeleteOne
                .Id(ctx => ctx.Message.BookingId)
        );
    }
}
The MongoOperationBuilder<TEvent, T> fluent API supports InsertOne, UpdateOne, DeleteOne, and Bulk operations. Use Id(...) to target a document by its _id field, or Filter(...) for arbitrary filter expressions.

Collection naming

MongoCollectionName.For<T>() generates a camelCase collection name by stripping common suffixes (Document, Entity, View, Projection, ProjectionDocument, ProjectionEntity) from the type name. For example, BookingDocument becomes the collection Booking. Override the collection name via MongoProjectionOptions<T>:
public class BookingStateProjection(IMongoDatabase database)
    : MongoProjector<BookingDocument>(
        database,
        new MongoProjectionOptions<BookingDocument> {
            CollectionName = "myBookings"
        }
    ) { /* ... */ }

Checkpoint store

MongoCheckpointStore saves the last processed subscription position in a MongoDB collection (default collection name: checkpoint). Use it when your read models live in the same MongoDB instance to avoid managing a separate checkpoint store.
// Simplest constructor — uses default options
var checkpointStore = new MongoCheckpointStore(database, loggerFactory);
MongoCheckpointStoreOptions controls batching behaviour, which can significantly reduce write pressure for high-throughput subscriptions:
PropertyDefaultDescription
CollectionName"checkpoint"Collection that holds checkpoint documents
BatchSize1Checkpoint commits are batched up to this count
BatchIntervalSec5Checkpoint commits are flushed at least every N seconds

Registration

Register the projector and checkpoint store as part of a subscription:
services.AddSingleton(Mongo.ConfigureMongo(configuration));

services.AddSubscription<AllStreamSubscription, AllStreamSubscriptionOptions>(
    "BookingsProjections",
    builder => builder
        .UseCheckpointStore<MongoCheckpointStore>()
        .AddEventHandler<BookingStateProjection>()
        .AddEventHandler<MyBookingsProjection>()
        .WithPartitioningByStream(2)
);
UseCheckpointStore<MongoCheckpointStore>() resolves MongoCheckpointStore from the DI container, which in turn requires IMongoDatabase and ILoggerFactory to be registered. Both are available by default in an ASP.NET Core application.

Build docs developers (and LLMs) love