Welcoming Babu Annamalai to JasperFx Software

Starting today, Babu Annamalai is taking a larger role at JasperFx Software (LLC) to help expand our support coverage to just about every time zone on the planet. Babu is a long time member of the Marten and now Critter Stack core team. In addition to some large contributions like the Partial API in Marten and smoothing out database migrations, he’s been responsible for most of our DevOps support and documentation websites that helps keep the Critter Stack moving forward.

A little more about Babu:

Babu has over 28 years of experience, excelling in technology and product management roles within renowned enterprise firms. His expertise lies in crafting cutting-edge products and solutions customised for the ever-evolving domain of investment management and research. Co-maintainer of Marten. Owns and manages .NET OSS libraries ReverseMarkdown.Net and MysticMind.PostgresEmbed. Drawing from his wealth of knowledge, he recently embarked on a thrilling entrepreneurial journey, establishing Radarleaf Technologies, providing top-notch consultancy and bespoke software development services.

Welcome aboard Babu!

The Critter Stack Gets Even Better at Testing

My internal code name for one of the new features I’m describing is “multi-stage tracked sessions” which somehow got me thinking of the ZZ Top song “Stages” and their Afterburner album because the sound track for getting this work done this week. Not ZZ Top’s best stuff, but there’s still some bangers on it, or at least *I* loved how it sounded on my Dad’s old phonograph player when I was a kid. For what it’s worth, my favorite ZZ Top albums cover to cover are Degüello and their La Futura comeback album.

I was heavily influenced by Extreme Programming in my early career and that’s made me have a very deep appreciation for the quality of “Testability” in the development tools I use and especially for the tools like Marten and Wolverine that I work on. I would say that one of the differentiators for Wolverine over other .NET messaging libraries and application frameworks is its heavy focus and support for automated testing of your application code.

The Critter Stack community released Marten 8.14 and Wolverine 5.1 today with some significant improvements to our testing support. These new features mostly originated from my work with JasperFx Software clients that give me a first hand look into what kinds of challenges our users hit automating tests that involve multiple layers of asynchronous behavior.

Stubbed Message Handlers in Wolverine

The first improvement is Wolverine getting the ability to let you temporarily apply stubbed message handlers to a bootstrapped application in tests. The key driver for this feature is teams that take advantage of Wolverine’s request/reply capabilities through messaging.

Jumping into an example, let’s say that your system interacts with another service that estimates delivery costs for ordering items. At some point in the system you might reach out through a request/reply call in Wolverine to estimate an item delivery before making a purchase like this code:

// This query message is normally sent to an external system through Wolverine
// messaging
public record EstimateDelivery(int ItemId, DateOnly Date, string PostalCode);

// This message type is a response from an external system
public record DeliveryInformation(TimeOnly DeliveryTime, decimal Cost);

public record MaybePurchaseItem(int ItemId, Guid LocationId, DateOnly Date, string PostalCode, decimal BudgetedCost);
public record MakePurchase(int ItemId, Guid LocationId, DateOnly Date);
public record PurchaseRejected(int ItemId, Guid LocationId, DateOnly Date);

public static class MaybePurchaseHandler
{
    public static Task<DeliveryInformation> LoadAsync(
        MaybePurchaseItem command, 
        IMessageBus bus, 
        CancellationToken cancellation)
    {
        var (itemId, _, date, postalCode, budget) = command;
        var estimateDelivery = new EstimateDelivery(itemId, date, postalCode);
        
        // Let's say this is doing a remote request and reply to another system
        // through Wolverine messaging
        return bus.InvokeAsync<DeliveryInformation>(estimateDelivery, cancellation);
    }
    
    public static object Handle(
        MaybePurchaseItem command, 
        DeliveryInformation estimate)
    {

        if (estimate.Cost <= command.BudgetedCost)
        {
            return new MakePurchase(command.ItemId, command.LocationId, command.Date);
        }

        return new PurchaseRejected(command.ItemId, command.LocationId, command.Date);
    }
}

And for a little more context, the EstimateDelivery message will always be sent to an external system in this configuration:

var builder = Host.CreateApplicationBuilder();
builder.UseWolverine(opts =>
{
    opts
        .UseRabbitMq(builder.Configuration.GetConnectionString("rabbit"))
        .AutoProvision();

    // Just showing that EstimateDelivery is handled by
    // whatever system is on the other end of the "estimates" queue
    opts.PublishMessage<EstimateDelivery>()
        .ToRabbitQueue("estimates");
});

In testing scenarios, maybe the external system isn’t available at all, or it’s just much more challenging to run tests that also include the external system, or maybe you’d just like to write more isolated tests against your service’s behavior before even trying to integrate with the other system (my personal preference anyway). To that end we can now stub the remote handling like this:

public static async Task try_application(IHost host)
{
    host.StubWolverineMessageHandling<EstimateDelivery, DeliveryInformation>(
        query => new DeliveryInformation(new TimeOnly(17, 0), 1000));

    var locationId = Guid.NewGuid();
    var itemId = 111;
    var expectedDate = new DateOnly(2025, 12, 1);
    var postalCode = "78750";

    var maybePurchaseItem = new MaybePurchaseItem(itemId, locationId, expectedDate, postalCode,
        500);
    
    var tracked =
        await host.InvokeMessageAndWaitAsync(maybePurchaseItem);
    
    // The estimated cost from the stub was more than we budgeted
    // so this message should have been published
    
    // This line is an assertion too that there was a single message
    // of this type published as part of the message handling above
    var rejected = tracked.Sent.SingleMessage<PurchaseRejected>();
    rejected.ItemId.ShouldBe(itemId);
    rejected.LocationId.ShouldBe(locationId);
}

After calling making this call:

        host.StubWolverineMessageHandling<EstimateDelivery, DeliveryInformation>(
            query => new DeliveryInformation(new TimeOnly(17, 0), 1000));

Calling this from our Wolverine application:

        // Let's say this is doing a remote request and reply to another system
        // through Wolverine messaging
        return bus.InvokeAsync<DeliveryInformation>(estimateDelivery, cancellation);

Will use the stubbed logic we registered. This is enabling you to use fake behavior for difficult to use external services.

For the next test, we can completely remove the stub behavior and revert back to the original configuration like this:

public static void revert_stub(IHost host)
{
    // Selectively clear out the stub behavior for only one message
    // type
    host.WolverineStubs(stubs =>
    {
        stubs.Clear<EstimateDelivery>();
    });
    
    // Or just clear out all active Wolverine message handler
    // stubs
    host.ClearAllWolverineStubs();
}

There’s a bit more to the feature you can read about in our documentation, but hopefully you can see right away how this can be useful for effectively stubbing out the behavior of external systems through Wolverine in tests.

And yes, some older .NET messaging frameworks already had *this* feature and it’s been occasionally requested from Wolverine, so I’m happy to say we have this important and useful capability.

Forcing Marten’s Asynchronous Daemon to “Catch Up”

Marten has had the IDocumentStore.WaitForNonStaleProjectionDataAsync(timeout) API (see the documentation for an example) for quite awhile that lets you pause a test while any running asynchronous projections or subscriptions run and catch up to wherever the event store “high water mark” was when you originally called the method. Hopefully, this lets ongoing background work proceed until the point where it’s now safe for you to proceed to the “Assert” part of your automated tests. As a convenience, this API is also available through extension methods on both IHost and IServiceProvider.

We’ve recently invested time into this API to make it provide much more contextual information about what’s happening asynchronously if the “waiting” does not complete. Specifically, we’ve made the API throw an exception that embeds a table of where every asynchronous projection or subscription ended up at compared to the event store’s “high water mark” (the highest sequential identifier assigned to a persisted event in the database). In this last release we made sure that that textual table also shows any projections or subscriptions that never recorded any process with a sequence of “0” so you can see what did or didn’t happen. We have also changed the API to record any exceptions thrown by the asynchronous daemon (serialization errors? application errors from *your* projection code? database errors?) and have those exceptions piped out in the failure messages when the “WaitFor” API does not successfully complete.

Okay, with all of that out of the way, we also added a completely new, slightly alternative for the asynchronous daemon that just forces the daemon to quickly process all outstanding events through every asynchronous projection or subscription right this second and throw up any exceptions that it encounters. We call this the “catch up” API:

        using var daemon = await theStore.BuildProjectionDaemonAsync();
        await daemon.CatchUpAsync(CancellationToken.None);

This mode is faster and hopefully more reliable than WaitFor***** because it’s happening inline and shortcuts a lot of the normal asynchronous polling and messaging within the normal daemon processing.

There’s also an IHost.CatchUpAsync() or IServiceProvider.CatchUpAsync() convenience method for test usage as well.

Multi Stage Tracked Sessions

I’m obviously biased, but I’d say that Wolverine’s tracked session capability is a killer feature that makes Wolverine stand apart from other messaging tools in the .NET ecosystem and it goes a long way toward making integration testing through Wolverine asynchronous messaging be productive and effective.

But, what if you have a testing scenario where you:

  1. Carry out some kind of action (an HTTP request invoked through Alba? publishing a message internally within your application?) that leads to messages being published in Wolverine that might in turn lead to even more messages getting published within your Wolverine system or other tracked systems
  2. Along the way, handling one or more commands leads to events being appended to a Marten event store
  3. An asynchronously executing projection might append other events or publish messages in Marten’s RaiseSideEffects() capability or an event subscription might in turn publish other Wolverine messages that start up an all new cycle of “when is the system really done with all the work it has started.”

That might sound a little bit contrived, but it reflects real world scenarios I’ve discussed with multiple JasperFx clients in just the past couple weeks. With their help and some input from the community, we came up with this new extension to Wolverine’s “tracked sessions” to also track and wait for work spawned by Marten. Consider this bit of code from the tests for this feature:

var tracked = await _host.TrackActivity()
    
    // This new helper just resets the main Marten store
    // Equivalent to calling IHost.ResetAllMartenDataAsync()
    .ResetAllMartenDataFirst()
    
    .PauseThenCatchUpOnMartenDaemonActivity(CatchUpMode.AndResumeNormally)
    .InvokeMessageAndWaitAsync(new AppendLetters(id, ["AAAACCCCBDEEE", "ABCDECCC", "BBBA", "DDDAE"]));


To add some context, handling the AppendLetters command message appends events to a Marten stream and possibly cascades another Wolverine message that also appends events. At the same time, there are asynchronous projections and event subscriptions that will publish messages through Wolverine as they run. We can now make this kind of testing scenario much more feasible and hopefully reliable (async heavy tests are super prone to being blinking tests) through the usage of the PauseThenCatchUpOnMartenDaemonActivity() extension method from the Wolverine.Marten library.

In the bit of test code above, that API is:

  1. Registering a “before” action to pause all async daemon activity before executing the “Act” part of the tracked session which in this case is calling IMessageBus.InvokeAsync() against an AppendLetters command
  2. Registering a 2nd stage of the tracked session

When this tracked session is executed, the following sequence happens:

  1. The tracked session calls Marten’s ResetAllMartenDataAsync() in the main DocumentStore for the application to effectively rewind the database state down to your defined initial state
  2. IMessageBus.InvokeAsync(AppendLetters) is called as the actual “execution” of the tracked session
  3. The tracked session is watching everything going on with Wolverine messaging and waits until all “cascaded” messages are complete — and that is recursive. Basically, the tracked session waits until all subsequent messaging activity in the Wolverine application is complete
  4. The 2nd stage we registered to “CatchUp” means the tracked session calls Marten’s new “CatchUp” API to force all asynchronous projections and event subscriptions in the system to immediately process all persisted events. This also restarts the tracked session monitoring of any Wolverine messaging activity so that this stage will only complete when all detected Wolverine messaging activity is completed.

By using this new capability inside of the older tracked session feature, we’re able to effectively test from the original message input through any subsequent messages triggered by the original message through asynchronous Marten behavior caused by the original messages which might in turn publish yet more messages through Wolverine.

Long story short, this gives us a reliable way to know when the “Act” part of a test is actually complete and proceed to the “Assert” portion of a test. Moreover, this new feature also tries really hard to bring out some visibility into the asynchronous Marten behavior and the second stage messaging behavior in the case of test failures.

Summary

None of this is particularly easy conceptually, and it’s admittedly here because of relatively hard problems in test automation that you might eventually run into. Selfishly, I needed to get these new features into the hands of a client tomorrow and ran out of time to better document these new features, so you get this braindump blog post.

If it helps, I’m going to talk through these new capabilities a bit more in our next Critter Stack live stream tomorrow (Nov. 6th):

Wolverine Does More to Simplify Server Side Code

Just to set myself up with some pressure to perform, let me hype up a live stream on Wolverine I’m doing later this week!

I’m doing a live stream on Thursday afternoon (U.S. friendly this time) entitled Vertical Slices the Critter Stack Way based on a fun, meandering talk I did for Houston DNUG and an abbreviated version at Commit Your Code last month.

So, yes, it’s technically about the “Vertical Slice Architecture” in general and specifically with Marten and Wolverine, but more importantly, the special sauce in Wolverine that does more — in my opinion of course — than any other server side .NET application framework to simplify your code and improve testability. In the live stream, I’m going to discuss:

  • A little bit about how I think modern layered architecture approaches and “Ports and Adapters” style approaches can sometimes lead to poor results over time
  • The qualities of a code base that I think are most important (the ability to reason about the behavior of the code, testability of all sorts, ease of iteration, and modularity)
  • How Wolverine’s low code ceremony improves outcomes and the qualities I listed above by reducing layering and shrinking your code into a much tighter vertical slice approach so you can actually see what your system does later on
  • Adopting Wolverine’s idiomatic “A-Frame Architecture” approach and “imperative shell, functional core” thinking to improve testability
  • A sampling of the ways that Wolverine can hugely simplify data access in simpler scenarios and how it can help you keep more complicated data access much closer to behavioral code so you can actually reason about the cause and effects between those two things. And all of that while happily letting you leverage every bit of power in whatever your database or data access tooling happens to be. Seriously, layering approaches and abstractions that obfuscate the database technologies and queries within your system are a very common source of poor system performance in Onion/Clean Architecture approaches.
  • Using Wolverine.HTTP as an alternative AspNetCore Endpoint model and why that’s simpler in the end than any kind of “Mediator” tooling inside of MVC Core or Minimal API
  • Wolverine’s adaptive approach to middleware
  • The full “Critter Stack” combination with Marten and how that leads to arguably the simplest and cleanest code for CQRS command handlers on the planet
  • Wolverine’s goodies for the majority of .NET devs using the venerable EF Core tooling as well

If you’ve never heard of Wolverine or haven’t really paid much attention to it yet, I’m most certainly inviting you to the live stream to give it a chance. If you’ve blown Wolverine off in the past as “yet another messaging tool in .NET,” come find out why that is most certainly not the full story because Wolverine will do much more for you within your application code than other, mere messaging frameworks in .NET or even any of the numerous “Mediator” tools floating around.

Wolverine 5 and Modular Monoliths

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:

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 DbContext types 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 DbContext types 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.

Wolverine 5.0 is Here!

That’s of course supposed to be a 1992 Ford Mustang GT with the 5.0L V8 that high school age me thought was the coolest car I could imagine ever owning (I most certainly never did of course). Queue “Ice, Ice Baby” and sing “rolling, in my 5.0” in your head because here we go…

Wolverine 5.0 went live on Nuget earlier today after about three months of pretty intensive development from *20* different contributors with easily that many more folks having contributed to discussions and GitHub issues that helped get us here. I’m just not going to be able to list everyone, so let me just thank the very supportive Wolverine community, the 19 other contributors, and the JasperFx clients who contributed to this release.

This release came closely on the heels of Wolverine 4.0 earlier this year, with the primary reasons for a new major version release being:

  • A big change in the internals as we replaced the venerable TPL DataFlow library with the System.Threading.Channels library in every place that Wolverine uses in memory queueing. We did this as a precursor to a hugely important new feature commissioned by a JasperFx Software client (who really needs to get that feature in for their “scale out” so it was definitely about time I got this out today).
  • Some breaking API changes in the “publinternals” of Wolverine to support “CritterWatch”, our long planned and I promise finally in real development add on tooling for Critter Stack observability and management

With that being said, the top line new changes to Wolverine that I’ll be trying to blog about next week are:

For a partial list of significant, smaller improvements:

  • Wolverine can utilize Marten batch querying for the declarative data access, and that includes working with multiple Marten event streams in one logical operation. This is part of the Critter Stack’s response to the “Dynamic Consistency Boundary” idea from some of the commercial event sourcing tools
  • You can finally use strong typed identifiers with the “aggregate handler workflow”
  • An overhaul of the dead letter queue administration services that was part of our ongoing work for CritterWatch
  • A new tutorial for dealing with concurrency when building against Wolverine
  • Optimistic concurrency support for EF Core backed Sagas from the community
  • Ability to target multiple Azure Service Bus namespaces from a single application and improvements to using Azure Service Bus namespace per tenant
  • Improvements to Rabbit MQ for advanced usage

What’s Next?

As happens basically every time, several features that were planned for 5.0 and some significant open issues didn’t make the 5.0 cut. The bigger effort to optimize the cold start time for both Marten and Wolverine will hopefully happen later this year. I think the next minor point release will target some open issues around Wolverine.HTTP (multi-part uploads, actual content negotiation) and the Kafka transport. I would like to take a longer look sometime at how the CritterStack combination can better support operations that cross stream boundaries.

But in the meantime, I’m shifting to open Marten issues before hopefully spending a couple weeks trying to jump start CritterWatch development again.

I usually end these kinds of major release announcements with a link to Don’t Steal My Sunshine as an exhortation to hold off on reporting problems or asking for whatever didn’t make the release. After referring to “Ice, Ice Baby” in the preface to this and probably getting that bass line stuck in your head, here’s the song you want to hear now anyway — which I feel much less of after getting this damn release out:

Migrations the “Critter Stack” Way

I was the guest speaker today on the .NET Data Community Standup doing a talk on how the “Critter Stack” (Marten, Wolverine, and Weasel) support a style of database migrations and even configuration for messaging brokers that greatly reduces development time friction for more productive teams.

The general theme is “it should just work” so developers and testers can get their work done and even iterate on different approaches without having to spend much time fiddling with database or other infrastructure configuration.

And I also shared some hard lessons learned from previous OSS project failures that made the Critter Stack community so adamant that the default configurations “should just work.”

Last Sprint to Wolverine 5.0

Little update since the last check in on Wolverine 5.0. I think right now that Wolverine 5.0 hits by next Monday (October 6th). To be honest, besides documentation updates, the biggest work is just pushing more on the CritterWatch backend this week to see if that forces any breaking changes in the Wolverine internals.

What’s definitely “in” right now:

  • The “Partitioned Sequential Messaging” feature work that we previewed in a live stream
  • Big improvements and expansion to Wolverine’s interoperability story against NServiceBus, MassTransit, CloudEvents, and whatever custom interoperability folks need to do
  • A first class SignalR transport which is getting used heavily in our own CritterWatch work
  • A first class Redis messaging transport from the community
  • Modernization and upgrades to the GCP Pubsub transport
  • The ability to mix and match database storage with Wolverine for modular monoliths
  • A bit batch of optimization for the Marten integration including improvements for multi-stream operations as our response to the “Dynamic Boundary Consistency” idea from other tools
  • The utilization of System.Threading.Channels in place of the TPL DataFlow library

What’s unfortunately out:

  • Any effort to optimize the cold start times for Marten and Wolverine. Just a bandwidth problem, plus I think this can get done without breaking changes

And we’ll see:

  • Random improvements for Azure Service Bus and Kafka usage
  • HTTP improvements for content negotiation and multi-part uploads
  • Yet more improvements to the “aggregate handler workflow” with Marten to allow for yet more strong typed identifier usage

The items in the 3rd list don’t require any breaking changes, so could slide to Wolverine 5.1 if necessary.

All in all, I’d argue this turned out to be a big batch of improvements with very few breaking API changes and almost nothing that would impact the average user.

Some Thoughts on MS Pulling Back on their “Eventing Framework”

I’ll admit that I’d stopped paying attention quite awhile ago and didn’t even realize Microsoft was still considering building out their own “Eventing Framework” until everybody and their little brother started posting a link to their announcement about forgoing this effort today.

Here’s a very few thoughts from me about this, and I think for about the first time ever, I’m disallowing comments on this one to just spit this out and be done with it.

  • I thought that what they were proposing in terms of usability by basically trying to make it “Minimal API” for asynchronous messaging was not going to be very successful in complex systems. I get that their approach might have led to a low learning code for simple usage and there’s some appeal to having a common programming model with web development, but man, I think that would have severely limited that tooling in terms of what it helped you do to deal with application complexity or testability compared to existing tools in this space.
  • Specifically, I think that the Microsoft tooling teams have a blind spot sometimes about testability design in their application frameworks
  • I think this is a technical area where .NET is actually very rich in options and there’s actually a lot of existing innovation across our ecosystem already (Wolverine, NServiceBus, MassTransit, AkkaDotNet, Rebus, Brighter, Microsoft’s own Dapr for crying out loud). I did not believe that the proposed tooling from Microsoft in this case did anything to improve the ecosystem except for the inevitable folks who just don’t want to have any dependency on .NET technology that is not from Microsoft
  • I’m continuously shocked anytime something like this bubbles up how a seemingly large part of the .NET community is outright hostile to non-Microsoft tooling in .NET
  • I will 100% admit that I was concerned about my own Wolverine project being severely harmed by the MS offering at the same time believing quite fervently that Wolverine would long remain a far superior technical solution. The reality is that Microsoft tooling tends to quickly take the Oxygen out of the air for non-Microsoft tools regardless of relative quality or even suitability for real usage. You can absolutely compete with the Microsoft offerings on technical quality, but not in informational reach or community attention
  • If Microsoft had gone ahead with their tooling, I had every intention of being aggressive online to try to point out every possible area where Wolverine had advantages and I had no plans to just give up. My thought was to just lean in much, much harder to the greater Critter Stack as a full blown Event Sourcing solution where there is really nothing competitive to the Critter Stack in the rest of the .NET community (I said what I said) and certainly nothing from Microsoft themselves (yet)
  • I think it hurts the .NET ecosystem when Microsoft squelches community innovation and this is something I’ve never liked about the greater .NET community’s fixation on having official, Microsoft approved tooling.
  • One thing the Microsoft folks tried to sell people like me who lead asynchronous messaging projects is that they (MS) were really good at application frameworks, and we could all take dependencies on a new set of medium level messaging abstractions and core libraries for messaging. I wonder if what they meant is what are now the various Aspire plugins for Rabbit MQ or Azure Service Bus. I was also extremely dubious about all of that.
  • As someone else pointed out, do you really want one tool trying to be all things to all people because that’s a recipe for a bloated, unmaintainable tool
  • I think the Microsoft team was a bit naive about what they would have to build out and how many feature requests they would have gotten from folks wanting to ditch very mature tools like MassTransit. I really don’t believe that Microsoft would have resisted the demands from some elements of the community to grow the new things into something able to handle more complex requirements
  • I don’t know what to say about the people who flipped their lids over the MassTransit and MediatR commercialization plans. I think folks were drastically underestimating the value of those tools, the overhead in supporting those tools over time, and in complete denial about the practicality of rolling your own one off tools.
  • The idea that Microsoft is an infallible maintainer of their development tools is bonkers
  • Regardless, the Critter Stack is going down the “Open Core” model

As my mentor used to say at my first real software development job, I feel better now and thank you for listening.

And now, back to Wolverine 5 and CritterWatch for me…

Little Diary of How JasperFx Helps Our Clients

For any shops using the “Critter Stack” (Marten and Wolverine), JasperFx Software offers support contracts and custom consulting engagements in support of these tools — or really anything you might be doing on the server side with .NET as well. Something we’ve had some success with, especially lately, is positioning these “support” contracts as essentially having JasperFx on call for adhoc consulting beyond merely assisting with production issues or bugs.

Just to try to illustrate the value of these engagements, I thought it would be interesting to describe what JasperFx has done for clients in just the past 30 days — in very general terms with zero information about our client’s business domain of course.

In no particular order, we’ve:

  • Helped several clients with CI/CD related tasks around Wolverine or Marten’s code generation as a way to find problems faster and to optimize cold start times. Also fixed some issues with the codegen for one of our support clients. As always, I’d rather we never had any bugs, but we do try to stomp those out relatively quickly for our clients
  • Explained and worked through error handling strategies built into Wolverine with a client who was dealing with a dependency on an external service that has somewhat strict rate limiting
  • Planned out identity strategies for importing data from a legacy system into Marten and for interoperability with that legacy system for the time being
  • Jumped on a Zoom call to pair program with a client who needed to use some pretty advanced Wolverine middleware capabilities
  • Did a Zoom call with that same client to help them plan for future message broker usage. Most of our support work is done through Discord or Slack, but sometimes a real call is what you need to have more of discussion — especially when I think I need to ask the client several questions to better understand their needs and the context around their questions before firing off a quick answer.
  • Helped a client troubleshoot usage issue with Kafka
  • Added some improvements for Wolverine usage with F# for one of our first clients
  • Developed some new test automation support around scheduled message capabilities in Wolverine for one of our clients who is very aggressive in their integration test automation
  • Built a small feature in Marten to help optimize some upcoming work for a client using Marten projections. I won’t say that building new features is an official part of support contracts, but we will prioritize features for support clients.
  • Interacted with a client team to best utilize the Critter Stack “Aggregate Handler Workflow” approach as a way of streamlining their application code and maximizing their ability to unit test business logic. If you’ll buy into Wolverine idioms, you can build systems with much less code than the typical Clean/Onion Architecture approaches.
  • Assisted several clients with using the test automation support features backed into Wolverine, with quite a bit of pure consulting on how to be more successful with test automation efforts in .NET.
  • Conducted more Zoom calls to talk through Event Sourcing modeling questions for multiple clients. I’m a big believer in Event Sourcing, but it is a pretty new technique and architectural style, and it’s not necessarily a natural transition for folks who are very used to thinking and building in terms of relational databases. JasperFx can help!
  • We fielded plenty of questions about the best usage of projections in Marten
  • Helped a client try to optimize their experience with Kubernetes helpfully stopping and starting pods while the pods were quite busy with Marten and Wolverine work. That was fun. Not.
  • Talked through Wolverine usages and made some additional changes to Wolverine for a client who is using Wolverine as an in memory message bus for a modular monolith architecture.

And answering plenty of small questions about features or approaches that probably just amount to giving our clients peace of mind about what they were doing.

As I was compiling this, I noticed that there hasn’t been any recent support questions about multi-tenancy or concurrency lately. I’m going to take that as a sign that we’re very mature in those two areas!

I would hope the point I made here is that there’s quite a lot of value we can bring to your organization through an ongoing support contract and engagement with JasperFx Software. Certainly feel free to reach out to us at sales@jasperfx.net for any questions about how we could potentially help your shop!

While I do enjoy interacting with our clients and I most certainly love getting to make a living off of my own technical babies, anytime I do some outright shilling and promotion like this post, I’m a bit reminded of this (and I’m definitely the “Ray”):

Update on Wolverine 5 and CritterWatch

We’re targeting October 1st for the release of Wolverine 5.0. At this point, I think I’d like to say that we’re not going to be adding any new features to Wolverine 4.* except for JasperFx Software client needs. And also, not that I have any pride about this, I don’t think we’re going to address bugs in 4.* if those bugs do not impact many people.

To catch you up if you want:

In addition to the new features we originally envisioned, add in:

  • A messaging transport for Redis from the community — which might also lead to a Redis backed saga model some day too, but not for 5.0
  • Ongoing work to improve Wolverine’s capability to allow folks to mix and match persistent message stores in “modular monolith” applications
  • Working over some of the baked in Dead Letter Queue administration, which is being done in conjunction with ongoing “CritterWatch” work

I think we’re really close to the point where it’s time to play major release triage and push back any enhancements that wouldn’t require any breaking changes to the public API, so anything not yet done or at least started probably slides to a future 5.* minor release. The one exception might be trying to tackle the “cold start optimization.” The wild card in this is that I’m desperately trying to work through as much of the CritterWatch backend plumbing as possible right now as that work is 100% causing some changes and improvements to Wolverine 5.0

What about CritterWatch?

If you understand why the image above appears in this section, I would hope you’d feel some sympathy for me here:-)

I’ve been able to devote some serious time to CritterWatch the past couple weeks, and it’s starting to be “real” after all this time. Jeffry Gonzalez and I will be marrying up the backend and a real frontend in the next couple weeks and who knows, we might be able to demo something to early adopters in about a month or so. After Wolverine 5.0 is out, CritterWatch will be my and JasperFx’s primary technical focus the rest of the year.

Just to rehash, the MVP for CritterWatch is looking like:

  1. The basic shell and visualization of what your monitored Critter Stack applications are, including messaging
  2. Every possible thing you need to manage Dead Letter Queue messages in Wolverine — but I’d warn you that it’s focused on Wolverine’s database backed DLQ
  3. Monitoring and a control panel over Marten event projections and subscriptions and everything you need to keep those running smoothly in production