Skip to content

qorpe/mediator

Repository files navigation

Qorpe.Mediator

Enterprise-Grade CQRS Mediator for .NET

NuGet NuGet Downloads Build License: MIT .NET

A production-ready CQRS mediator library for .NET with Result pattern, pipeline behaviors, DDD support, and attribute-based HTTP endpoint mapping.


Why Qorpe.Mediator?

  • Result Pattern built-in — No more throwing exceptions for control flow
  • Explicit CQRSICommand<T>, IQuery<T> instead of just IRequest
  • 10 built-in behaviors — Audit, logging, validation, auth, transactions, retry, caching, performance, idempotency, cache invalidation
  • Attribute-based HTTP endpoints[HttpEndpoint] eliminates controller boilerplate
  • DDD nativeIDomainEvent, aggregate root patterns
  • Publish performance — Up to 66% faster notification fanout with 4.7x less memory (benchmarks)
  • 221 tests — Unit, integration, load, E2E

Performance

Benchmarked against MediatR v12 using BenchmarkDotNet. Full results: docs/BENCHMARKS.md

Publish (Notification Fanout) — Qorpe wins

Handlers Qorpe MediatR v12 Result
1 handler 24 ns / 88 B 46 ns / 288 B 48% faster, 3.3x less memory
10 handlers 69 ns / 376 B 187 ns / 1,656 B 63% faster, 4.4x less memory
100 handlers 532 ns / 3,256 B 1,552 ns / 15,336 B 66% faster, 4.7x less memory

Send (Pipeline) — Equal speed, Qorpe uses less memory

Behaviors Qorpe MediatR v12 Result
1 behavior 61 ns / 288 B 59 ns / 368 B ~equal speed, 22% less memory
3 behaviors 89 ns / 560 B 90 ns / 656 B Qorpe 1% faster, 15% less memory
5 behaviors 119 ns / 832 B 118 ns / 944 B ~equal speed, 12% less memory

Quick Start

1. Install

dotnet add package Qorpe.Mediator

2. Register

builder.Services.AddQorpeMediator(cfg =>
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));

3. Define a Command

public record CreateOrderCommand(string UserId, List<OrderItem> Items)
    : ICommand<Result<Guid>>;

4. Handle it

public class CreateOrderHandler : ICommandHandler<CreateOrderCommand, Result<Guid>>
{
    public ValueTask<Result<Guid>> Handle(CreateOrderCommand request, CancellationToken ct)
    {
        var orderId = Guid.NewGuid();
        // ... create order
        return new ValueTask<Result<Guid>>(Result<Guid>.Success(orderId));
    }
}

5. Send

var result = await mediator.Send(new CreateOrderCommand("user-1", items));
result.Match(
    id => Console.WriteLine($"Order created: {id}"),
    error => Console.WriteLine($"Failed: {error}")
);

Features

CQRS Separation

// Commands — change state
public record CreateOrder(string UserId) : ICommand<Result<Guid>>;
public record CancelOrder(Guid Id) : ICommand<Result>;

// Queries — read state, no side effects
public record GetOrderById(Guid Id) : IQuery<Result<Order>>;

// Streaming
public record SearchOrders(string? Status) : IStreamRequest<Order>;

Result Pattern

// Success
return Result<Guid>.Success(orderId);

// Failure with typed errors
return Error.NotFound("Order.NotFound", "Order not found");

// Functional operations
var name = result.Map(order => order.Name)
                 .Bind(name => ValidateName(name))
                 .Match(
                     name => $"Hello, {name}",
                     error => $"Error: {error.Description}");

Domain Events

public record OrderCreatedEvent(Guid OrderId, string UserId) : IDomainEvent
{
    public DateTimeOffset OccurredOn { get; } = DateTimeOffset.UtcNow;
}

// Multiple handlers per event
public class SendEmail : INotificationHandler<OrderCreatedEvent> { ... }
public class UpdateInventory : INotificationHandler<OrderCreatedEvent> { ... }

await publisher.Publish(new OrderCreatedEvent(orderId, userId));

HTTP Endpoint Mapping

[HttpEndpoint("POST", "/api/orders", Tags = new[] { "Orders" })]
[Transactional]
[Auditable]
public record CreateOrderCommand : ICommand<Result<Guid>> { ... }

// In Program.cs
app.MapQorpeEndpoints(typeof(Program).Assembly);
// Result auto-mapped: Success->200/201, Validation->400, NotFound->404, etc.

Pipeline Behaviors

All behaviors are attribute-driven, configurable, and automatically ordered via IBehaviorOrder.

# Behavior Attribute Order Description
1 Audit [Auditable] 100 Async batching, store abstraction, sensitive data masking
2 Logging Auto 200 Structured logging, auto-mask, circular reference safe
3 UnhandledException Auto 300 Catch-all safety net, always re-throws
4 Authorization [Authorize] 400 Role + policy checking, Result-based responses
5 Validation Auto 500 FluentValidation multi-validator, Result.Failure
6 Idempotency [Idempotent] 600 SHA256 key, per-key locking, window-based expiry
7 Transaction [Transactional] 700 Command-only, rollback, distinct commit/handler errors
8 Performance [PerformanceThreshold] 800 Per-request thresholds, 30s hard ceiling
9 Retry [Retryable] 900 Exponential backoff with jitter, success attempt logging
10 Caching [Cacheable] 1000 Query-only, bounded lock pool, stampede prevention
11 Cache Invalidation [InvalidatesCache] 1001 Command-driven cache invalidation by key prefix

Full Configuration

builder.Services.AddQorpeMediator(cfg =>
{
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
    cfg.NotificationPublishStrategy = NotificationPublishStrategy.Parallel;
    cfg.EnablePolymorphicNotifications = true;
    cfg.ValidateOnStartup = true;
});

builder.Services.AddQorpeValidation(typeof(Program).Assembly);

builder.Services.AddQorpeAllBehaviors(opts =>
{
    opts.ConfigureLogging = log =>
    {
        log.MaskProperties.Add("CardNumber");
        log.MaxSerializedLength = 4096;
    };
    opts.ConfigurePerformance = perf =>
    {
        perf.WarningThresholdMs = 500;
        perf.CriticalThresholdMs = 5000;
    };
});

MediatR vs Qorpe.Mediator

Feature MediatR v12 Qorpe.Mediator
License Commercial (2025+) MIT
Publish Performance Baseline Up to 66% faster
Publish Memory Baseline Up to 4.7x less
Result Pattern No (exceptions) Built-in Result<T>
CQRS Types IRequest only ICommand, IQuery, IRequest
Domain Events INotification IDomainEvent + INotification
Pre/Post Processors Defined, wired Defined, wired
Behavior Ordering Registration order Explicit IBehaviorOrder
Polymorphic Notifications No Opt-in
Startup Validation No ValidateOnStartup
Built-in Behaviors 0 11
HTTP Endpoints No [HttpEndpoint] attribute
Cache Invalidation No [InvalidatesCache] attribute
ValueTask No (Task) Yes (ValueTask)
Cancellation Diagnostics No Pipeline stage tracking
Stream Pipeline Behaviors No IStreamPipelineBehavior
Sensitive Data No [SensitiveData] auto-mask
Telemetry Yes None

Test Coverage

Layer Tests What It Covers
Unit 182 Result, Error, Guard, Mediator, all behaviors, notifications, validation, pre/post processors
Integration 21 Full pipeline E2E, HTTP endpoints, cross-behavior, DI registration
Load 18 50K concurrent, 500K sequential, memory stability, streaming, latency percentiles
Total 221 Production-grade coverage

Packages

Package Description
Qorpe.Mediator Core — CQRS abstractions, Result pattern, Mediator implementation
Qorpe.Mediator.Behaviors 11 built-in pipeline behaviors
Qorpe.Mediator.FluentValidation FluentValidation integration — auto-discovery, multi-validator
Qorpe.Mediator.AspNetCore HTTP endpoint mapping — [HttpEndpoint], Result-to-HTTP, OpenAPI
Qorpe.Mediator.Contracts Shared contracts for multi-project solutions

Sample Project

See tests/Qorpe.Mediator.Sample.ECommerce/ for a complete e-commerce example with:

  • Order aggregate root with domain events
  • Commands with transactions, audit, authorization, idempotency, and retry
  • Queries with caching and streaming
  • FluentValidation validators
  • Attribute-based HTTP endpoint mapping
  • Full behavior pipeline configuration

Migration from MediatR

See docs/MIGRATION_GUIDE.md for a step-by-step migration guide.


Contributing

Contributions are welcome! Please open an issue or submit a pull request.


License

MIT License

About

Enterprise-grade CQRS mediator for .NET — Result pattern, pipeline behaviors, DDD-ready.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages