MediatR publish and MediatR send
Asked Answered
M

2

35

I have tried the CQRS pattern using MediatR and am loving the clean state in which applications am working on are transforming. In all the examples i have seen and used, I always do

await Mediator.Send(command);

It's been same for the queries as well

var data = await Mediator.Send(queryObject);

I just realized there's Mediator.Publish as well which after searching seems to me to be doing the same thing. I am trying to understand what the difference between the Mediator.Send and Mediator.Publish is. I have read the MediatR library docs and I still don't understand what the difference between these are. Kindly help me understand the difference.

Thanks for your help

Midland answered 31/7, 2020 at 6:12 Comment(1)
I think "Send" method is for sending commands and it gives the response but "Publish" method is for sending events and doesn't return any response.Tolliver
W
58

MediatR has two kinds of messages it dispatches:

  • Request/response messages, dispatched to a single handler
  • Notification messages, dispatched to multiple handlers
  • Send may return a response, but do not have to do it.
  • Publish never return the result.

You are sending requests (sometimes called commands) by _mediator.Send({command}) to exactly one concrete handler. It may be e.g. command that saves a new product to the database. It is often a request from the user (frontend/API) or sometimes it may be internal command in your system given by other service in a synchronous way. It is always expected that the command will be executed immediately and you will receive some proper result or error to immediately inform the client about some failures.

You are publishing notifications (often called events) by _mediator.Publish({event}) to zero, one or many handlers. You used notifications when you want to publish some information and you do not know who needs that. E.g. NewProductEvent which is published after successfully adding product to your Warehouse Module. Few other contexts want to subscribe the information and e.g. send email to a client that a new product is available or create some default configuration for the product in your Store Module (which payment and delivery are available for the product). You may use notifications in a synchronous way. All data will be saved in one transaction (product and store configuration) or you may use some async pattern with service bus or/and sagas. In the second case (asynchronous) you must manually handle cases when something wrong happens in other services or contexts which subscribe to your notification.

Example scenario: Default configuration was not created.

  • If you have one transaction (synchronous way) for a few contexts, you will receive an error, log the error and return it to the user/client.
  • In an asynchronous way, you send events after saving a new product to the database. You do not want to have the product in a half-error state in your system. So firstly I recommend creating it in the Draft state and wait for an event that informs you about the successfully created configuration and then changes the state to e.g New/Correct etc.

A good example of using mediatR you will find in e.g. Ordering microservice in EShopOnContainers by Microsoft: github. You will see an example usage of CQRS and DDD with EF core and ASP Net.

Whitehead answered 31/7, 2020 at 6:39 Comment(3)
Great, thank you for this explanation. So that means the Publish will be for Domain events?Midland
yes but it could be also Application/Integration events at all. Example of integration mediatR with event bus is in EShopOnContainersWhitehead
Reading this 4 years later - funny how naming is hard, and I now Jimmy spent some time on this, but people still struggle to get it. The 2 key differences, if you summarize this wonderful answer by @zolty13, is that Send can have a return type and is 1 sender to 1 only one handler, while Publish is void with no return type and 1 sender to many handlers, regardless of use case (DDD domain events, CQRS, etc.). MediatR lib was originally designed to mediate HTTP requests from controller actions in web layer into an app layer to decouple them, enabling a vertical slice architecture.Vexatious
O
0

I will present code examples to explain. I won't use MediatR, but IGet, with which you can easily create MediatR-like functionality.

In your startup class, instead of adding MediatR, use

serviceCollection.AddIGet();

mediatR.Send

Before sending a request you'll need to create a handler. The constructor may contain all kinds of dependencies, as long as they are part of your service collection.

public class WeatherForecastRequestHandler
{
    private IConnectionFactory _connectionFactory;
    private ILogger<MyHandler> _logger;

    public WeatherForecastRequestHandler(
        IConnectionFactory connectionFactory,
        ILogger<MyHandler> logger)
    {
        _connectionFactory = connectionFactory;
        _logger = logger;
    }

    public WeatherForecast Handle(WeatherForecastRequest request)
    {
        // connect and get weather forecast

        return new WeatherForecast
        {
            // set properties.
        }
    }
}

Get the weather forecast:

var forecast = i.Get<WeatherForecastRequestHandler>().Handle(request);

mediatR.Publish

This example deviates more from how MediatR works internally, because MediatR collects all INotificationHandlers via assembly scanning. With IGet you can also activate types from a Type[] but for now I will explicitly instantiate the handlers and invoke their methods. Let's do that in a class that we call NotificationPublisher (without logging exceptions in this example):

public class NotificationPublisher
{
    private IGet i;

    public NotificationPublisher(IGet iget)
    {
        i = iget;
    }

    public async Task PublishAsync(Notification notification)
    {
        try
        {
            await i.Get<FirstHandler>().HandleAsync(notification);
        }
        catch { }
        try
        {
            await i.Get<SecondHandler>().HandleAsync(notification);
        }
        catch { }
        try
        {
            i.Get<ThirdHandler>().Handle(notification);
        }
        catch { }
        // etc.
    }
}

Now define the 3 handlers in a similar way as in the first example and then we can publish a notification to all of them with one line of code:

await i.Get<NotificationPublisher>().PublishAsync(notification);

The IGet readme actually also contains an example that shows how to make a generic notificaiton publisher for any type of notification.

Ovida answered 15/3, 2023 at 20:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.