Skip to main content

Relayer

Messaging for .NET that wraps your broker’s own client. Relayer handles retries, dead-lettering, the outbox, tracing and serialization. Kafka, Service Bus, SQS and RabbitMQ keep sending, ordering and settling messages the way they already do.

Early development. The core pipeline is implemented; transports and NuGet packages are not released yet.

Dekaf producer recordsent and settled by the native client

topic
orders.placed
key
c-42

CloudEvents envelopewritten by Relayer as native headers

ce_id
0193c0de-5b1a-7c2e-9f41-6a0d2b7e1c55
ce_type
orders.placed;v=1
ce_source
/orders
ce_time
2026-11-01T09:30:00.0000000Z
ce_specversion
1.0
traceparent
00-4bf92f35…-00f067aa…-01

OrderPlacedyour record, serialized with source-generated JSON

{ "orderId": "A-1001", "customerId": "c-42" }
Switch the transport: the envelope and your message stay the same. Only the native client changes.

Who does what

Relayer is not another broker abstraction. It owns the work that is the same on every broker and leaves the rest to the client you already trust.

Relayer

Serialization
System.Text.Json source generation by default, so Native AOT works.
Message identity
Stable names like orders.placed;v=1 on the wire, never CLR type names.
Failure handling
Classification, immediate retries, delayed redelivery, dead-letter copies with full history.
Outbox and inbox
Sends inside a transaction are enlisted automatically.
Telemetry
OpenTelemetry messaging conventions, with one ActivitySource and one Meter.
Startup validation
Routes, handlers and endpoint capabilities are checked before any consumer starts.

The native client

Sending and receiving
Dekaf, the Azure SDK, the AWS SDK and RabbitMQ.Client do the I/O.
Settlement
Complete, abandon, ack, commit. Relayer only decides which one.
Ordering
Partition keys, sessions and FIFO message groups, used as they are.
Dead-letter queues
Native DLQs where the broker has them, with error metadata attached.
Configuration
ProducerBuilder, ServiceBusClientOptions, ConnectionFactory, passed through unchanged.
The raw message
Always one call away from a handler, through the feature collection.

A message, a handler, a route

Registration is explicit. There is no assembly scanning: the source generator writes the registration code, and you can read it.

  • Analyzers catch a missing message name or an unregistered handler at compile time.
  • Startup fails with every configuration error listed together, before a consumer runs.
  • An ordering key on an endpoint that cannot order messages is an error, not a silent no-op.
Walk through it step by step
Program.cs
[Message("orders.placed", Version = 1)]
public sealed record OrderPlaced(string OrderId, string CustomerId);

public sealed class OrderPlacedHandler(AppDbContext db) : IHandler<OrderPlaced>
{
public async ValueTask HandleAsync(OrderPlaced message, MessageContext context,
CancellationToken cancellationToken)
{
db.Invoices.Add(Invoice.For(message));
await db.SaveChangesAsync(cancellationToken);
}
}

services.AddRelayer(relayer =>
{
relayer.AddMessageTypesFromThisAssembly();
relayer.AddTransport<BrokerOptions, RouteOptions, ConsumerOptions>("events", broker =>
{
broker.Send<OrderPlaced>("orders.placed",
route => route.OrderingKey(static m => m.CustomerId));
broker.Consume("orders.placed",
consumer => consumer.Handler<OrderPlacedHandler>().Concurrency(8));
});
});

await sender.SendAsync(new OrderPlaced("A-1001", "c-42"), cancellationToken);

Where it stands

Relayer is built in the open. This is what works on main today.

Configuration, routes and startup validationImplemented
Source-generated handlers, messages and JSONImplemented
Batch dispatch and receive executor compositionImplemented core seam
Send pipeline and middlewareImplemented
Consume pipeline and receive SPIImplemented
Failure classification, retries and dead-letter copiesImplemented
Declared topology and JSON exportImplemented
In-memory transport for tests and samplesImplemented
Kafka, Service Bus, SQS/SNS and RabbitMQ transportsIn progress
Health checks with application-provided adaptersImplemented
Built-in health adaptersPlanned
EF Core and PostgreSQL outbox and inboxPlanned
Core tracing, consume metrics and failure logsImplemented
Native transport and outbox telemetryPlanned
See the full roadmap