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.

Scrie un comentariu

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