
In the announcement for the Wolverine 5.0 release last week, I left out a pretty big set of improvements for modular monolith support, specifically in how Wolverine can now work with multiple databases from one service process.
Wolverine works closely with databases for:
- Transactional middleware, which also includes:
- A transactional inbox, outbox, and scheduled messaging (in addition to using native message broker scheduling in other cases)
- End to end multi-tenancy with both Marten “conjoined” tenancy and a database per tenant for both Marten and EF Core
- A database backed “dead letter queue” with “replay” capabilities
- Some miscellaneous storage that helps with Wolverine’s leadership election and agent assignment
- Saga storage
And all of those features are supported for Marten, EF Core with either PostgreSQL or SQL Server, and RavenDb.
Back to the “modular monolith” approach and what I’m seeing folks do or want to do is some combination of:
- Use multiple EF Core
DbContexttypes that target the same database, but maybe with different schemas - Use Marten’s “ancillary or separated store” feature to divide the storage up for different modules against the same database
Wolverine 3/4 supported the previous two bullet points, but now Wolverine 5 will be able to support any combination of every possible option in the same process. That even includes the ability to:
- Use multiple
DbContexttypes that target completely different databases altogether - Mix and match with Marten ancillary stores that target completely different database
- Use RavenDb for some modules, even if others use PostgreSQL or SQL Server
- Utilize either Marten’s built in multi-tenancy through a database per tenant or Wolverine’s managed EF Core multi-tenancy through a database per tenant
And now do that in one process while being able to support Wolverine’s transactional inbox, outbox, scheduled messages, and saga support for every single database that the application utilizes. And oh, yeah, from the perspective of the future CritterWatch, you’ll be able to use Wolverine’s dead letter management services against every possible database in the service.
Okay, this is the point where I do have to admit that the RavenDb support for the dead letter administration is lagging a little bit, but we’ll get that hole filled in soon.
Here’s an example from the tests:
var builder = Host.CreateApplicationBuilder();
var sqlserver1 = builder.Configuration.GetConnectionString("sqlserver1");
var sqlserver2 = builder.Configuration.GetConnectionString("sqlserver2");
var postgresql = builder.Configuration.GetConnectionString("postgresql");
builder.UseWolverine(opts =>
{
// This helps Wolverine "know" how to share inbox/outbox
// storage across logical module databases where they're
// sharing the same physical database but with different schemas
opts.Durability.MessageStorageSchemaName = "wolverine";
// This will be the "main" store that Wolverine will use
// for node storage
opts.Services.AddMarten(m =>
{
m.Connection(postgresql);
}).IntegrateWithWolverine();
// "An" EF Core module using Wolverine based inbox/outbox storage
opts.UseEntityFrameworkCoreTransactions();
opts.Services.AddDbContextWithWolverineIntegration<SampleDbContext>(x => x.UseSqlServer(sqlserver1));
// This is helping Wolverine out by telling it what database to use for inbox/outbox integration
// when using this DbContext type in handlers or HTTP endpoints
opts.PersistMessagesWithSqlServer(sqlserver1, role:MessageStoreRole.Ancillary).Enroll<SampleDbContext>();
// Another EF Core module
opts.Services.AddDbContextWithWolverineIntegration<ItemsDbContext>(x => x.UseSqlServer(sqlserver2));
opts.PersistMessagesWithSqlServer(sqlserver2, role:MessageStoreRole.Ancillary).Enroll<ItemsDbContext>();
// Yet another Marten backed module
opts.Services.AddMartenStore<IFirstStore>(m =>
{
m.Connection(postgresql);
m.DatabaseSchemaName = "first";
});
});
I’m certainly not saying that you *should* run out and build a system that has that many different persistence options in a single deployable service, but now you *can* with Wolverine. And folks have definitely wanted to build Wolverine systems that target multiple databases for different modules and still get every bit of Wolverine functionality for each database.
Summary
Part of the Wolverine 5.0 work was also Jeffry Gonzalez and I pushing on JasperFx’s forthcoming “CritterWatch” tool and looking for any kind of breaking changes in the Wolverine “publinternals” that might be necessary to support CritterWatch. The “let’s let you use all the database options at one time!” improvements I tried to show in the post were suggested by the work we are doing for dead letter message management in CritterWatch.
I shudder to think how creative folks are going to be with this mix and match ability, but it’s cool to have some bragging rights over these capabilities because I don’t think that any other .NET tool can match this.