Simplifying Development and Separating Concerns with MediatR


OSS&Microsoft Banner

Welcome to our weekly MVP series where we discuss some of the most useful solutions, tips & tricks and how to’s on integrating OSS & Microsoft technologies. In case you have any questions, don’t hesitate and drop us a comment below or reach out to us via Twitter and Facebook! Enjoy!

One of the great things about open source software is how quickly and readily we are able to make our own lives easier as developers.  Sometimes we are able to find a pure gem out there, something that is non-invasive, doesn’t require us to re-architect our entire solution but at the same time can reduce complexity in parts of the application we’re building or servicing. MediatR is one such library and worth considering in your .NET applications.

This post explores the MediatR open source library used in an ASP.NET 5 application in Visual Studio 2015 (download VS here).

First Things First

MediatR is an open source implementation of the mediator pattern that doesn’t try to do too much and performs no magic. It allows you to compose messages, create and listen for events using synchronous or asynchronous patterns.  It helps to reduce coupling and isolate the concerns of requesting the work to be done and creating the handler that dispatches the work.

What are We Trying to Solve

A common problem that I’ve seen crop up in on MVC projects: folks have the best of intentions and try to isolate code by creating different controllers for different features or facets of the application. To ensure that the class itself doesn’t know how to “do too much” developers will leverage an IoC container to inject their dependencies. Something that might look familiar to you is this example of a constructor on a controller:

public DashboardController(
    ICustomerRepository customerRepository
    )

This seems pretty harmless at first, right? But invariably it seems that over the course of feature development that the controller starts to need to know how to do more than just work with the customer data in order to present a meaningful user experience. It’s not long before we end up with something like this:

public DashboardController(
    ICustomerRepository customerRepository,
    IOrderService orderService,
    ICustomerHistoryRepository historyRepository,
    IOrderRepository orderRepository,
    IProductRespoitory productRespoitory,
    IRelatedProductsRepository relatedProductsRepository,
    ISupportService supportService,
    ILog logger
    )  

Ouch! There are a lot of things going on here and likely some debate as to where one might start fixing things, but I can also tell that someone’s trying to do the right thing: ensure the controller isn’t doing real things on its own, and isolate the different pieces of functionality into appropriately-sized blocks of code.

However, this is an easy trap to fall into and soon you’re left with signatures like the above, and worse, business logic will start cropping up in your action methods as you need to check the IOrdersRepository for outstanding items before completing a request through IOrderService. Sure, a controller doesn’t know how to do anything on its own now, but it invites absolutely everyone to the party every time it loads.

So, how do we get away from this?

The Mediator Pattern to the Rescue

The larger problem here is that as controllers throughout the application grow, our coupling starts to take control of our code. Refactoring gets more complicated as the complexity of communication between our objects becomes more entangled. Thankfully, there is a common solution to this in our long-lived pattern known as “Mediator”.

A mediator is an object that makes decisions on how and when objects interact with each other. It encapsulates the “how” and coordinates execution based on state, the way it’s invoked or the payload you provide to it.

What this means to us in MVC is that rather than passing in a list of four, seven or thirteen dependencies, we pass in only one: the mediator.

The MediatR Library to the Rescue

Completely unassuming, MediatR jumps in and lets us to do this, instead of the messy constructor we saw up above, we get this:

public DashboardController(
    IMediator mediator
    )

Sweet! We pass in an instance of an IMediator instead, and all our problems are solved! Okay…well, that doesn’t solve everything. After all, our controller was doing a Canadian tonne of stuff there. Let’s look at how a simple command might play out in a real project.

The Mechanics of MediatR

MediatR is a godsend as it takes care of a lot of the chug work we’d have to do on our own to achieve proper implementation of the Mediator pattern. To use it, you’re going to:

  • Install the package via NuGet
  • Create a request object that can be sent to the mediator
  • Create a handler that processes the request

We start by adding MediatR to our project. In ASP.NET 5 you just modify your project.json to include MediatR in your dependencies. If you’re not yet on ASP.NET 5, you can use NuGet via dialogs or in the Package Manager Console to install the library:

Install-Package MediatR

Next we wire it up in our IoC container. Here, I’m using AutoFac on ASP.NET 5 in an MVC 6 project inside the ConfigureServices method in the Startup class:

var builder = new ContainerBuilder();

builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterAssemblyTypes(typeof(IMediator).Assembly).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof(Startup).Assembly).AsImplementedInterfaces();
builder.Register<SingleInstanceFactory>(ctx =>
{
    var c = ctx.Resolve<IComponentContext>();
    return t => c.Resolve(t);
});
builder.Register<MultiInstanceFactory>(ctx =>
{
    var c = ctx.Resolve<IComponentContext>();
    return t => (IEnumerable<object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
});

//Populate the container with services that were previously registered
builder.Populate(services);

var container = builder.Build();

To be honest, this is the only part of adding MediatR that is just plumbing code. Mostly this is here so that MediatR can decide which objects to use when as we pass requests onto the bus. You’ll notice the line where we register the types in the Startup assembly, for instance, which is a way to get AutoFac to scan our code for any class that implements an assembly in the code we’ve written; this assumes that our requests and handlers are all in the same assembly, but you could just as easily make them external to your site.

If you are on a different platform or framework version, or use a different IoC library, just check the MediatR samples to fit your needs.

Now, we create an object that will represent the work that we need done:

public class AddEmergencyContactCommand : IRequest
{
    public AddEmergencyContactViewModel Contact { get; set; }
    public string UserId { get; set; }
}

This is a simple class that implements the IRequest interface – which is just a marker for location – and has a few properties on it. The properties are the payload so that you you can pass everything that is needed to the mediator in one shot. You’ll note that I’m using a mix of simple and complex types here; you are free to set this up as you need, with a long list of properties or a couple of complex types, or however fits your style.

We can then create the handler for the request as follows:

public class AddEmergencyContactHandler : RequestHandler<AddEmergencyContactCommand>
{
    private readonly ApplicationDbContext _context;
    public AddEmergencyContactHandler(ApplicationDbContext context)
    {
        _context = context;
    }

    protected override void HandleCore(AddEmergencyContactCommand message)
    {
        // create my new object, add it to the database, etc.
        // ...

        _context.SaveChanges();
    }
}

The handler is also just a simple class, but it inherits from RequestHandler<T> and MediatR makes sure it gets invoked with the correct payload. You’ll also notice that my constructor here takes an ApplicationDbContext, so my handler itself can have its own dependencies. These are now separated away from the controller. Because the class is doing only one thing – handling the appropriate request – my dependencies here are never going to grow out of hand or implicitly increase coupling in other areas of my application.

Finally, we wrap things up by giving the request to the mediator in our controller’s action:

[HttpPost]
public IActionResult AddEmergencyContact(AddEmergencyContactViewModel model)
{
    if (ModelState.IsValid)
    {
        model.EmergencyContactId = Guid.NewGuid();
        _mediator.Send(new AddEmergencyContactCommand { Contact = model, UserId = User.GetUserId()});
        return RedirectToAction("Index");
    }
    return View(model);
}

In my POST action on my controller, I validate the state of my object, put it on the bus with the requisite information and finally send the command along its way. The studious developer will also note that I’m following the PRG pattern here, helping to prevent duplicate form submissions by sending the user back to a different page.

Next Steps

We often take for granted or overlook the cogs and gears that are part of the base class library itself, so it’s understandable that we can miss the libraries out there that can really help us out.  Not only is it healthy to be out there learning about projects like MediatR, but you have the chance to grow and contribute as well.

We’re in a new era in the .NET space, one filled with package managers, multiple runtime environments and increased requirements for complexity, manageability and scalability in our applications. The open source projects that are out there can help get us there faster.  Here’s how you can get involved:

  • Get yourself the latest version of Visual Studio
  • Check out MediatR or other open source projects
  • Make sure you have a GitHub account, and start forking repos!

Happy coding! Smile

Comments (0)

Skip to main content