Building a Mediator Pattern in .NET Core Web API

Building a Mediator Pattern in .NET Core Web API


We'll create a simple implementation of the Mediator Pattern for a Web API that retrieves user data. This implementation uses pure C# and focuses on the key principles of the Mediator Pattern.


Step 1: Create a .NET Core Web API Project

  1. Open your terminal and run the following command: dotnet new webapi -n MediatorPatternDemo cd MediatorPatternDemo
  2. Open the project in your favorite code editor or IDE.


Step 2: Define the Mediator Interface

The IMediator interface will act as a central hub for handling requests and responses.

public interface IMediator
{
    Task<TResponse> Send<TRequest, TResponse>(TRequest request);
}
        

Step 3: Implement the Mediator

Create a concrete implementation of the IMediator interface. This implementation will map requests to their respective handlers.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class Mediator : IMediator
{
    private readonly Dictionary<Type, object> _handlers = new();

    public void RegisterHandler<TRequest, TResponse>(IRequestHandler<TRequest, TResponse> handler)
    {
        _handlers[typeof(TRequest)] = handler;
    }

    public async Task<TResponse> Send<TRequest, TResponse>(TRequest request)
    {
        if (_handlers.TryGetValue(typeof(TRequest), out var handler))
        {
            var typedHandler = (IRequestHandler<TRequest, TResponse>)handler;
            return await typedHandler.Handle(request);
        }

        throw new InvalidOperationException($"No handler registered for {typeof(TRequest).Name}");
    }
}
        

Step 4: Define the Request and Handler Interfaces

Define an interface for the request handler.

public interface IRequestHandler<TRequest, TResponse>
{
    Task<TResponse> Handle(TRequest request);
}
        

Step 5: Create a Request and its Handler

Request

Create a GetUserByIdRequest class to represent the request.

public class GetUserByIdRequest
{
    public int UserId { get; }

    public GetUserByIdRequest(int userId)
    {
        UserId = userId;
    }
}
        

Handler

Create a GetUserByIdHandler class to handle the request.

using System.Threading.Tasks;

public class GetUserByIdHandler : IRequestHandler<GetUserByIdRequest, User>
{
    public Task<User> Handle(GetUserByIdRequest request)
    {
        // Simulate fetching user from a database
        var user = new User
        {
            Id = request.UserId,
            Name = "John Doe",
            Email = "john.doe@example.com"
        };

        return Task.FromResult(user);
    }
}
        

Step 6: Create a User Model

Define a User class to represent the user data.

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}
        

Step 7: Configure the Mediator in Program.cs

In Program.cs, register the mediator and handlers.

var builder = WebApplication.CreateBuilder(args);

// Register services
builder.Services.AddControllers();
builder.Services.AddSingleton<IMediator, Mediator>();
builder.Services.AddSingleton<IRequestHandler<GetUserByIdRequest, User>, GetUserByIdHandler>();

// Register Mediator Handlers
var app = builder.Build();
var mediator = app.Services.GetRequiredService<IMediator>();
mediator.RegisterHandler(
    app.Services.GetRequiredService<IRequestHandler<GetUserByIdRequest, User>>()
);

app.MapControllers();
app.Run();
        

Step 8: Create a Controller

Use the IMediator to handle requests in a Web API controller.

using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly IMediator _mediator;

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

    [HttpGet("{id}")]
    public async Task<IActionResult> GetUserById(int id)
    {
        var request = new GetUserByIdRequest(id);
        var user = await _mediator.Send<GetUserByIdRequest, User>(request);

        if (user == null)
            return NotFound();

        return Ok(user);
    }
}
        

Step 9: Run the Application

  1. Run the application: dotnet run
  2. Make a GET request to the API endpoint: GET https://localhost:5001/api/users/1

You should receive a response with the user data:

{
  "id": 1,
  "name": "John Doe",
  "email": "john.doe@example.com"
}
        

Advantages of This Custom Mediator Implementation

  1. No External Dependencies: Fully self-contained without plugins like MediatR.
  2. Flexibility: You control how the mediator handles requests and responses.
  3. Simplicity: Suitable for small to medium-sized projects.


Limitations

  • Manual Handler Registration: Requires manual registration of handlers for each request.
  • Scalability: For large-scale applications, libraries like MediatR are better suited.


Conclusion

The Mediator Pattern is a powerful design principle for decoupling components and centralizing communication in a .NET Core Web API. This example demonstrates how you can implement the pattern from scratch while maintaining clean and maintainable code.

For larger or more complex projects, consider using a library like MediatR for advanced features and better scalability.

Thank you for this article!!

Like
Reply

To view or add a comment, sign in

More articles by Ravinder Singh

Explore content categories