Tentative Roadmap for Wolverine 3.0

By the way, JasperFx Software offers support contracts or tailored consulting engagements to help your shop maximize your success with Wolverine.

It’s an unfortunate fact of long running software tooling projects like Wolverine that decisions made years earlier won’t hold up perfectly as the usage requirements, underlying platform, and other tools it’s integrated with change over time. In the case of Wolverine, it’s time for a medium sized 3.0 release to make some potentially breaking API changes and changes to its original model to accommodate some use cases that weren’t part of the original vision.

This is all subject to change based on whatever feedback comes in, but right now I think the items that are absolutely in scope are:

  1. De-coupling Wolverine from Lamar. That work has been unfortunately huge so far, but almost complete as I type this. At a minimum, Wolverine is going to be functional with both the build in ServiceProvider container and still Lamar itself. To a large degree, this was actually a prerequisite for the next bullet point
  2. Make sure that Wolverine can be bootstrapped from basically any possible flavor of XyzHostBuilder and not just IHostBuilder like it is today. That’s a current annoyance, but also an absolute prerequisite for the next bullet point
  3. Get Wolverine completely on the .NET Aspire train. Really just making sure that all the external infrastructure integrations with Wolverine that also have Aspire integrations like PostgreSQL, Sql Server, Rabbit MQ, Azure Service Bus, Kafka, and AWS SQS are able to correctly use the Aspire configuration. That’s really just a matter of using some IoC container integration and not a huge deal. I think that testing and documentation will be more work on that front than the actual development. I know that there are mixed opinions about whether or not Aspire is valuable, but this can’t be a reason why folks won’t consider using Wolverine in the future, so here we go.
  4. Eliminate the goofy limitation that Wolverine has today where if a message type has multiple handlers, they can only run together in one logical transaction. A couple users and at least one JasperFx customer have wanted to run multiple local actions asynchronously on the same message. Wolverine 3.0 will enable this. This is the result of historical reasons dating back to FubuTransportation from the early 2010’s that no longer make the slightest sense now
  5. Maybe allow Wolverine to address multiple service brokers of the same type in a single application. I.e. 2 or more connections to Azure Service Bus or Rabbit MQ etc. I think that might take breaking API changes, so it’s time to look at that now

Beyond that, I think there are some additive features I just didn’t want to work on right now until the 3.0 work is complete:

  • An HTTP messaging transport — which I think we really want inside of Wolverine as a precursor to the long planned “Critter Stack Pro” add on tooling. Which also might help enable:
  • Some improvements for dynamic multi-tenancy where you want to spin up or down tenants in a running application without downtime
  • Improving the EF Core integration with Wolverine to bring it a bit up to parity with the existing Marten integrations. That would potentially include multi-tenancy support and more middleware and productivity shortcuts for Wolverine
  • I’d like to completely reconsider our bootstrapping, especially in an application that combines both Marten & Wolverine. I think there’s some room for a customized CritterStackApplicationBuilder or CritterStackWebApplication model. More on this later.

I don’t really want this to be a huge release that takes very long, and I absolutely don’t want this to require very many changes at all for our users to adopt this.

The “Critter Stack” Just Leveled Up on Modular Monolith Support

The goal for the “Critter Stack” tools is to be the absolute best set of tools for building server side .NET applications, and especially for any usage of Event Driven Architecture approaches. To go even farther, I would like there to be a day where organizations purposely choose the .NET ecosystem just because of the benefits that the “Critter Stack” provides over other options. But for now, that’s the journey we’re on. This post demonstrates an important new feature that I think fills in a huge capability gap that has long bothered me.

And as always, JasperFx Software is happy to work with any “Critter Stack” users through either support contracts or consulting engagements to help you wring the most value out of our tools and help you succeed with what you’re building.

I recently wrote some posts about the whole “Modular Monolith” architecture approach:

  1. Thoughts on “Modular Monoliths”
  2. Actually Talking about Modular Monoliths
  3. Modular Monoliths and the “Critter Stack”

Marten already has strong support for modular monoliths through its “separate store” functionality. In the last post though, I lamented that all the whizz bang integration between Wolverine and Marten like the aggregate handler workflow or Wolverine’s transactional outbox or Marten side effects or the new event subscription model that make the full “Critter Stack” such a productive toolset for Event Sourcing were, alas, not available in conjunction with Marten’s separate store model.

This week I’m helping a JasperFx client who has some complicated multi-tenancy requirements. In one of their services they have some types of event streams that need to use “conjoined multi-tenancy“, but at least one type of event stream (and related aggregate) that is global across all tenants. Marten event stores are either multi-tenanted or they’re not, with no mixing and matching. It occurred to me that we could solve this issue by putting the one type of global event streams in a separate Marten store. Even though the 2nd Marten store will still target the exact same PostgreSQL database (but in a different schema), we can give this second schema a different configuration to accommodate the different tenancy rules. Moreover, this would even be a good way to improve performance and scalability of their service by effectively sharding the events and streams tables (smaller tables generally mean better performance).

At the same time, I’m also helping them introduce Wolverine message handlers as well, and I really wanted to be able to use the aggregate handler workflow for commands that spawn new Marten events (effectively the Critter Stack version of the “Decider” pattern, but lower ceremony). I finally took some time — and stumbled onto a workable approach — that finally adds far better support for modular monolith architectures with the Wolverine 2.13.0 release that hit today.

Specifically, Wolverine finally got some support for full integration with ancillary document and event stores from Marten in the same application.

To see a sneak peek, let’s say that you have two additional Marten stores for your application like these two:

public interface IPlayerStore : IDocumentStore;
public interface IThingStore : IDocumentStore;

You can now bootstrap a Marten + Wolverine application (using the WolverineFx.Marten Nuget dependency) like so:

theHost = await Host.CreateDefaultBuilder()
    .UseWolverine(opts =>
    {
        opts.Services.AddMarten(Servers.PostgresConnectionString).IntegrateWithWolverine();

        opts.Policies.AutoApplyTransactions();
        opts.Durability.Mode = DurabilityMode.Solo;

        opts.Services.AddMartenStore<IPlayerStore>(m =>
        {
            m.Connection(Servers.PostgresConnectionString);
            m.DatabaseSchemaName = "players";
        })
            // THIS AND BELOW IS WHAT IS NEW FOR WOLVERINE 2.13
            .IntegrateWithWolverine()
            
            // Add a subscription
            .SubscribeToEvents(new ColorsSubscription())
            
            // Forward events to wolverine handlers
            .PublishEventsToWolverine("PlayerEvents", x =>
            {
                x.PublishEvent<ColorsUpdated>();
            });
        
        // Look at that, it even works with Marten multi-tenancy through separate databases!
        opts.Services.AddMartenStore<IThingStore>(m =>
        {
            m.MultiTenantedDatabases(tenancy =>
            {
                tenancy.AddSingleTenantDatabase(tenant1ConnectionString, "tenant1");
                tenancy.AddSingleTenantDatabase(tenant2ConnectionString, "tenant2");
                tenancy.AddSingleTenantDatabase(tenant3ConnectionString, "tenant3");
            });
            m.DatabaseSchemaName = "things";
        }).IntegrateWithWolverine(masterDatabaseConnectionString:Servers.PostgresConnectionString);

        opts.Services.AddResourceSetupOnStartup();
    }).StartAsync();

Now, moving to message handlers or HTTP endpoints, you will have to explicitly tag either the containing class or individual messages with the [MartenStore(store type)] attribute like this simple example below:

// This will use a Marten session from the
// IPlayerStore rather than the main IDocumentStore
[MartenStore(typeof(IPlayerStore))]
public static class PlayerMessageHandler
{
    // Using a Marten side effect just like normal
    public static IMartenOp Handle(PlayerMessage message)
    {
        return MartenOps.Store(new Player{Id = message.Id});
    }
}

Boom! Even that minor sample is using transactional middleware targeting Marten and able to work with the separate IPlayerStore. This new integration includes:

  • Transactional outbox support for all configured Marten stores
  • Transactional middleware
  • The “aggregate handler workflow”
  • Marten side effects
  • Subscriptions to Marten events
  • Multi-tenancy, both “conjoined” Marten multi-tenancy and multi-tenancy through separate databases

For more information, see the documentation on this new feature.

Summary

I’m maybe a little too excited for a feature that most users will never touch, but for those who do see this, the “Critter Stack” now has first class modular monolith support across a wide range of the features that make the “Critter Stack” a desirable platform in the first place.

Multi-Tenancy: What is it and why do you care?

I’m always on the lookout for ideas about how to endlessly promote both Marten & Wolverine. Since I’ve been fielding a lot of questions, issues, and client requests around multi-tenancy support in both tools the past couple weeks, now seems to be a good time for a new series exploring the existing foundation in both critter stack tools for handling quite a few basic to advanced multi-tenancy scenarios. But first, let’s start by just talking about what the phrase “multi-tenancy” even means for architecting software systems.

In the course of building systems, you’re frequently going to have a single system that needs to serve different sets of users or clients. Some examples I’ve run across have been systems that need to segregate data for different partner companies, different regions within the same company, or just flat out different users like online email services do today.

I don’t know the origin of the terminology, but we refer to those logical separations within the system data as “tenants.”

My youngest is very quickly outgrowing Dr. Seuss books, but we still read “Because a Bug went Kachoo!” above

It’s certainly going to be important many times to keep the data accessed through the system segregated so that nobody is able to access data that they should not. For example, I probably shouldn’t be able to read you email inbox when I lot into my gmail account. For another example from my early career, I worked with an early web application system that was used to gather pricing quotes from my very large manufacturing company’s suppliers for a range of parts. Due to a series of unfortunate design decisions (because a bug went kachoo!), that application did a very poor job being able to segregate data, and I figured out that some of our suppliers were able to see the quoted prices from their competitors and get unfair advantages.

So we can all agree that mixing up the data between users who shouldn’t see each other’s data is a bad thing, so what can we do about that? The most extreme solution is to just flat out deploy a completely different set of servers for each segregated group of users as shown below:

While there are some valid reasons once in awhile to do the completely separate deployments, that’s potentially a lot of overhead and extra hosting costs. At best, this is probably only viable for a finite number of deployments (Gmail is certainly not building out a separate web server for every one of us with a Gmail account for example).

When a single deployed system is able to serve different tenants, we call that “multi-tenancy.”

According to Wikipedia:

Software multitenancy is a software architecture in which a single instance of software runs on a server and serves multiple tenants.

With multi-tenancy, we’re ensuring that one single deployment of the logical service can handle requests for multiple tenants without allowing users from one tenant to inappropriately see data from other tenants.

Roughly speaking, I’m familiar with three different ways to achieve multi-tenancy.

The first approach is to use one database for all tenant data, but to use some sort of tenant id field that just denotes which tenant the data belongs to. This is what I termed “Conjoined Tenancy” in Marten. This approach is simpler in terms of the database deployment and database change management because after all, there’s only one of them! It is potentially more complex within your codebase because your persistence layer will always need to apply filters on the data being modified and accessed by the user and whichever tenant they are part of.

    There’s some inherent risk with this approach as developers aren’t perfectly omniscient, and there’s always a chance that we miss some scenarios and let data leak out inappropriately to the wrong users. I think this approach is much more viable when using persistence tooling that has strong support (like Marten!) for this type of “conjoined multi-tenancy” and mostly takes care of the tenancy filters for you.

    The second approach is to use a separate schema for each tenant within the same database. I’ve never used this approach myself, and I’m not aware of any tooling in my own .NET ecosystem that supports this approach out of the box. I think this would be a good approach if you were building something on top of a relational database from scratch with a custom data layer — but I think it would be a lot of extra overhead managing the database schema migrations.

    The third way to do multi-tenancy is to use a separate database for each tenant, but the single deployed system is smart enough to connect to the correct database throughout its persistence layer based on the current user (or through metadata on messages as we’ll see in a later entry on Wolverine multi-tenancy). This approach is shown below:

    There’s of course some challenges to this approach as well. First off, there’s more databases to worry about, and subsequently more overhead for database migrations and management. This approach does give you rock solid data segregation between tenants, and I’ve heard of strong business or regulatory requirements to take this approach even when the data volume wouldn’t require this. As my last statement hints at, we all know that the system database is very commonly the bottleneck for performance and scalability, so segregating different tenant data into separate databases may be a good way to improve the scalability of your system.

    It’s obviously going to be more difficult to do any kind of per-tenant data rollup or summary with the separate database approach, but some cloud providers have specialized infrastructure for per tenant database multi-tenancy.

    A Note about Scalability

    I was taught very early on that an effective way to scale systems was to design for any given server to be able to handle all the possible types of operations, then add more servers to the horizontal cluster. I think at the time this was in reaction to several systems we had where teams had tried to scale bigger systems by segregating all operations for one region to one set of servers, and a different set of servers for other regions. The end result was an explosion of deployed servers and frequently having servers absolutely pegged on CPU or memory while North America factories were in full swing while the servers tasked with handling factories on the Pacific Rim were completely dormant when their factories were closed. An architecture that can spread all the work across the cluster of running nodes might often be a much cheaper solution in the end than standing up many more nodes that can only service a subset of tenants.

    Then again, you might also want to prioritize some tenants over others, so take everything I just said with a grain of “it depends” salt.

    Thar be Dragons!

    In the next set of posts, I’ll get into first Marten, then Wolverine capabilities for multi-tenancy, but just know first that there’s a significant set of challenges ahead:

    • Managing multiple database schemas if using separate databases per tenant
    • Needing to use per-tenant filters if using the conjoined storage model for query segregation — and trust me as the author of a persistence tool, there’s plenty of edge case dragons here
    • Detecting the current tenant based on HTTP requests or messaging metadata
    • Communicating the tenant information when using asynchronous messaging
    • Querying across tenants
    • Dynamically spinning up new tenant databases at runtime — or tearing them down! — or even moving them at runtime?!?
    • Isolated data processing by tenant database
    • Multi-level tenancy!?! JasperFx helped a customer build this out with Marten
    • Transactional outbox support in a multi-tenanted work — which Wolverine can do today!

    The two “Critter Stack” tools help with most of these challenges today, and I’ll get around to some discussion about future work to help fill in the more advanced usages that some real users are busy running into right now.

    Wolverine’s Test Support Diagnostics

    I’m working on fixing a reported bug with Wolverine today and its event forwarding from Marten feature. I can’t say that I yet know why this should-be-very-straightforward-and-looks-exactly-like-the-currently-passing-tests bug is happening, but it’s a good time to demonstrate Wolverine’s automated testing support and even how it can help you to understand test failures.

    First off, and I’ll admit that there’s some missing context here, I’m setting up a system such that when this message handler is executed:

    public record CreateShoppingList();
    
    public static class CreateShoppingListHandler
    {
        public static string Handle(CreateShoppingList _, IDocumentSession session)
        {
            var shoppingListId = CombGuidIdGeneration.NewGuid().ToString();
            session.Events.StartStream<ShoppingList>(shoppingListId, new ShoppingListCreated(shoppingListId));
            return shoppingListId;
        }
    }
    

    The configured Wolverine + Marten integration should be kicking in to publish the event appended in the handler above to a completely different handler shown below with the Marten IEvent wrapper so that you can use Marten event store metadata within the secondary, cascaded message:

    public static class IntegrationHandler
    {
        public static void Handle(IEvent<ShoppingListCreated> _)
        {
            // Don't need a body here, and I'll show why not
            // next
        }
    }
    

    Knowing those two things, here’s the test I wrote to reproduce the problem:

        [Fact]
        public async Task publish_ievent_of_t()
        {
            // The "Arrange"
            using var host = await Host.CreateDefaultBuilder()
                .UseWolverine(opts =>
                {
                    opts.Policies.AutoApplyTransactions();
    
                    opts.Services.AddMarten(m =>
                    {
                        m.Connection(Servers.PostgresConnectionString);
                        m.DatabaseSchemaName = "forwarding";
    
                        m.Events.StreamIdentity = StreamIdentity.AsString;
                        m.Projections.LiveStreamAggregation<ShoppingList>();
                    }).UseLightweightSessions()
                    .IntegrateWithWolverine()
                    .EventForwardingToWolverine();;
                }).StartAsync();
            
            // The "Act". This method is an extension method in Wolverine
            // specifically for facilitating integration testing that should
            // invoke the given message with Wolverine, then wait until all
            // additional "work" is complete
            var session = await host.InvokeMessageAndWaitAsync(new CreateShoppingList());
    
            // And finally, just assert that a single message of
            // type IEvent<ShoppingListCreated> was executed
            session.Executed.SingleMessage<IEvent<ShoppingListCreated>>()
                .ShouldNotBeNull();
        }
    

    And now, when I run the test — which “helpfully” reproduces reported bug from earlier today — I get this output:

    System.Exception: No messages of type Marten.Events.IEvent<MartenTests.Bugs.ShoppingListCreated> were received
    
    System.Exception
    No messages of type Marten.Events.IEvent<MartenTests.Bugs.ShoppingListCreated> were received
    Activity detected:
    
    ----------------------------------------------------------------------------------------------------------------------
    | Message Id                             | Message Type                          | Time (ms)   | Event               |
    ----------------------------------------------------------------------------------------------------------------------
    | 018f82a9-166d-4c71-919e-3bcb04a93067   | MartenTests.Bugs.CreateShoppingList   |          873| ExecutionStarted    |
    | 018f82a9-1726-47a6-b657-2a59d0a097cc   | System.String                         |         1057| NoRoutes            |
    | 018f82a9-17b1-4078-9997-f6117fd25e5c   | EventShoppingListCreated              |         1242| Sent                |
    | 018f82a9-166d-4c71-919e-3bcb04a93067   | MartenTests.Bugs.CreateShoppingList   |         1243| ExecutionFinished   |
    | 018f82a9-17b1-4078-9997-f6117fd25e5c   | EventShoppingListCreated              |         1243| Received            |
    | 018f82a9-17b1-4078-9997-f6117fd25e5c   | EventShoppingListCreated              |         1244| NoHandlers          |
    ----------------------------------------------------------------------------------------------------------------------
    

    EDIT: If I’d read this more closely before, I would have noticed that the problem was somewhere different than the routing that I first suspected from a too casual read.

    The textual table above is Wolverine telling me what it did do during the failed test. In this case, the output does tip me off that there’s some kind of issue with the internal message routing in Wolverine that should be applying some special rules for IEvent<T> wrappers, but was not in this case. While that work fixing the real bug continues for me, what I hope you get out of this is how Wolverine is trying to help you diagnose test failures by providing diagnostic information about what was actually happening internally during all the asynchronous processing. As a long veteran of test automation efforts, I will vociferously say that it’s important for test automation harnesses to be able to adequately explain the inevitable test failures. Like Wolverine helpfully does.

    Now, back to work trying to actually fix the problem…

    Scheduled Message Delivery with Wolverine

    Wolverine has the ability to schedule the delivery of messages for a later time. While Wolverine certainly isn’t trying to be Hangfire or Quartz.Net, the message scheduling in Wolverine today is valuable for “timeout” messages in sagas, or “retry this evening” type scenarios, or reminders of all sorts.

    If using the Azure Service Bus transport, scheduled messages sent to Azure Service Bus queues or topics will use native Azure Service Bus scheduled delivery. For everything else today, Wolverine is doing the scheduled delivery for you. To make those scheduled messages be durable (i.e. not completely lost when the application is shut down), you’re going to want to add message persistence to your Wolverine application as shown in the sample below using SQL Server:

    // This is good enough for what we're trying to do
    // at the moment
    builder.Host.UseWolverine(opts =>
    {
        // Just normal .NET stuff to get the connection string to our Sql Server database
        // for this service
        var connectionString = builder.Configuration.GetConnectionString("SqlServer");
        
        // Telling Wolverine to build out message storage with Sql Server at 
        // this database and using the "wolverine" schema to somewhat segregate the 
        // wolverine tables away from the rest of the real application
        opts.PersistMessagesWithSqlServer(connectionString, "wolverine");
        
        // In one fell swoop, let's tell Wolverine to make *all* local
        // queues be durable and backed up by Sql Server 
        opts.Policies.UseDurableLocalQueues();
    });
    

    Finally, with all that said, here’s one of the ways to schedule message deliveries:

        public static async Task use_message_bus(IMessageBus bus)
        {
            // Send a message to be sent or executed at a specific time
            await bus.ScheduleAsync(new DebitAccount(1111, 100), DateTimeOffset.UtcNow.AddDays(1));
    
            // Or do the same, but this time express the time as a delay
            await bus.ScheduleAsync(new DebitAccount(1111, 225), 1.Days());
            
            // ScheduleAsync is really just syntactic sugar for this:
            await bus.PublishAsync(new DebitAccount(1111, 225), new DeliveryOptions { ScheduleDelay = 1.Days() });
        }
    

    Or, if you want to utilize Wolverine’s cascading message functionality to keep most if not all of your handler method signatures “pure”, you can use this syntax within message handlers or HTTP endpoints:

        public static IEnumerable<object> Consume(Incoming incoming)
        {
            // Delay the message delivery by 10 minutes
            yield return new Message1().DelayedFor(10.Minutes());
    
            // Schedule the message delivery for a certain time
            yield return new Message2().ScheduledAt(new DateTimeOffset(DateTime.Today.AddDays(2)));
        }
    

    Finally, one last alternative that was primarily meant for saga usage, subclassing TimeoutMessage like so:

    public record EnforceAccountOverdrawnDeadline(Guid AccountId) : TimeoutMessage(10.Days()), IAccountCommand;
    

    By subclassing TimeoutMessage, the message type above is “scheduled” for a later time when it’s returned as a cascading message.

    Wolverine’s HTTP Model Does More For You

    One of the things I’m wrestling with right now is frankly how to sell Wolverine as a server side toolset. Yes, it’s technically a message library like MassTransit or NServiceBus. It can also be used as “just” a mediator tool like MediatR. With Wolverine.HTTP, it’s even an alternative HTTP endpoint framework that’s technically an alternative to FastEndpoints, MVC Core, or Minimal API. You’ve got to categorize Wolverine somehow, and we humans naturally understand something new by comparing it to some older thing we’re already familiar with. In the case of Wolverine, it’s drastically selling the toolset short by comparing it to any of the older application frameworks I rattled off above because Wolverine fundamentally does much more to remove code ceremony, improve testability throughout your codebase, and generally just let you focus more on core application functionality than older application frameworks.

    This post was triggered by a conversation I had with a friend last week who told me he was happy with his current toolset for HTTP API creation and couldn’t imagine how Wolverine’s HTTP endpoint model could possibly reduce his efforts. Challenge accepted!

    For just this moment, consider a simplistic HTTP service that works on this little entity:

    public record Counter(Guid Id, int Count);
    

    Now, let’s build an HTTP endpoint that will:

    1. Receive route arguments for the Counter.Id and the current tenant id because of course let’s say that we’re using multi-tenancy with a separate database per tenant
    2. Try to look up the existing Counter entity by its id from the right tenant database
    3. If the entity doesn’t exist, return a status code 404 and get out of there
    4. If the entity does exist, increment the Count property and save the entity to the database and return a status code 204 for a successful request with an empty body

    Just to make it easier on me because I already had this example code, we’re going to use Marten for persistence which happens to have much stronger multi-tenancy built in than EF Core. Knowing all that, here’s a sample MVC Core controller to implement the functionality I described above:

    public class CounterController : ControllerBase
    {
        [HttpPost("/api/tenants/{tenant}/counters/{id}")]
        [ProducesResponseType(204)] // empty response
        [ProducesResponseType(404)]
        public async Task<IResult> Increment(
            Guid id, 
            string tenant, 
            [FromServices] IDocumentStore store)
        {
            // Open a Marten session for the right tenant database
            await using var session = store.LightweightSession(tenant);
            var counter = await session.LoadAsync<Counter>(id, HttpContext.RequestAborted);
            if (counter == null)
            {
                return Results.NotFound();
            }
            else
            {
                counter = counter with { Count = counter.Count + 1 };
                await session.SaveChangesAsync(HttpContext.RequestAborted);
                return Results.Empty;
            }
        }
    }
    

    I’m completely open to recreating the multi-tenancy support from the Marten + Wolverine combo for EF Core and SQL Server through Wolverine, but I’m shamelessly waiting until another company is willing to engage with JasperFx Software to deliver that.

    Alright, now let’s switch over to using Wolverine.HTTP with its WolverineFx.Http.Marten add on Nuget setup. Let’s drink some Wolverine koolaid and write a functionally identical endpoint the Wolverine way:

    You need Wolverine 2.7.0 for this by the way!

        [WolverinePost("/api/tenants/{tenant}/counters/{id}")]
        public static IMartenOp Increment([Document(Required = true)] Counter counter)
        {
            counter = counter with { Count = counter.Count + 1 };
            return MartenOps.Store(counter);
        }
    

    Seriously, this is the same functionality and even the same generated OpenAPI documentation. Some things to note:

    • Wolverine is able to derive much more of the OpenAPI documentation from the type signatures and from policies applied to the endpoint method, like…
    • The usage of the Document(Required = true) tells Wolverine that it will be trying to load a document of type Counter from Marten, and by default it’s going to do that through a route argument named “id”. The Required property tells Wolverine to return a 404 NotFound status code automatically if the Counter document doesn’t exist. This attribute usage also applies some OpenAPI smarts to tag the route as potentially returning a 404
    • The return value of the method is an IMartenOpside effect” just saying “go save this document”, which Wolverine will do as part of this endpoint execution. Using the side effect makes this method a nice, simple pure function that’s completely synchronous. No wrestling with async Task, await, or schlepping around CancellationToken every which way
    • Because Wolverine can see there will not be any kind of response body, it’s going to use a 204 status code to denote the empty body and tag the OpenAPI with that as well.
    • There is absolutely zero Reflection happening at runtime because Wolverine is generating and compiling code at runtime (or ahead of time for faster cold starts) that “bakes” in all of this knowledge for fast execution
    • Wolverine + Marten has a far more robust support for multi-tenancy all the way through the technology stack than any other application framework I know of in .NET (web frameworks, mediators, or messaging libraries), and you can see that evident in the code above where Marten & Wolverine would already know how to detect tenant usage in an HTTP request and do all the wiring for you all the way through the stack so you can focus on just writing business functionality.

    To make this all more concrete, here’s the generated code:

    // <auto-generated/>
    #pragma warning disable
    using Microsoft.AspNetCore.Routing;
    using System;
    using System.Linq;
    using Wolverine.Http;
    using Wolverine.Marten.Publishing;
    using Wolverine.Runtime;
    
    namespace Internal.Generated.WolverineHandlers
    {
        // START: POST_api_tenants_tenant_counters_id_inc2
        public class POST_api_tenants_tenant_counters_id_inc2 : Wolverine.Http.HttpHandler
        {
            private readonly Wolverine.Http.WolverineHttpOptions _wolverineHttpOptions;
            private readonly Wolverine.Runtime.IWolverineRuntime _wolverineRuntime;
            private readonly Wolverine.Marten.Publishing.OutboxedSessionFactory _outboxedSessionFactory;
    
            public POST_api_tenants_tenant_counters_id_inc2(Wolverine.Http.WolverineHttpOptions wolverineHttpOptions, Wolverine.Runtime.IWolverineRuntime wolverineRuntime, Wolverine.Marten.Publishing.OutboxedSessionFactory outboxedSessionFactory) : base(wolverineHttpOptions)
            {
                _wolverineHttpOptions = wolverineHttpOptions;
                _wolverineRuntime = wolverineRuntime;
                _outboxedSessionFactory = outboxedSessionFactory;
            }
    
    
    
            public override async System.Threading.Tasks.Task Handle(Microsoft.AspNetCore.Http.HttpContext httpContext)
            {
                var messageContext = new Wolverine.Runtime.MessageContext(_wolverineRuntime);
                // Building the Marten session
                await using var documentSession = _outboxedSessionFactory.OpenSession(messageContext);
                if (!System.Guid.TryParse((string)httpContext.GetRouteValue("id"), out var id))
                {
                    httpContext.Response.StatusCode = 404;
                    return;
                }
    
    
                var counter = await documentSession.LoadAsync<Wolverine.Http.Tests.Bugs.Counter>(id, httpContext.RequestAborted).ConfigureAwait(false);
                // 404 if this required object is null
                if (counter == null)
                {
                    httpContext.Response.StatusCode = 404;
                    return;
                }
    
                
                // The actual HTTP request handler execution
                var martenOp = Wolverine.Http.Tests.Bugs.CounterEndpoint.Increment(counter);
    
                if (martenOp != null)
                {
                    
                    // Placed by Wolverine's ISideEffect policy
                    martenOp.Execute(documentSession);
    
                }
    
                
                // Commit any outstanding Marten changes
                await documentSession.SaveChangesAsync(httpContext.RequestAborted).ConfigureAwait(false);
    
                
                // Have to flush outgoing messages just in case Marten did nothing because of https://github.com/JasperFx/wolverine/issues/536
                await messageContext.FlushOutgoingMessagesAsync().ConfigureAwait(false);
    
                // Wolverine automatically sets the status code to 204 for empty responses
                if (!httpContext.Response.HasStarted) httpContext.Response.StatusCode = 204;
            }
    
        }
    
        // END: POST_api_tenants_tenant_counters_id_inc2
        
        
    }
    
    

    Summary

    Wolverine isn’t “just another messaging library / mediator / HTTP endpoint alternative.” Rather, Wolverine is a completely different animal that while fulfilling those application framework roles for server side .NET, potentially does a helluva lot more than older frameworks to help you write systems that are maintainable, testable, and resilient. And do all of that with a lot less of the typical “Clean/Onion/Hexagonal Architecture” cruft that shines in software conference talks and YouTube videos but helps lead teams into a morass of unmaintainable code in larger systems in the real world.

    But yes, the Wolverine community needs to find a better way to communicate how Wolverine adds value above and beyond the more traditional server side application frameworks in .NET. I’m completely open to suggestions — and fully aware that some folks won’t like the “magic” in the “drank all the Wolverine Koolaid” approach I used.

    You can of course use Wolverine with 100% explicit code and none of the magic.

    Controlling Parallelism with Wolverine Background Processing

    A couple weeks back I started a new blog series meant to explore Wolverine’s capabilities for background processing. Working in very small steps and only one new concept at a time, the first time out just showed how to set up Wolverine inside a new ASP.Net Core web api service and immediately use it for offloading some processing from HTTP endpoints to background processing by using Wolverine’s local queues and message handlers for background processing. In the follow up post, I added durability to the background processing so that our work being executed in the background would be durable even in the face of application restarts.

    In this post, let’s look at how Wolverine allows you to either control the parallelism of your background processing, or restrict the processing to be strictly sequential.

    To review, in previous posts we were “publishing” a SignupRequest message from a Minimal API endpoint to Wolverine like so:

    app.MapPost("/signup", (SignUpRequest request, IMessageBus bus) 
        => bus.PublishAsync(request));
    

    In this particular case, our application has a message handler for SignupRequest, so Wolverine has a sensible default behavior of publishing the message to a local, in memory queue where each message will be processed in a separate thread from the original HTTP request, and do so asynchronously in the background.

    So far, so good? By default, each message type gets its own local, in memory queue, with a default “maximum degree of parallelism” equal to the number of detected processors (Environment.ProcessorCount). In addition, the local queues do not enforce strict ordering by default.

    But now, what if you do need to strict sequential ordering? Or if you want to restrict or expand the number of parallel messages that can be processed? Or the get really wild, constrain some messages to running sequentially while other messages run in parallel?

    First, let’s see how we could alter the parallelism of our SignUpRequest to an absurd degree and say that up to 20 messages could theoretically be processed at one time by the system. We’ll do that by breaking into the UseWolverine() configuration and adding this:

    builder.Host.UseWolverine(opts =>
    {
        // The other stuff...
    
        // Make the SignUpRequest messages be published with even 
        // more parallelization!
        opts.LocalQueueFor<SignUpRequest>()
            
            // A maximum of 20 at a time because why not!
            .MaximumParallelMessages(20);
    });
    

    Easy enough, but now let’s say that we want all logical event messages in our system to be handled in the sequential order that our process publishes these messages. An easy way to do that with Wolverine is to have each event message type implement Wolverine’s IEvent marker interface like so:

    public record Event1 : IEvent;
    public record Event2 : IEvent;
    public record Event3 : IEvent;
    

    To be honest, the IEvent and corresponding IMessage and ICommand interfaces were added to Wolverine originally just to make it easier to transition a codebase from using NServiceBus to Wolverine, but those types have little actual meaning to Wolverine. The only way that Wolverine even uses them is for the purpose of “knowing” that a type is an outbound message so that Wolverine can preview the message routing for a type implementing one of these interfaces automatically in diagnostics.

    Revisiting our UseWolverine() code block again, we’ll add that publishing rule like this:

    builder.Host.UseWolverine(opts =>
    {
        // Other stuff...
    
        opts.Publish(x =>
        {
            x.MessagesImplementing<IEvent>();
            x.ToLocalQueue("events")
                // Force every event message to be processed in the 
                // strict order they are enqueued, and one at a 
                // time
                .Sequential();
            });
    });
    

    With the code above, our application would be publishing every single message where the message type implements IEvent to that one local queue named “events” that has been configured to process messages in strict sequential order.

    Summary and What’s Next

    Wolverine makes it very easy to do work in background processing within your application, and even to easily control the desired parallelism in your application, or to make a subset of messages be processed in strict sequential order when that’s valuable instead.

    To be honest, this series is what I go to when I feel like I need to write more Critter Stack content for the week, so it might be a minute or two before there’s a follow up. There’ll be at least two more posts, one on scheduling message execution and an example of using the local processing capabilities in Wolverine to implement the producer/consumer pattern.

    Recent Marten & Wolverine Improvements and Roadmap Update

    I’d love any feedback on any of this of course. And from something I wrote in a survey of sorts about the commercial product ideas down below yesterday (which is partially a response to a recent query wanting to know how Marten stacks up against AxonIQ from the JVM world):

    There’s definitely an opportunity for a full blown Event Driven Architecture stack in the .NET ecosystem – and frankly, Jeremy really wants the Critter Stack to grow into the very best Event Driven Architecture toolset on the planet to the point where shops will purposely adopt .NET just because of the Critter Stack

    I’m honestly just thinking out loud in this post, but a lot has been released for both Marten and Wolverine since the big Marten 7.0 release and the last time I published a roadmap update for the two big toolsets.

    Here’s some recent highlights you might have missed from the past two months:

    What’s Next?

    Getting Marten 7.0 and the accompanying Wolverine 2.0 release across the finish line enabled a flurry of follow up features the past two month — largely driven by a couple JasperFx Software client projects (yeah!). Moving forward, I think these are the remaining strategic features that will hopefully go in soon:

    • Marten will get the ability to use PostgreSQL read replicas for read-only queries very soon as a way to scale applications
    • A new, alternative “Quick Append Event” workflow to Marten. The current internal mechanism in Marten for appending events is built for maximal support for “Inline” projections. This new model would simplify the runtime mechanics for appending events and hopefully make the Marten event store more robust in the face of concurrent requests than it is today. This model should also allow for faster performance if users opt into this mechanism.
    • Some ability to efficiently raise or append events (or side effects of some sort) from running projections. This has been in the backlog for a long time. I’d certainly feel better about this if we had some concrete use cases that folks want to do here. The “Quick Append Event” workflow would be a prerequisite
    • Using PostgreSQL partitioning on the Marten streams and events tables. This is the oldest item in the Marten backlog that’s been kicked down the road forever, but I think this is potentially huge for Marten scalability. This would probably be done in conjunction with some tactical improvements to the Marten projection model and the Wolverine aggregate handler workflow to make the archiving more accessible. The biggest issue has always been in how to handle the database migration model for this feature to convert brownfield applications
    • Wolverine 3.0
      • Try to eliminate the hard dependency on Lamar as the IoC container for Wolverine. Most people don’t care, but the folks who do care really don’t like that. So far from my research it looks like the answer is going to be supporting the built in .NET DI container or Lamar with the current Wolverine model — and we can maybe think about supporting other IoC containers with a step back in the runtime optimizations that Wolverine can do today with Lamar. I think it’s quickly coming to the point where all other IoC libraries besides the built in ServiceProvider container from Microsoft die off — even though there are still plenty of areas where that container is lacking compared to alternatives. Alas.
      • Try to apply the Wolverine error handling policies that today only work for Wolverine message handlers to HTTP endpoints

    Critter Stack Pro

    The Marten & Wolverine community is helping Babu, Jeffry Gonzalez & I brainstorm ideas for the future “Critter Stack Pro” suite of commercial add on tools. The goal is to (make money) make the “Critter Stack” be much more manageable in production environments, help troubleshoot production support issues, heal the system from runtime problems, and understand resource utilization. We don’t have the exact roadmap or exact technical approach locked down yet.

    Right now that looks like:

    • A headless library to better distribute Marten projections and subscriptions across a running cluster of processes. This is “ready for testing” by a JasperFx customer
    • A management console application that will be offered both as an ASP.Net Core add on library for a single application or distributed as a standalone Docker image for managing multiple systems from one console
      • Analyze system configuration
      • Manage Wolverine’s “dead letter queue” for messages, including the ability to replay messages
      • Some integration with Open Telemetry and metrics data emitted from Marten and/or Wolverine applications, probably at a summary level with navigation to the “real” observability platform (Prometheus? Grafana? Something totally different?)
      • Management for Marten Asynchronous Projections and Subscriptions
        • Performance information
        • Triggering rebuilds or replays
        • Pausing/restarting projections or subscriptions
      • Tenant Management
        • Dynamically add or remove tenant databases
        • Pause tenants
        • Understand resource utilization and performance on a tenant by tenant basis
      • Marten Event Store Explorer — and we’re collecting several ideas for this
      • Wolverine Message Processing Explorer — ditto
      • Wolverine Scheduled Message Dashboard

    My fervent hope is that this tooling will be demonstrable for friendly early adopters at the end of the 2nd quarter, and looking good in the 4th quarter to try to make a serious push for sales in the all important 1st quarter of next year.

    And Beyond!

    I’m very interested in porting just the event store functionality from Marten to a new library targeting SQL Server as the backing store. The goal here would be to give it the same Wolverine support as the existing Marten functionality. This would be pending some of the Marten projection model stabilizing up above.

    Maybe adding CosmosDb and/or DynamoDb support to Wolverine.

    And who knows? It’s likely something I’m not even aware of now will be the highest priority in the 3rd and 4th quarters!

    Critter Stack Improvements for Event Driven Architecture

    JasperFx Software is open for business and offering consulting services (like helping you craft modular monolith strategies!) and support contracts for both Marten and Wolverine so you know you can feel secure taking a big technical bet on these tools and reap all the advantages they give for productive and maintainable server side .NET development.

    As a follow on post from First Class Event Subscriptions in Marten last week, let’s introduce Wolverine into the mix for end to end Event Driven Architecture approaches. Using Wolverine’s new Event Subscriptions model, “Critter Stack” systems can automatically process Marten event data with Wolverine message handlers:

    If all we want to do is publish Marten event data through Wolverine’s message publishing (which remember, can be either to local queues or external message brokers), we have this simple recipe:

    using var host = await Host.CreateDefaultBuilder()
        .UseWolverine(opts =>
        {
            opts.Services
                .AddMarten()
                
                // Just pulling the connection information from 
                // the IoC container at runtime.
                .UseNpgsqlDataSource()
                
                // You don't absolutely have to have the Wolverine
                // integration active here for subscriptions, but it's
                // more than likely that you will want this anyway
                .IntegrateWithWolverine()
                
                // The Marten async daemon most be active
                .AddAsyncDaemon(DaemonMode.HotCold)
                
                // This would attempt to publish every non-archived event
                // from Marten to Wolverine subscribers
                .PublishEventsToWolverine("Everything")
                
                // You wouldn't do this *and* the above option, but just to show
                // the filtering
                .PublishEventsToWolverine("Orders", relay =>
                {
                    // Filtering 
                    relay.FilterIncomingEventsOnStreamType(typeof(Order));
    
                    // Optionally, tell Marten to only subscribe to new
                    // events whenever this subscription is first activated
                    relay.Options.SubscribeFromPresent();
                });
        }).StartAsync();
    

    First off, what’s a “subscriber?” That would mean any event that Wolverine recognizes as having:

    • A local message handler in the application for the specific event type, which would effectively direct Wolverine to publish the event data to a local queue
    • A local message handler in the application for the specific IEvent<T> type, which would effectively direct Wolverine to publish the event with its IEvent Marten metadata wrapper to a local queue
    • Any event type where Wolverine can discover subscribers through routing rules

    All the Wolverine subscription is doing is effectively calling IMessageBus.PublishAsync() against the event data or the IEvent<T> wrapper. You can make the subscription run more efficiently by applying event or stream type filters for the subscription.

    If you need to do a transformation of the raw IEvent<T> or the internal event type to some kind of external event type for publishing to external systems when you want to avoid directly coupling other subscribers to your system’s internals, you can accomplish that by just building a message handler that does the transformation and publishes a cascading message like so:

    public record OrderCreated(string OrderNumber, Guid CustomerId);
    
    // I wouldn't use this kind of suffix in real life, but it helps
    // document *what* this is for the sample in the docs:)
    public record OrderCreatedIntegrationEvent(string OrderNumber, string CustomerName, DateTimeOffset Timestamp);
    
    // We're going to use the Marten IEvent metadata and some other Marten reference
    // data to transform the internal OrderCreated event
    // to an OrderCreatedIntegrationEvent that will be more appropriate for publishing to
    // external systems
    public static class InternalOrderCreatedHandler
    {
        public static Task<Customer?> LoadAsync(IEvent<OrderCreated> e, IQuerySession session,
            CancellationToken cancellationToken)
            => session.LoadAsync<Customer>(e.Data.CustomerId, cancellationToken);
        
        
        public static OrderCreatedIntegrationEvent Handle(IEvent<OrderCreated> e, Customer customer)
        {
            return new OrderCreatedIntegrationEvent(e.Data.OrderNumber, customer.Name, e.Timestamp);
        }
    }
    

    Process Events as Messages in Strict Order

    In some cases you may want the events to be executed by Wolverine message handlers in strict order. With the recipe below:

    using var host = await Host.CreateDefaultBuilder()
        .UseWolverine(opts =>
        {
            opts.Services
                .AddMarten(o =>
                {
                    // This is the default setting, but just showing
                    // you that Wolverine subscriptions will be able
                    // to skip over messages that fail without
                    // shutting down the subscription
                    o.Projections.Errors.SkipApplyErrors = true;
                })
    
                // Just pulling the connection information from 
                // the IoC container at runtime.
                .UseNpgsqlDataSource()
    
                // You don't absolutely have to have the Wolverine
                // integration active here for subscriptions, but it's
                // more than likely that you will want this anyway
                .IntegrateWithWolverine()
                
                // The Marten async daemon most be active
                .AddAsyncDaemon(DaemonMode.HotCold)
                
                // Notice the allow list filtering of event types and the possibility of overriding
                // the starting point for this subscription at runtime
                .ProcessEventsWithWolverineHandlersInStrictOrder("Orders", o =>
                {
                    // It's more important to create an allow list of event types that can be processed
                    o.IncludeType<OrderCreated>();
    
                    // Optionally mark the subscription as only starting from events from a certain time
                    o.Options.SubscribeFromTime(new DateTimeOffset(new DateTime(2023, 12, 1)));
                });
        }).StartAsync();
    

    In this recipe, Marten & Wolverine are working together to call IMessageBus.InvokeAsync() on each event in order. You can use both the actual event type (OrderCreated) or the wrapped Marten event type (IEvent<OrderCreated>) as the message type for your message handler.

    In the case of exceptions from processing the event with Wolverine:

    1. Any built in “retry” error handling will kick in to retry the event processing inline
    2. If the retries are exhausted, and the Marten setting for StoreOptions.Projections.Errors.SkipApplyErrors is true, Wolverine will persist the event to its PostgreSQL backed dead letter queue and proceed to the next event. This setting is the default with Marten when the daemon is running continuously in the background, but false in rebuilds or replays
    3. If the retries are exhausted, and SkipApplyErrors = false, Wolverine will tell Marten to pause the subscription at the last event sequence that succeeded

    Custom, Batched Subscriptions

    The base type for all Wolverine subscriptions is the Wolverine.Marten.Subscriptions.BatchSubscription class. If you need to do something completely custom, or just to take action on a batch of events at one time, subclass that type. Here is an example usage where I’m using event carried state transfer to publish batches of reference data about customers being activated or deactivated within our system:

    public record CompanyActivated(string Name);
    
    public record CompanyDeactivated();
    
    public record NewCompany(Guid Id, string Name);
    
    // Message type we're going to publish to external
    // systems to keep them up to date on new companies
    public class CompanyActivations
    {
        public List<NewCompany> Additions { get; set; } = new();
        public List<Guid> Removals { get; set; } = new();
    
        public void Add(Guid companyId, string name)
        {
            Removals.Remove(companyId);
            
            // Fill is an extension method in JasperFx.Core that adds the 
            // record to a list if the value does not already exist
            Additions.Fill(new NewCompany(companyId, name));
        }
    
        public void Remove(Guid companyId)
        {
            Removals.Fill(companyId);
    
            Additions.RemoveAll(x => x.Id == companyId);
        }
    }
    
    public class CompanyTransferSubscription : BatchSubscription
    {
        public CompanyTransferSubscription() : base("CompanyTransfer")
        {
            IncludeType<CompanyActivated>();
            IncludeType<CompanyDeactivated>();
        }
    
        public override async Task ProcessEventsAsync(EventRange page, ISubscriptionController controller, IDocumentOperations operations,
            IMessageBus bus, CancellationToken cancellationToken)
        {
            var activations = new CompanyActivations();
            foreach (var e in page.Events)
            {
                switch (e)
                {
                    // In all cases, I'm assuming that the Marten stream id is the identifier for a customer
                    case IEvent<CompanyActivated> activated:
                        activations.Add(activated.StreamId, activated.Data.Name);
                        break;
                    case IEvent<CompanyDeactivated> deactivated:
                        activations.Remove(deactivated.StreamId);
                        break;
                }
            }
            
            // At the end of all of this, publish a single message
            // In case you're wondering, this will opt into Wolverine's
            // transactional outbox with the same transaction as any changes
            // made by Marten's IDocumentOperations passed in, including Marten's
            // own work to track the progression of this subscription
            await bus.PublishAsync(activations);
        }
    }
    

    And the related code to register this subscription:

    using var host = await Host.CreateDefaultBuilder()
        .UseWolverine(opts =>
        {
            opts.UseRabbitMq(); 
            
            // There needs to be *some* kind of subscriber for CompanyActivations
            // for this to work at all
            opts.PublishMessage<CompanyActivations>()
                .ToRabbitExchange("activations");
            
            opts.Services
                .AddMarten()
    
                // Just pulling the connection information from 
                // the IoC container at runtime.
                .UseNpgsqlDataSource()
                
                .IntegrateWithWolverine()
                
                // The Marten async daemon most be active
                .AddAsyncDaemon(DaemonMode.HotCold)
    
                                    
                // Register the new subscription
                .SubscribeToEvents(new CompanyTransferSubscription());
        }).StartAsync();
    

    Summary

    The feature set shown here has been a very long planned set of capabilities to truly extend the “Critter Stack” into the realm of supporting Event Driven Architecture approaches from soup to nuts. Using the Wolverine subscriptions automatically gets you support to publish Marten events to any transport supported by Wolverine itself, and does so in a much more robust way than you can easily roll by hand like folks did previously with Marten’s IProjection interface. I’m currently helping a JasperFx Software client utilize this functionality for data exchange that has strict ordering and at least once delivery guarantees.

    First Class Event Subscriptions in Marten

    This feature has been planned for Marten for years, but finally happened this month because a JasperFx Software client had a complicated multi-tenanted integration need for this as part of a complicated multi-tenanted and order sensitive data integration.

    Marten recently (these samples are pulled from Marten 7.9) got a first class “event subscription” feature that allows users to take action upon events being appended to Marten’s event store in strict sequential order in a background process. While you’ve long been able to integrate Marten with other systems by using Marten’s older projection model, the newer subscription model is leaner and more efficient for background processing.

    Before I get to “what” it is, let’s say that you need to carry out some kind of background processing on these events as they are captured? For example, maybe you need to:

    • Publish events to an external system as some kind of integration?
    • Carry out background processing based on a captured event
    • Build a view representation of the events in something outside of the current PostgreSQL database, like maybe an Elastic Search view for better searching

    With this recently added feature, you can utilize Marten’s ISubscription model that runs within Marten’s async daemon subsystem to “push” events into your subscriptions as events flow into your system. Note that this is a background process within your application, and happen in a completely different thread than the initial work of appending and saving events to the Marten event storage.

    Subscriptions will always be an implementation of the ISubscription interface shown below:

    /// <summary>
    /// Basic abstraction for custom subscriptions to Marten events through the async daemon. Use this in
    /// order to do custom processing against an ordered stream of the events
    /// </summary>
    public interface ISubscription : IAsyncDisposable
    {
        /// <summary>
        /// Processes a page of events at a time
        /// </summary>
        /// <param name="page"></param>
        /// <param name="controller">Use to log dead letter events that are skipped or to stop the subscription from processing based on an exception</param>
        /// <param name="operations">Access to Marten queries and writes that will be committed with the progress update for this subscription</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task<IChangeListener> ProcessEventsAsync(EventRange page, ISubscriptionController controller,
            IDocumentOperations operations,
            CancellationToken cancellationToken);
    }
    

    So far, the subscription model gives you these abilities:

    • Access to the Marten IDocumentOperations service that is scoped to the processing of a single page and can be used to either query additional data or to make database writes within the context of the same transaction that Marten will use to record the current progress of the subscription to the database
    • Error handling abilities via the ISubscriptionController interface argument that can be used to record events that were skipped by the subscription or to completely stop all further processing
    • By returning an IChangeListener, the subscription can be notified right before and right after Marten commits the database transaction for any changes including recording the current progress of the subscription for the current page. This was done purposely to enable transactional outbox approaches like the one in Wolverine. See the async daemon diagnostics for more information.
    • The ability to filter the event types or stream types that the subscription is interested in as a way to greatly optimize the runtime performance by preventing Marten from having to fetch events that the subscription will not process
    • The ability to create the actual subscription objects from the application’s IoC container when that is necessary
    • Flexible control over where or when the subscription starts when it is first applied to an existing event store
    • Some facility to “rewind and replay” subscriptions

    To make this concrete, here’s the simplest possible subscription you can make to simply write out a console message for every event:

    public class ConsoleSubscription: ISubscription
    {
        public Task<IChangeListener> ProcessEventsAsync(EventRange page, ISubscriptionController controller, IDocumentOperations operations,
            CancellationToken cancellationToken)
        {
            Console.WriteLine($"Starting to process events from {page.SequenceFloor} to {page.SequenceCeiling}");
            foreach (var e in page.Events)
            {
                Console.WriteLine($"Got event of type {e.Data.GetType().NameInCode()} from stream {e.StreamId}");
            }
    
            // If you don't care about being signaled for
            return Task.FromResult(NullChangeListener.Instance);
        }
    
        public ValueTask DisposeAsync()
        {
            return new ValueTask();
        }
    }
    

    And to register that with our Marten store:

    var builder = Host.CreateApplicationBuilder();
    builder.Services.AddMarten(opts =>
        {
            opts.Connection(builder.Configuration.GetConnectionString("marten"));
    
            // Because this subscription has no service dependencies, we
            // can use this simple mechanism
            opts.Events.Subscribe(new ConsoleSubscription());
    
            // Or with additional configuration like:
            opts.Events.Subscribe(new ConsoleSubscription(), s =>
            {
                s.SubscriptionName = "Console"; // Override Marten's naming
                s.SubscriptionVersion = 2; // Potentially version as an all new subscription
    
                // Optionally create an allow list of
                // event types to subscribe to
                s.IncludeType<InvoiceApproved>();
                s.IncludeType<InvoiceCreated>();
    
                // Only subscribe to new events, and don't try
                // to apply this subscription to existing events
                s.Options.SubscribeFromPresent();
            });
        })
        .AddAsyncDaemon(DaemonMode.HotCold);
    
    using var host = builder.Build();
    await host.StartAsync();
    

    Here’s a slightly more complicated sample that publishes events to a configured Kafka topic:

    public class KafkaSubscription: SubscriptionBase
    {
        private readonly KafkaProducerConfig _config;
    
        public KafkaSubscription(KafkaProducerConfig config)
        {
            _config = config;
    
            SubscriptionName = "Kafka";
    
            // Access to any or all filtering rules
            IncludeType<InvoiceApproved>();
    
            // Fine grained control over how the subscription runs
            // in the async daemon
            Options.BatchSize = 1000;
            Options.MaximumHopperSize = 10000;
    
            // Effectively run as a hot observable
            Options.SubscribeFromPresent();
        }
    
        // The daemon will "push" a page of events at a time to this subscription
        public override async Task<IChangeListener> ProcessEventsAsync(
            EventRange page,
            ISubscriptionController controller,
            IDocumentOperations operations,
            CancellationToken cancellationToken)
        {
            using var kafkaProducer =
                new ProducerBuilder<string, string>(_config.ProducerConfig).Build();
    
            foreach (var @event in page.Events)
            {
                await kafkaProducer.ProduceAsync(_config.Topic,
                    new Message<string, string>
                    {
                        // store event type name in message Key
                        Key = @event.Data.GetType().Name,
                        // serialize event to message Value
                        Value = JsonConvert.SerializeObject(@event.Data)
                    }, cancellationToken);
    
            }
    
            // We don't need any kind of callback, so the nullo is fine
            return NullChangeListener.Instance;
        }
    
    }
    
    // Just assume this is registered in your IoC container
    public class KafkaProducerConfig
    {
        public ProducerConfig? ProducerConfig { get; set; }
        public string? Topic { get; set; }
    }
    

    This time, it’s requiring IoC services injected through its constructor, so we’re going to use this mechanism to add it to Marten:

    var builder = Host.CreateApplicationBuilder();
    builder.Services.AddMarten(opts =>
        {
            opts.Connection(builder.Configuration.GetConnectionString("marten"));
        })
        // Marten also supports a Scoped lifecycle, and quietly forward Transient
        // to Scoped
        .AddSubscriptionWithServices<KafkaSubscription>(ServiceLifetime.Singleton, o =>
        {
            // This is a default, but just showing what's possible
            o.IncludeArchivedEvents = false;
    
            o.FilterIncomingEventsOnStreamType(typeof(Invoice));
    
            // Process no more than 10 events at a time
            o.Options.BatchSize = 10;
        })
        .AddAsyncDaemon(DaemonMode.HotCold);
    
    using var host = builder.Build();
    await host.StartAsync();
    

    But there’s more!

    The subscriptions run with Marten’s async daemon process, which just got a world of improvements in the Marten V7 release, including the ability to distribute work across running nodes in your application at runtime.

    I didn’t show it in this blog post, but there are also facilities to configure whether a new subscription will start by working through all the events from the beginning of the system, or whether the subscription should start from the current sequence of the event store, or even go back to an explicitly stated sequence or timestamp, then play forward. Marten also has support — similar to its projection rebuild functionality — to rewind and replay subscriptions.

    Wolverine already has specific integrations to utilize Marten event subscriptions to process events with Wolverine message handlers, or to forward events as messages through Wolverine publishing (Kafka? Rabbit MQ? Azure Service Bus?), or to do something completely custom with batches of events at a time (which I’ll demonstrate in the next couple weeks). I’ll post about that soon after that functionality gets fully documented with decent examples.

    Lastly, and this is strictly in the hopefully near term future, there will be specific support for Marten subscriptions in the planned “Critter Stack Pro” add on product to Marten & Wolverine to:

    • Distribute subscription work across running nodes within your system — which actually exists in a crude, but effective form, and will absolutely be in Critter Stack Pro V1!
    • User interface monitoring and control pane to manually turn on and off subscriptions, review performance, and manually “rewind” subscriptions

    Hopefully much more on this soon. It’s taken much longer than I’d hoped, but it’s still coming.