MassTransit Mediator with ASP.NET Core

MassTransit Mediator with ASP.NET Core

Mediator is one of the 23 design patterns stated inside the GOF Design Patterns book. All it does is encapsulates the communication between objects, hence no coupling. It falls into the object behavior patterns category.

Nevertheless, this pattern is widely used in many productions ready libraries; MediatR is such as one. Recently I've been dwelling in the world of messaging frameworks. In the land of so many awesome libraries, one caught my eyes. Ladies and gentlemen, I present to you MassTransit.

Anyways, I was glad to find out that MassTransit has a Mediator implementation that may/may not (depends on your use case) replace the need of MediatR.

MassTransit Mediator runs in-process and in-memory, no transport (RabbitMQ, Service Bus, etc.) is required i.e. pretty similar to MediatR. So, why using two separate libraries when you can use one? Am I right or am I right?

No more talking. Here goes an example.

  • Start with the default ASP.NET Core Web API project. I'm using .NET 6.
  • Add MassTransit.AspNetCore package from NuGet.
  • Configure the MassTransit Mediator services as follows,
using MassTransit;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMediator();

/* Code removed for brevity */

var app = builder.Build();

/* Code removed for brevity */

app.Run();
Program.cs

MassTransit is consumer-based, unlike MediatR that uses RequestHandlers for handling Queries and Commands. You can write a consumer for GetWeatherForecast like this:

using MassTransit;

namespace WeatherAPI;

public class GetWeatherForecastConsumer : IConsumer<GetWeatherForecasts>
{
    private static readonly string[] Summaries = new[] 
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public async Task Consume(ConsumeContext<GetWeatherForecasts> context)
    {
        var forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        }).ToArray();

        await context.RespondAsync<WeatherForecasts>(new
        {
            Forecasts = forecasts
        });
    }
}
GetWeatherForecastConsumer.cs

Both GetWeatherForecasts and WeatherForecasts are message contracts. In MassTransit, contracts are simply interfaces,

public interface GetWeatherForecasts
{
}

public interface WeatherForecasts
{
    WeatherForecast[] Forecasts {  get; }
}

GetWeatherForecasts is empty cause we ain't passing any input arguments to get the forecasts.

Getting back to the WeatherForecastController, a request client is needed to get a response from the GetWeatherForecastConsumer,

using MassTransit;
using Microsoft.AspNetCore.Mvc;

namespace WeatherAPI.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private readonly IRequestClient<GetWeatherForecasts> _requestClient;

        public WeatherForecastController(IRequestClient<GetWeatherForecasts> requestClient)
        {
            _requestClient = requestClient;
        }

        [HttpGet]
        public async Task<IEnumerable<WeatherForecast>> Get()
        {
            var response = await _requestClient.GetResponse<WeatherForecasts>(new { });
            return response.Message.Forecasts;
        }
    }
}
WeatherForecastController.cs

Pretty much done accept for the service registrations for GetWeatherForecastConsumer and IRequestClient<GetWeatherForecats>. Going back to the Program.cs, configure the AddMediator service as follows,

builder.Services.AddMediator(cfg =>
{
    cfg.AddConsumers(Assembly.GetExecutingAssembly());
    cfg.AddRequestClient<GetWeatherForecasts>();
});
Program.cs

AddConsumers(Assembly.GetExecutingAssembly()) will register all the consumers available in the current assembly. AddRequestClient<GetWeatherForecasts>() will register a request client for GetWeatherForecasts.

Done! Run the application and everything should work properly.

Extras:

Wondering whether you can register request clients on demand? Sure, you can! Inject the IMediator interface in the constructor of WeatherForecastController and use it to create a request client like this:

using MassTransit.Mediator;
using Microsoft.AspNetCore.Mvc;

namespace WeatherAPI.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private readonly IMediator _mediator;

        public WeatherForecastController(IMediator mediator)
        {
            _mediator = mediator;
        }

        [HttpGet]
        public async Task<IEnumerable<WeatherForecast>> Get()
        {
            var client = _mediator.CreateRequestClient<GetWeatherForecasts>();

            var response = await client.GetResponse<WeatherForecasts>(new { });
            return response.Message.Forecasts;
        }
    }
}
WeatherForecastController.cs

Remove the AddRequestClient<GetWeatherForecasts>() from Program.cs as it is no longer needed.

Github Repository:

GitHub - fiyazbinhasan/MassTransitMediator
Contribute to fiyazbinhasan/MassTransitMediator development by creating an account on GitHub.
Mediator | MassTransit
A free, open-source distributed application framework for .NET.
Requests | MassTransit
A free, open-source distributed application framework for .NET.
Consumers | MassTransit
A free, open-source distributed application framework for .NET.
Roslyn Analyzers | MassTransit
A free, open-source distributed application framework for .NET.

Comments