Skip to content

RavenDb Integration 3.0

Wolverine supports a RavenDb backed message persistence strategy option as well as RavenDb-backed transactional middleware and saga persistence. To get started, add the WolverineFx.RavenDb dependency to your application:

bash
dotnet add package WolverineFx.RavenDb

and in your application, tell Wolverine to use RavenDb for message persistence:

cs
var builder = Host.CreateApplicationBuilder();

// You'll need a reference to RavenDB.DependencyInjection
// for this one
builder.Services.AddRavenDbDocStore(raven =>
{
    // configure your RavenDb connection here
});

builder.UseWolverine(opts =>
{
    // That's it, nothing more to see here
    opts.UseRavenDbPersistence();
    
    // The RavenDb integration supports basic transactional
    // middleware just fine
    opts.Policies.AutoApplyTransactions();
});

// continue with your bootstrapping...

snippet source | anchor

Also see RavenDb's own documentation for bootstrapping RavenDb inside of a .NET application.

Message Persistence

The durable inbox and outbox options in Wolverine are completely supported with RavenDb as the persistence mechanism. This includes scheduled execution (and retries), dead letter queue storage using the DeadLetterMessage collection, and the ability to replay designated messages in the dead letter queue storage.

Saga Persistence

The RavenDb integration can serve as a Wolverine Saga persistence mechanism. The only limitation is that your Saga types can only use strings as the identity for the Saga.

cs
public class Order : Saga
{
    // Just use this for the identity
    // of RavenDb backed sagas
    public string Id { get; set; }
    
    // Handle and Start methods...
}

snippet source | anchor

There's nothing else to do, if RavenDb integration is applied to your Wolverine, it's going to kick in for saga persistence as long as your Saga type has a string identity property.

Transactional Middleware

WARNING

The RavenDb transactional middleware only supports the RavenDb IAsyncDocumentSession service

The normal configuration options for transactional middleware in Wolverine apply to the RavenDb backend, so either mark handlers explicitly with [Transactional] like so:

cs
public class CreateDocCommandHandler
{
    [Transactional]
    public async Task Handle(CreateDocCommand message, IAsyncDocumentSession session)
    {
        await session.StoreAsync(new FakeDoc { Id = message.Id });
    }
}

snippet source | anchor

Or if you choose to do this more conventionally (which folks do tend to use quite often):

csharp
        builder.UseWolverine(opts =>
        {
            // That's it, nothing more to see here
            opts.UseRavenDbPersistence();
            
            // The RavenDb integration supports basic transactional
            // middleware just fine
            opts.Policies.AutoApplyTransactions();
        });

and the transactional middleware will kick in on any message handler or HTTP endpoint that uses the RavenDb IAsyncDocumentSession like this handler signature:

cs
public class AlternativeCreateDocCommandHandler
{
    // Auto transactions would kick in just because of the dependency
    // on IAsyncDocumentSession
    public async Task Handle(CreateDocCommand message, IAsyncDocumentSession session)
    {
        await session.StoreAsync(new FakeDoc { Id = message.Id });
    }
}

snippet source | anchor

The transactional middleware will also be applied for any usage of the RavenOps side effects model for Wolverine's RavenDb integration:

cs
public record RecordTeam(string Team, int Year);

public static class RecordTeamHandler
{
    public static IRavenDbOp Handle(RecordTeam command)
    {
        return RavenOps.Store(new Team { Id = command.Team, YearFounded = command.Year });
    }
}

snippet source | anchor

System Control Queues

The RavenDb integration to Wolverine does not yet come with a built in database control queue mechanism, so you will need to add that from external messaging brokers as in this example using Azure Service Bus:

cs
var builder = Host.CreateApplicationBuilder();
builder.UseWolverine(opts =>
{
    // One way or another, you're probably pulling the Azure Service Bus
    // connection string out of configuration
    var azureServiceBusConnectionString = builder
        .Configuration
        .GetConnectionString("azure-service-bus")!;

    // Connect to the broker in the simplest possible way
    opts.UseAzureServiceBus(azureServiceBusConnectionString)
        .AutoProvision()
        
        // This enables Wolverine to use temporary Azure Service Bus
        // queues created at runtime for communication between
        // Wolverine nodes
        .EnableWolverineControlQueues();

});

snippet source | anchor

For local development, there is also an option to let Wolverine just use its TCP transport as a control endpoint with this configuration option:

csharp
WolverineOptions.UseTcpForControlEndpoint();

In the option above, Wolverine is just looking for an unused port, and assigning that found port as the listener for the node being bootstrapped.

RavenOps Side Effects

The RavenOps static class can be used as a convenience for RavenDb integration with Wolverine:

cs
/// <summary>
/// Side effect helper class for Wolverine's integration with RavenDb
/// </summary>
public static class RavenOps
{
    /// <summary>
    /// Store a new RavenDb document
    /// </summary>
    /// <param name="document"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static IRavenDbOp Store<T>(T document) => new StoreDoc<T>(document);

    /// <summary>
    /// Delete this document in RavenDb
    /// </summary>
    /// <param name="document"></param>
    /// <returns></returns>
    public static IRavenDbOp DeleteDocument(object document) => new DeleteByDoc(document);

    /// <summary>
    /// Delete a RavenDb document by its id
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public static IRavenDbOp DeleteById(string id) => new DeleteById(id);
}

snippet source | anchor

See the Wolverine side effects model for more information.

Released under the MIT License.