04 Behaviors and Pipeline - vagnerjsmello/NexMediator GitHub Wiki

NexMediator Logo

🧩 Pipeline Behaviors

NexMediator supports ordered, pluggable pipeline behaviors for cross-cutting concerns.

Each behavior runs before and/or after the request handler. Behaviors are executed in the order you define when registering AddNexMediator(...).

🔢 Execution Order Example

services.AddNexMediator(options =>
{
    options.AddBehavior(typeof(LoggingBehavior<,>), 1);
    options.AddBehavior(typeof(FluentValidationBehavior<,>), 2);
    options.AddBehavior(typeof(CachingBehavior<,>), 3);
    options.AddBehavior(typeof(TransactionBehavior<,>), 4);
});

🪵 LoggingBehavior<,>

Adds logging context for each request.
Optionally, it can also include X-Correlation-ID tracking if enabled.

Optional: Correlation ID support

To enable correlation tracking, pass useCorrelationId: true to the behavior’s constructor:

options.AddBehavior(typeof(LoggingBehavior<,>), 1);

If correlation is enabled, you also need:

services.AddScoped<ICorrelationContext, CorrelationContext>();
app.UseMiddleware<CorrelationMiddleware>();

This will read/write the X-Correlation-ID HTTP header and push it into your logs.

Logging implementation (agnostic)

NexMediator is agnostic about the logging provider. In the AuctionBoardGame sample, logging is done via Serilog, but you could also use:

  • Microsoft.Extensions.Logging
  • Azure Application Insights
  • Elastic (ELK), Seq, or others

✅ FluentValidationBehavior<,>

Runs all FluentValidation validators before executing the handler.

If validation fails, the pipeline is short-circuited and the error is returned.

Requires:

  • Reference to FluentValidation
  • Validators implementing IValidator<T>
public class CreateAuctionValidator : AbstractValidator<CreateAuctionCommand>
{
    public CreateAuctionValidator()
    {
        RuleFor(x => x.Title).NotEmpty();
        RuleFor(x => x.EndDate).GreaterThan(DateTime.UtcNow);
    }
}

🧠 CachingBehavior<,>

Caches the response of any query implementing ICacheableRequest<T>.

NexMediator checks the CacheKey and Expiration properties of the request.

Requires:

services.AddMemoryCache();
services.AddScoped<ICache, MemoryRequestCache>();

Implementation (agnostic)

The interface ICache is defined in NexMediator.Abstractions.
In the AuctionBoardGame example, it is implemented as:

public class MemoryRequestCache : ICache
{
    private readonly IMemoryCache _cache;

    public Task<T?> GetAsync<T>(string key, CancellationToken ct) { ... }
    public Task SetAsync<T>(string key, T value, int? ttl, CancellationToken ct) { ... }
    public Task RemoveAsync(string key, CancellationToken ct) { ... }
    public Task RemoveByPrefixAsync(string prefix, CancellationToken ct) { ... }
}

This could be swapped for:

  • Redis cache
  • SQL-based cache
  • Distributed memory
  • File-based cache

Just implement ICache.

🔐 TransactionBehavior<,>

Wraps request execution in a transaction scope when request implements ITransactionalRequest<T>.

Requires:

services.AddScoped<ITransactionManager, EfCoreTransactionManager>();

Implementation (agnostic)

The EfCoreTransactionManager used in the example looks like this:

public class EfCoreTransactionManager : ITransactionManager
{
    public async Task BeginTransactionAsync(CancellationToken ct) => ...;
    public async Task CommitTransactionAsync(CancellationToken ct) => ...;
    public async Task RollbackTransactionAsync(CancellationToken ct) => ...;
}

You could replace it with:

  • Dapper-based transaction
  • MongoDB transaction
  • Cosmos DB or any other transactional service

As long as it implements ITransactionManager.

⚙️ Custom Behaviors

You can add your own behavior by implementing:

public class CustomBehavior<TRequest, TResponse> : INexPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
    {
        // Before handler
        var response = await next();
        // After handler
        return response;
    }
}

Then register:

options.AddBehavior(typeof(CustomBehavior<,>), 99);

Pipeline behaviors help enforce cross-cutting concerns with minimal boilerplate.
Combined with NexMediator’s ordering system and DI-first design, they give you full control over the request lifecycle.

Next → Notifications

⚠️ **GitHub.com Fallback** ⚠️