Nuvora Nexus · .NET 10 · PostgreSQL-native

Every message finds its way.
Nothing gets lost.

Relay is the batteries-included DDD / CQRS / event-sourcing framework for services with real domain behavior — orders, payments, ledgers, bookings. One mediator pipeline in, durable guarantees out.

dotnet add package Nuvora.Nexus.Relay

The shape of a Relay service

Three moves. The pipeline does the rest.

No handler registration, no plumbing copy-paste. Authorization, validation, transactions and the outbox wrap every message automatically — the hundredth command ships as safely as the first.

[RelayHttpPost("/products")]
[RelayHttpTag("Catalog")]
public sealed record CreateProductCommand(
    string Name, string Category, decimal Price) : ICommand<Product>;

public sealed class CreateProductCommandHandler(ProductCatalog catalog)
    : ICommandHandler<CreateProductCommand, Product>
{
    public Task<Product> Handle(
        CreateProductCommand command, CancellationToken ct)
    {
        var product = new Product(
            Id: $"p-{Guid.NewGuid():N}",
            Name: command.Name.Trim(),
            Category: command.Category.Trim().ToLowerInvariant(),
            Price: command.Price);

        return Task.FromResult(catalog.Save(product));
    }
}
public sealed record OrderPlaced(
    Guid OrderId, string Customer, decimal Total) : DomainEvent
{
    public override Guid AggregateId => OrderId;
}

[AggregateType("orders.order")]
public sealed class Order : AggregateRoot<Guid>
{
    public static Order Place(Guid id, string customer, decimal total)
    {
        Guard.AgainstNullOrWhiteSpace(customer, nameof(customer));
        Guard.AgainstNegativeOrZero(total, nameof(total));

        var order = new Order();
        order.RaiseEvent(new OrderPlaced(id, customer.Trim(), total));
        return order;
    }

    // MarkPaid / Ship / Cancel / Refund each RaiseEvent(...)
    // State is rebuilt from the event stream — full history, for free.
}
var builder = WebApplication.CreateBuilder(args);

// Scans the assembly once: every handler, validator and
// pipeline behavior is discovered and wired automatically.
builder.Services.AddRelay(typeof(CreateProductCommand).Assembly);

var app = builder.Build();

// ProblemDetails + correlation ids around everything downstream.
app.UseRelayExceptionHandling();

app.UseRouting();
app.UseEndpoints(endpoints =>
{
    // One HTTP endpoint per [RelayHttp*]-annotated message.
    endpoints.MapRelayEndpoints();
});

app.Run();

Batteries included

The production concerns are the framework.

Mediator & CQRS

One message, one handler, one pipeline. Authorization, validation, logging and transactions wrap every operation uniformly.

Event sourcing

An append-only PostgreSQL event store with snapshots, optimistic concurrency, upcasters and time-travel reads.

Projections

Async read models with checkpoints, lag monitoring, sharded lanes and zero-downtime blue/green rebuilds.

Outbox & inbox

No dual writes, no duplicate side effects: atomic publish on the way out, idempotent consumption on the way in.

Eight broker transports

RabbitMQ, Kafka, Azure Service Bus, Amazon SQS, NATS, Pulsar, ActiveMQ/NMS and in-memory — behind one abstraction.

Sagas & routing slips

Imperative sagas, declarative state machines, and routing slips with LIFO compensation — distributed transactions without 2PC.

Durable scheduling

Delayed commands, recurring cron jobs and redelivery that survive restarts and never run twice across a cluster.

Multi-tenancy

Fail-closed tenant resolution and enforcement, from shared tables with RLS to schema- or database-per-tenant.

Fail-closed authorization

Every message declares its authorization posture — an undecorated command refuses to even start the host.

Query caching

Declarative [Cacheable] queries with stampede protection, backed by memory or Redis.

Observability & resiliency

OpenTelemetry traces and metrics, health checks, retries, circuit breakers, rate and concurrency limits.

Relay Watch

A live control plane: topology, sagas, projections, DLQs, alerts and audit — streamed to a React dashboard.

One abstraction, eight transports

RabbitMQKafkaAzure Service BusAmazon SQSNATSPulsarActiveMQ / NMSIn-Memory

Relay, in stories

Three companies. Thirty ways it goes wrong.

Read with a coffee, not a compiler: three seasons following small teams into the problems every team eventually hits — and how Relay changes the ending.

37tutorial articles
37runnable samples
30stories, 3 seasons
40NuGet packages
MITlicensed, by Nuvora Labs