In our previous blogs, we had visited the Mediator Pattern and also walked through the implementation of Mediator using Mediatr library. In this example, we will look into an alternative library – the MassTransit.
Over the years, MassTransit has made its APIs a lot more simpler. I wasn’t a huge of the earlier implementations, but the last time I checked the library things has evolved to a more usable API. Let us examine the implementation now.
To demonstrate the capabilities of MassTransit as an implementation of Mediator, we will build a Web API with an enpoint for order submission. The API has a response which indicates if the Order was successful or not. The endpoint would accept two Guids each representing the OrderId
and CustomerId
.
Before we begin, ensure that we have installed the necessary nuget packages.
MassTransit
We will begin by defining the OrderSubmitCommand
which indicates the command for a new Order.
public class OrderSubmitCommand : Request<OrderSubmitResponse>
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public DateTime TimeStamp { get; set; }
}
public class OrderSubmitResponse:IResponseBase
{
public Guid OrderId { get; set; }
public Guid CustomerId { get; set; }
public DateTime TimeStamp { get; set; }
public string Message { get; set; } = string.Empty;
public bool IsSuccess { get; set; }
}
public interface IResponseBase
{
string Message { get; set; }
bool IsSuccess { get; set; }
}
The OrderSubmitCommand
requires an Id
, CustomerId
and a associated TimeStamp
. The OrderSubmitResponse
represents the expected response from the mediator consumer/handler.
We will now proceed te define our consumer OrderSubmitHandler
public class OrderSubmitHandler : IConsumer<OrderSubmitCommand>
{
public async Task Consume(ConsumeContext<OrderSubmitCommand> context)
{
var request = context.Message;
await context.RespondAsync(new OrderSubmitResponse
{
OrderId = request.Id,
TimeStamp = DateTime.UtcNow,
Message = request.CustomerId.Equals(Guid.Empty) ? "Invalid User" : "Order Submission Successful;",
IsSuccess = !request.CustomerId.Equals(Guid.Empty)
});
}
}
The consumer needs to inherit from IConsumer<T>
. Do not that this is likely to change as the current develop branch of MassTransit uses a different base class. We indicate that the particular consumer is designed to handler OrderSubmitCommand
by passing the type in IConsumer
.
The consumer doesn’t do anything fancy. It checks if the CustomerId
is a valid one and respond based on it.
Now is the time to define our endpoint. We will use DI to inject an instance of IMediator
.
public class DemoController : ControllerBase
{
private readonly IMediator _mediator;
public DemoController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<IActionResult> PlaceOrder(Guid id,Guid customerId)
{
var response = await _mediator.SendRequest(new OrderSubmitCommand
{
Id = id,
CustomerId = customerId,
TimeStamp = DateTime.UtcNow
});
return response.IsSuccess ? Ok(response) : BadRequest(response);
}
}
So how does the Mediator.Send
know where to find consumers ? This magic is done with some configurations which we need to do.
builder.Services.AddMediator(cfg =>
{
cfg.AddConsumersFromNamespaceContaining<OrderSubmitHandler>();
});
In the above code, we are asking our application to detect all the consumers defined under the namespace where OrderSubmitHandler
is defined (in this case, we have only one).
That’s all we require. Run your API and test the output. You can find the source code in this example in my Github.