ASP.NET Core Docs

Mediator Pattern
& MediatR

Kiến trúc Mediator giúp tách biệt logic, giảm coupling — triển khai dễ dàng với MediatR

Mediator Pattern là gì?

Mediator là một design pattern thuộc nhóm Behavioral, giúp giảm sự phụ thuộc trực tiếp giữa các đối tượng bằng cách đưa toàn bộ giao tiếp qua một trung gian (mediator). Thay vì component A gọi trực tiếp component B, cả hai chỉ giao tiếp qua mediator.

Direct Coupling

Controller phụ thuộc trực tiếp vào nhiều service
  • Controller inject 5-10 services
  • Thay đổi service → sửa controller
  • Khó unit test (nhiều mock)
  • Constructor phình to, khó đọc
  • Vi phạm Single Responsibility

Mediator Pattern

Controller chỉ phụ thuộc vào mediator
  • Controller chỉ inject IMediator
  • Logic nằm trong Handler riêng biệt
  • Dễ unit test từng handler
  • Constructor gọn gàng, rõ ràng
  • Mỗi handler = một responsibility

Cách hoạt động

Khi controller cần thực hiện một hành động, nó tạo một request object và gửi qua MediatR. MediatR tìm handler tương ứng, thực thi, và trả về kết quả:

Controller
tạo request
MediatR
tìm handler
Behaviors
pipeline
Handler
xử lý logic
Response
kết quả
MediatR không phải là message broker như RabbitMQ hay Kafka. Nó là một in-process mediator — tất cả xảy ra trong cùng một process, không có network call. Hãy nghĩ nó như một "bộ điều phối" nội bộ trong ứng dụng.

MediatR Library

MediatR là thư viện .NET của Jimmy Bogard (tác giả AutoMapper), triển khai Mediator pattern với hai mô hình giao tiếp:

Request / Response

Một request → đúng một handler
  • IRequest<TResponse> — request có response
  • IRequest — request không trả response
  • Gửi qua mediator.Send(request)
  • Luôn có đúng một handler xử lý
📣

Notification (Pub/Sub)

Một notification → nhiều handlers
  • INotification — sự kiện phát đi
  • INotificationHandler<T> — subscriber
  • Gửi qua mediator.Publish(notification)
  • Có thể có 0 đến nhiều handlers

Cài đặt & Thiết lập

Package Manager

Install-Package MediatR

.NET CLI

dotnet add package MediatR

Đăng ký MediatR vào DI container trong Program.cs:

C#
using MediatR;

var builder = WebApplication.CreateBuilder(args);

// Đăng ký MediatR — scan tất cả handlers trong assembly
builder.Services.AddMediatR(cfg =>
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)
);

// Nếu handlers nằm ở nhiều assembly khác nhau:
builder.Services.AddMediatR(cfg =>
    cfg.RegisterServicesFromAssemblies(
        typeof(Program).Assembly,
        typeof(ApplicationLayer).Assembly
    )
);
Từ MediatR v12+, method AddMediatR(typeof(Program).Assembly) đã bị deprecated. Luôn dùng overload với Action<MediatRServiceConfiguration> như ví dụ trên.

Ví dụ cơ bản

Một ví dụ đơn giản: API lấy thông tin sản phẩm theo ID.

1. Định nghĩa Request

C#
public record GetProductByIdQuery(
    int Id
) : IRequest<ProductDto>;

2. Tạo Handler

C#
public class GetProductByIdHandler
    : IRequestHandler<GetProductByIdQuery, ProductDto>
{
    private readonly AppDbContext _db;

    public GetProductByIdHandler(AppDbContext db)
        => _db = db;

    public async Task<ProductDto> Handle(
        GetProductByIdQuery request,
        CancellationToken cancellationToken)
    {
        var product = await _db.Products
            .FindAsync(new object[] { request.Id }, cancellationToken)
            ?? throw new NotFoundException("Product", request.Id);

        return new ProductDto(product.Id, product.Name, product.Price);
    }
}

3. Gọi từ Controller

C#
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly ISender _sender;

    public ProductsController(ISender sender)
        => _sender = sender;

    [HttpGet("{id:int}")]
    public async Task<IActionResult> GetById(int id)
    {
        var result = await _sender.Send(new GetProductByIdQuery(id));
        return Ok(result);
    }
}
Inject ISender thay vì IMediator nếu controller chỉ cần Send(). Nguyên tắc Interface Segregation — chỉ phụ thuộc vào interface nhỏ nhất cần thiết.

Lợi ích chính

1 Loose Coupling +

Controller không biết handler nào xử lý request. Bạn có thể thay đổi, refactor, hoặc swap handler mà không cần sửa controller. Điều này đặc biệt hữu ích trong các dự án lớn có nhiều team cùng phát triển.

2 Single Responsibility +

Mỗi handler chỉ xử lý đúng một use case. Thay vì một ProductService chứa 20 methods, bạn có 20 handlers nhỏ gọn, mỗi handler là một file riêng, dễ navigate và dễ hiểu.

3 Testability +

Unit test một handler cực kỳ đơn giản: tạo instance handler, truyền dependencies (mock nếu cần), gọi Handle(), assert kết quả. Không cần mock MediatR, không cần setup controller pipeline.

4 Cross-Cutting Concerns +

Pipeline Behaviors cho phép bạn thêm logging, validation, caching, authorization... vào tất cả requests mà không sửa bất kỳ handler nào. Giống middleware trong ASP.NET nhưng ở tầng application logic.

5 CQRS-Ready +

MediatR là nền tảng tự nhiên cho CQRS (Command Query Responsibility Segregation). Bạn tách Command (ghi) và Query (đọc) thành các request riêng biệt, mỗi loại có handler và pipeline behavior riêng.

Khám phá chi tiết

Chọn chủ đề bạn muốn tìm hiểu sâu hơn: