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:
dotnet add package WolverineFx.RavenDb
and in your application, tell Wolverine to use RavenDb for message persistence:
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...
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
.
public class Order : Saga
{
// Just use this for the identity
// of RavenDb backed sagas
public string Id { get; set; }
// Handle and Start methods...
}
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:
public class CreateDocCommandHandler
{
[Transactional]
public async Task Handle(CreateDocCommand message, IAsyncDocumentSession session)
{
await session.StoreAsync(new FakeDoc { Id = message.Id });
}
}
Or if you choose to do this more conventionally (which folks do tend to use quite often):
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:
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 });
}
}
The transactional middleware will also be applied for any usage of the RavenOps
side effects model for Wolverine's RavenDb integration:
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 });
}
}
RavenOps Side Effects
The RavenOps
static class can be used as a convenience for RavenDb integration with Wolverine:
/// <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);
}
See the Wolverine side effects model for more information.