Implementarea bazelor CQRS în .NET

  • Doru Bulubasa
  • 09 July 2025

După ce am înțeles conceptele de bază și structura recomandată, e timpul să trecem la implementare. În această etapă, vom configura MediatR și vom crea primele comenzi și interogări (queries) într-o aplicație .NET, folosind un exemplu simplu: Post.


3️⃣ Instalarea MediatR

Pentru a separa responsabilitățile și a evita legături directe între layere, folosim MediatR – o bibliotecă care facilitează trimiterea comenzilor și interogărilor către handler-ele dedicate.

🛠️ Instalare

În proiectul MyApp.Application, adaugă MediatR:

dotnet add package MediatR

dotnet add package MediatR.Extensions.Microsoft.DependencyInjection

În MyApp.API (sau proiectul de UI/API), configurează serviciile în Program.cs:

builder.Services.AddMediatR(cfg =>

    cfg.RegisterServicesFromAssembly(typeof(MyApp.Application.AssemblyReference).Assembly));

Adaugă o clasă de referință în MyApp.Application:

namespace MyApp.Application;

public static class AssemblyReference { }


4️⃣ Crearea unei comenzi simple: CreatePostCommand

🧾 Command: CreatePostCommand.cs

using MediatR;

public record CreatePostCommand(string Title, string Content) : IRequest<Guid>;

IRequest<Guid> înseamnă că această comandă va returna un Guid (ID-ul postării create)


🧠 Handler: CreatePostCommandHandler.cs

public class CreatePostCommandHandler : IRequestHandler<CreatePostCommand, Guid>
{
    private readonly IApplicationDbContext _context;

    public CreatePostCommandHandler(IApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<Guid> Handle(CreatePostCommand request, CancellationToken cancellationToken)
    {
        var post = new Post
        {
            Id = Guid.NewGuid(),
            Title = request.Title,
            Content = request.Content,
            CreatedAt = DateTime.UtcNow
        };

        _context.Posts.Add(post);
        await _context.SaveChangesAsync(cancellationToken);

        return post.Id;
    }
}


5️⃣ Separarea completă pentru o interogare: GetPostByIdQuery

🔍 Query: GetPostByIdQuery.cs

using MediatR;

public record GetPostByIdQuery(Guid Id) : IRequest<PostDto>;

PostDto este un obiect doar pentru afișare – fără logică de business.


🔄 Handler: GetPostByIdQueryHandler.cs

public class GetPostByIdQueryHandler : IRequestHandler<GetPostByIdQuery, PostDto?>
{
    private readonly IApplicationDbContext _context;

    public GetPostByIdQueryHandler(IApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<PostDto?> Handle(GetPostByIdQuery request, CancellationToken cancellationToken)
    {
        var post = await _context.Posts
            .AsNoTracking()
            .Where(p => p.Id == request.Id)
            .Select(p => new PostDto(p.Id, p.Title, p.Content, p.CreatedAt))
            .FirstOrDefaultAsync(cancellationToken);

        return post;
    }
}


📦 PostDto.cs (în Application)

public record PostDto(Guid Id, string Title, string Content, DateTime CreatedAt);


6️⃣ Rezumat: IRequest și IRequest

Tip de request

Interfață

Returnează

Command (scriere)

IRequest<Unit>

Nimic (Unit.Value)

Command cu rezultat

IRequest<Guid>

De exemplu: ID-ul creat

Query (citire)

IRequest<PostDto>

Un rezultat de tip DTO


🧪 Exemplu de folosire din Controller / UI

[HttpPost]
public async Task<IActionResult> Create(CreatePostCommand command)
{
    var id = await _mediator.Send(command);
    return CreatedAtAction(nameof(GetById), new { id }, null);
}

[HttpGet("{id}")]
public async Task<ActionResult<PostDto>> GetById(Guid id)
{
    var post = await _mediator.Send(new GetPostByIdQuery(id));
    return post is not null ? Ok(post) : NotFound();
}


🧠 Concluzie

În această etapă am implementat primele piese esențiale ale unui sistem CQRS:

  • ✅ Comenzi care modifică starea aplicației

  • ✅ Interogări separate pentru citire

  • ✅ Handler-e dedicate și independente

  • ✅ MediatR ca intermediar central

🏁 Ce urmează

În următorul articol vom adăuga:

  • ✅ Validări cu FluentValidation

  • ✅ Mapping între DTO-uri și entități

  • ✅ Separarea clară între WriteModel și ReadModel

🤖 Întreabă AI despre acest articol

AI Răspuns generat de AI pe baza acestui articol.
AI scrie răspunsul…

Scrie un comentariu

Adresa de mail nu va fi publicata. Campurile obligatorii sunt marcate cu *