Request Response Messaging Pattern With MassTransit
Tags: microservices, messaging, masstransti, distributedsystems
Published: 2024-04-30 14:44:11

---
title: Request Response Messaging Pattern With MassTransit
published: true
date: 2024-04-27 00:00:00 UTC
tags: microservices,messaging,masstransti,distributedsystems
canonical_url: https://www.milanjovanovic.tech/blog/request-response-messaging-pattern-with-masstransit
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzei0cf84dh3tth9vvpk.png
---
Building distributed applications might seem simple at first. It's just servers talking to each other. Right?

However, it opens a set of potential problems you must consider. What if the network has a hiccup? A service unexpectedly crashes? You try to scale, and everything crumbles under the load? This is where the way your distributed system communicates becomes critical.

Traditional synchronous communication, where services call each other directly, is inherently fragile. It creates tight coupling, making your whole application vulnerable to single points of failure.

To combat this, we can turn to distributed messaging (and introduce an entirely different set of problems, but that's a story for another issue).

One powerful tool for achieving this in the .NET world is MassTransit.

In this week's issue, we'll explore MassTransit's implementation of the request-response pattern.

## Request-Response Messaging Pattern Introduction

Let's start by explaining how the request-response messaging pattern works.

The request-response pattern is just like making a traditional function call but over the network. One service, the requester, sends a request message and waits for a corresponding response message. This is a [**synchronous communication approach**](https://www.milanjovanovic.tech/blog/modular-monolith-communication-patterns) from the requester's side.

The good parts:

- **Loose Coupling**: Services don't need direct knowledge of each other, only of the message contracts. This makes changes and scaling easier.
- **Location Transparency**: The requester doesn't need to know _where_ the responder is located, leading to improved flexibility.

The bad parts:

- **Latency**: The overhead of messaging adds some additional latency.
- **Complexity**: Introducing a messaging system and managing the additional infrastructure can increase project complexity.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9odxtnxr9zyco3pqlqj1.png)

## Request-Response Messaging With MassTransit

[**MassTransit**](https://www.milanjovanovic.tech/blog/using-masstransit-with-rabbitmq-and-azure-service-bus)supports the [request-response](https://masstransit.io/documentation/concepts/requests) messaging pattern out of the box. We can use a **request client** to send requests and wait for a response. The request client is asynchronous and supports the `await` keyword. The request will also have a timeout of 30 seconds by default, to prevent waiting for the response for too long.

Let's imagine a scenario where you have an order processing system that needs to fetch an order's latest status. We can fetch the status from an Order Management service. With MassTransit, you'll create a request client to initiate the process. This client will send a `GetOrderStatusRequest` message onto the bus.

```csharp
public record GetOrderStatusRequest
{
    public string OrderId { get; init; }
}
```

On the Order Management side, a responder (or consumer) will be listening for `GetOrderStatusRequest` messages. It receives the request, potentially queries a database to get the status, and then sends a `GetOrderStatusResponse` message back onto the bus. The original request client will be waiting for this response and can then process it accordingly.

```csharp
public class GetOrderStatusRequestConsumer : IConsumer<GetOrderStatusRequest>
{
    public async Task Consume(ConsumeContext<GetOrderStatusRequest> context)
    {
        // Get the order status from a database.

        await context.ResponseAsync<GetOrderStatusResponse>(new
        {
            // Set the respective response properties.
        });
    }
}
```

## Getting User Permissions In a Modular Monolith

Here's a real-world scenario where my team decided to implement this pattern. We were building a [**modular monolith**](https://www.milanjovanovic.tech/modular-monolith-architecture), and one of the modules was responsible for managing user permissions. The other modules could call out to the Users module to get the user's permissions. And this works great while we are still inside a monolith system.

However, at one point we needed to extract one module into a separate service. This meant that the communication with the Users module using simple method calls would no longer work.

Luckily, we were already using MassTransit and RabbitMQ for messaging inside the system.

So, we decided to use the MassTransit request-response feature to implement this.

The new service will inject an `IRequestClient<GetUserPermissions>`. We can use it to send a `GetUserPermissions` message and await a response.

A very powerful feature of MassTransit is that you can await more than one response message. In this example, we're waiting for a `PermissionsResponse` or an `Error` response. This is great, because we also have a way to handle failures in the consumer.

```csharp
internal sealed class PermissionService(
    IRequestClient<GetUserPermissions> client)
    : IPermissionService
{
    public async Task<Result<PermissionsResponse>> GetUserPermissionsAsync(
        string identityId)
    {
        var request = new GetUserPermissions(identityId);

        Response<PermissionsResponse, Error> response =
            await client.GetResponse<PermissionsResponse, Error>(request);

        if (response.Is(out Response<Error> errorResponse))
        {
            return Result.Failure<PermissionsResponse>(errorResponse.Message);
        }

        if (response.Is(out Response<PermissionsResponse> permissionResponse))
        {
            return permissionResponse.Message;
        }

        return Result.Failure<PermissionsResponse>(NotFound);
    }
}
```

In the Users module, we can easily implement the `GetUserPermissionsConsumer`. It will respond with a `PermissionsResponse` if the permissions are found or an `Error` in case of a failure.

```csharp
public sealed class GetUserPermissionsConsumer(
    IPermissionService permissionService)
    : IConsumer<GetUserPermissions>
{
    public async Task Consume(ConsumeContext<GetUserPermissions> context)
    {
        Result<PermissionsResponse> result =
            await permissionService.GetUserPermissionsAsync(
                context.Message.IdentityId);

        if (result.IsSuccess)
        {
            await context.RespondAsync(result.Value);
        }
        else
        {
            await context.RespondAsync(result.Error);
        }
    }
}
```

## Closing Thoughts

By embracing messaging patterns with MassTransit, you're building on a much sturdier foundation. Your .NET services are now less tightly coupled, giving you the flexibility to evolve them independently and weather those inevitable network glitches or service outages.

The [request-response pattern](https://youtu.be/NjsoykEOkrk) is a powerful tool in your messaging arsenal. MassTransit makes it remarkably easy to implement, ensuring that requests and responses are delivered reliably.

We can use request-response to implement communication between modules in a [**modular monolith**](https://www.milanjovanovic.tech/modular-monolith-architecture). However, don't take this to the extreme, or your system might suffer from increased latency.

Start small, experiment, and see how the reliability and flexibility of messaging can transform your development experience.

That's all for this week. Stay awesome!

* * *

**P.S. Whenever you're ready, there are 3 ways I can help you:**

1. [**Modular Monolith Architecture (NEW):**](https://www.milanjovanovic.tech/modular-monolith-architecture?utm_source=dev.to&utm_medium=website&utm_campaign=cross-posting) Join 450+ engineers in this in-depth course that will transform the way you build modern systems. You will learn the best practices for applying the Modular Monolith architecture in a real-world scenario.

2. [**Pragmatic Clean Architecture:**](https://www.milanjovanovic.tech/pragmatic-clean-architecture?utm_source=dev.to&utm_medium=website&utm_campaign=cross-posting) Join 2,700+ students in this comprehensive course that will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture.

3. [**Patreon Community:**](https://www.patreon.com/milanjovanovic) Join a community of 1,050+ engineers and software architects. You will also unlock access to the source code I use in my YouTube videos, early access to future videos, and exclusive discounts for my courses.