Extensii și pattern-uri suplimentare în CQRS
Doru Bulubasa
06 October 2025

După ce o arhitectură CQRS este stabilă, următorul pas natural este rafinarea ei prin extensii și pattern-uri care o fac mai robustă, mai testabilă și mai ușor de întreținut. În această etapă, ne concentrăm pe trei direcții-cheie: decoratori peste MediatR, event publishing și audit logging. Toate acestea adresează nevoi comune ale aplicațiilor enterprise, fără să compromită separarea clară dintre comenzi și interogări.


🎯 1. Decorators peste MediatR pentru Logging, Retry și Validation

Unul dintre cele mai elegante moduri de a introduce comportamente comune în pipeline-ul aplicației este prin decoratori. În ecosistemul MediatR, acest lucru se realizează prin implementarea interfeței IPipelineBehavior<TRequest, TResponse>.

Această interfață acționează ca un middleware care interceptează toate cererile trimise către MediatR, permițând adăugarea logicilor cross-cutting – adică funcționalități care traversează mai multe componente, precum:

  • Logging – urmărirea cererilor și răspunsurilor pentru depanare și monitorizare;

  • Retry – reexecutarea unei comenzi în caz de erori tranzitorii;

  • Validation – validarea cererilor înainte de procesare.

Exemplu simplu de decorator pentru logging:

public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;

    public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
        => _logger = logger;

    public async Task<TResponse> Handle(
        TRequest request, 
        RequestHandlerDelegate<TResponse> next, 
        CancellationToken cancellationToken)
    {
        _logger.LogInformation("Handling {Request}", typeof(TRequest).Name);
        var response = await next();
        _logger.LogInformation("Handled {Request}", typeof(TRequest).Name);
        return response;
    }
}

Această abordare permite adăugarea de comportamente fără a „polua” handler-ele individuale. Cu un simplu services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));, toate comenzile și interogările beneficiază automat de logging centralizat.


📢 2. Event Publishing: Domain Events vs Integration Events

Evenimentele sunt o componentă naturală a modelului Domain-Driven Design, iar în contextul CQRS, ele capătă un rol major. În esență, vorbim despre două categorii:

  • Domain Events – reprezintă ceva ce s-a întâmplat în interiorul domeniului. De exemplu, PostCreatedDomainEvent poate fi declanșat atunci când o postare nouă este creată.

  • Integration Events – sunt utilizate pentru a notifica alte sisteme externe sau microservicii despre o schimbare. Ele traversează limitele aplicației.

Pentru Domain Events, MediatR oferă o integrare elegantă prin interfața INotification și handler-ele asociate INotificationHandler<TNotification>.

public class PostCreatedDomainEvent : INotification
{
    public Guid PostId { get; }
    public string Title { get; }

    public PostCreatedDomainEvent(Guid postId, string title)
    {
        PostId = postId;
        Title = title;
    }
}

public class PostCreatedEventHandler : INotificationHandler<PostCreatedDomainEvent>
{
    private readonly ILogger<PostCreatedEventHandler> _logger;
    public PostCreatedEventHandler(ILogger<PostCreatedEventHandler> logger) => _logger = logger;

    public Task Handle(PostCreatedDomainEvent notification, CancellationToken cancellationToken)
    {
        _logger.LogInformation("New post created: {Title}", notification.Title);
        return Task.CompletedTask;
    }
}

Astfel, domeniul rămâne coerent, iar logica de reacție la evenimente poate fi extinsă fără a afecta nucleul aplicației.

Pentru Integration Events, este recomandată o abordare asincronă – de exemplu, publicarea în RabbitMQ, Azure Service Bus sau Kafka – pentru a decupla complet comunicația între aplicații.


🧾 3. Audit Logging la comenzi

În multe aplicații enterprise, urmărirea acțiunilor utilizatorilor este o cerință obligatorie, mai ales în medii financiare, medicale sau guvernamentale. În CQRS, audit logging-ul poate fi implementat elegant tot printr-un pipeline behavior sau direct în handler-ele de comandă.

O abordare simplă presupune crearea unei entități AuditLog și salvarea evenimentelor într-o tabelă auxiliară sau într-un Event Store dedicat.

public class AuditBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    private readonly IAuditService _auditService;

    public AuditBehavior(IAuditService auditService)
        => _auditService = auditService;

    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken cancellationToken)
    {
        var response = await next();
        await _auditService.SaveAsync(request, response);
        return response;
    }
}

Prin această metodă, fiecare comandă trimisă prin MediatR lasă o urmă în baza de date – cu detalii despre cine a făcut acțiunea, când și ce s-a modificat.

Această transparență ajută la depanare, securitate și conformitate cu standarde precum GDPR sau ISO 27001.


🔚 Concluzie

Extensiile și pattern-urile din această etapă duc arhitectura CQRS la un nivel matur.

Prin decoratori, evenimente și audit logging, aplicația devine:

  • Scalabilă – logica comună este centralizată;

  • Observabilă – toate acțiunile pot fi urmărite și analizate;

  • Ușor de extins – adăugarea unui nou comportament global nu afectează handler-ele existente.

CQRS nu înseamnă doar separarea dintre comenzi și interogări, ci și crearea unui cadru solid care facilitează mentenanța, auditul și extensibilitatea pe termen lung.