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
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
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
Limitations
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!!