MediatR is Going Commercial! πŸ›‘ Top 4 FREE Alternatives to Use πŸš€

 MediatR has been a part of lots of software solutions ever since people started adopting the vertical architecture concept more. The vertical architecture which Jimmy Bogard made famous gave people a way to minimize dependencies and implement features in a more distinct and focused manner.

Having a such a library for free encouraged people to adopt this concept more and more. Until other options emerged who have their own distinct features and others that overlap with those of MediatR's.

Now that MediatR has announced it's going commercial, a lot of projects will naturally research other options which offer the same features that MediatR used to provide but without costing them anything.

Let's take a look at some of the options you can you use to replace the CQRS implementation of MediatR.

We'll only look at replacements for the CQRS feature and command/query dispatching of MediatR not the behavioral pipeline. We can discuss behavioral pipeline replacements in another post.

Now let's talk about some of the available options.

1.Rebus🚌

Rebus is mainly used as a wrapper for message brokers and queueing systems. But it can also be used as command/query dispatcher. This comes naturally to the famous library because essentially it publishes a message and routes it to its consumer but rather than RabbitMQ or Azure Service Bus you can configure it to use in memory queues which is basically what MediatR does when handling commands and queries.

Rebus is mostly useful for people who already have Rebus in the solutions so they can utilize it as well in command/query handling. Also it can fit developers who already deal with RabbitMQ or Azure Service Bus or other brokers which Rebus is integrated with and can benefit from Rebus as both a wrapper for the broker and a replacement for MediatR as well.

Now there are a few things developers must know before using Rebus as a MediatR replacement.

As Rebus is essentially a pub/sub wrapper, it typically does not wait for a response or a returned value when publishing a message (command/query). So, in order to dispatch a query and expect a return, you have to follow the message brokers request/reply pattern.

Request/reply pattern is basically when you dispatch a message and listen to another message that the consumer should publish. In doing so, you have to do some redundant dance around dispatching a query (or even a command with a return value) just to do what MediatR does out of the box. It really isn't Rebus' fault if you think about it. It's not the main purpose of Rebus after all.

That being said, you can expect that you need to set a timeout for waiting for the reply message, you have to manually reply in the handler with the return type this handler is expected to return and last but not least every time you dispatch this command/query you have to specify the return type when dispatching.

One other crucial detail too. If you care about injecting the http context in your handler. I have bad news for you. Unlike MediatR, the http context from requests is not injected in the handler. So naturally if you try to access the context to get some claims for example you will get a good old null reference exception. This of course can be avoided by getting anything you want in the API layer and setting it in the command/query thus avoiding accessing the http context from the application layer, which is not a bad thing if you think about.

As you can imagine, Rebus is far from a perfect replacement. What about we take a look at another option. One with far less configuration.

2.Wolverine 🐾

Wolverine can definitely be a good replacement for MediatR when talking command/query dispatching. It is easier to set-up and use when compared to MediatR.

In MediatR, in order to create a command/query you had to inherit a certain interface and specify return type. Which will force you to add and implement the Handle function which only has the command/query as the injected parameter along with the cancellation token.

This acts as a sort of a guardrail or guidance for developers as there were certain steps to follow and each step more or less leads to the next one. 

In Wolverine however...none of that is needed. And this is honestly where the magic happens. You don't need to implement any interface or specify any return type in the command/query. Just create a method with the name Handle(s)/Async or Consume(s)/Async with the command/query as the first parameter then any number of parameters you need to inject in this method, and the handler is magically matched to the message dispatched.

Cool, right? Yeah until someone make a typo in the function name or does not make the first parameter in the method the command/query or anything as simple as this could easily result in failing to discover this handler method.

So as you can see, as easy it is to use, it's also very brittle. Of course there are work arounds like adding the [WolverineHandler] attribute but that defeats the purpose of the simplicity Wolverine offers.

Wolverine also applies the same magic in matching handlers in consuming messages from message brokers such as RabbitMQ or Azure Service Bus and many others.

And as mentioned, the dependencies you inject are on method level giving you the opportunity to merge more than one handler method in the same class. Methods can also be static which reduces object allocations.

The handlers are also http context aware just like MediatR. It's better to access the claims in the API layer and add them to the message but if you must, you can use a very nice feature which is Wolverine API attributes. You add on top of your APIs and they guarantee that your handler receives the http context it requires.

On the other hand however, commands and queries do not specify the return type as I mentioned. Which means you have to specify the return type when dispatching the message like Rebus, as it also uses the request/reply pattern. And they mention in the documentation : "The command (message) should only contain the input data needed to execute the operation. It does not need a property to "hold" the response". Which does make sense I have to agree. But still, I believe that MediatR had a more solid approach in designing the commands/queries as contracts which the publisher and the consumer should respect and follow.

One huge advantage of Wolverine is the seamless integration with Marten to create the Critter Stack. A concept that is really important to understand and allows simplified event sourcing with least effort while utilizing Postgres to give your CQRS messages the resiliency it might need based on how important the messages you deal with. But that's a topic for another day.

All in all Wolverine is definitely powerful enough to replace MediatR's CQRS features and can allow you to expand in directions that could elevate your system.

Now what else...

3.DIY πŸ”¨

If you are using MediatR solely for CQRS handling, why not go ahead and create your own message dispatcher. Yeah, I know it's reinventing the wheel. But how can you understand the differences between each option if you haven't gotten your hands dirty by looking under the hood? I recommend that you try this tutorial out. Or even look for similar ones online. My point is that you yourself have to try implementing a CQRS handling solution on your own. And who knows, you might not need these over-the-top solutions if your implementation was good enough.

But wait, do you really need any of that?

4.Forget about it!

Okay, now I know CQRS is such a buzz word. Everyone is talking about how you MUST follow a mediator pattern to do this and that etc. But honestly, if you're using MediatR or any other similar library to call the handler from the API layer, just cut it short and call the handler from the controller. You can just access the services injected in the API and get the handler you need and call its handle function and voila! Keep it simple stupid, everyone.

Honorable MentionsπŸ“

  • ConduitR 

Designed as a high-performance, "drop-in" feeling alternative with a familiar API (IRequest, IPipelineBehavior). It features cached pipelines for performance and built-in telemetry for OpenTelemetry.

  • Cortex.Mediator 

An MIT-licensed library that supports all core CQRS concepts and adds built-in transaction management via IUnitOfWork. It is part of the larger Cortex Data Framework, providing additional tools for stateful workflows.

  • LiteBus

A lightweight, CQS-focused library that has been free and open-source for five years. It offers specialized interfaces for commands, queries, and events, along with pre/post-handlers and error handlers.

  • Concordia

Leverages C# Source Generators for automatic handler registration at compile-time, eliminating runtime reflection and improving startup performance.

Comments

Popular posts

Why I Hate Microservices Part 1: The Russian Dolls Problem πŸͺ†πŸͺ†πŸͺ†

Why I Hate Microservices Part 3: The Identity Crisis 😡

Partitioning in Kafka: A Guide to Publishing Batched Data πŸ“ƒπŸ“©