Opinionated and Simplified Minimal APIs for Consuming Messages from RabbitMQ, Ensuring No Crucial Configurations Are Hidden.
Oragon.RabbitMQ provides everything you need to create resilient RabbitMQ consumers without the need to study numerous books and articles or introduce unknown risks to your environment.
public class BusinessService
{
public async Task DoSomethingAsync(BusinessCommandOrEvent commandOrEvent)
{
... business core ...
}
}
builder.Services.AddSingleton<BusinessService>();
builder.Services.AddSingleton<IAMQPSerializer, SystemTextJsonAMQPSerializer>();
builder.Services.MapQueue<BusinessService, BusinessCommandOrEvent>(config => config
.WithDispatchInRootScope()
.WithAdapter((svc, msg) => svc.DoSomethingAsync(msg))
.WithQueueName("events")
.WithPrefetchCount(1)
);
builder.Services.AddScoped<BusinessService>();
builder.Services.AddSingleton<IAMQPSerializer, SystemTextJsonAMQPSerializer>();
builder.Services.MapQueue<BusinessService, BusinessCommandOrEvent>(config => config
.WithDispatchInChildScope()
.WithAdapter((svc, msg) => svc.DoSomethingAsync(msg))
.WithQueueName("events")
.WithPrefetchCount(1)
);
builder.Services.AddKeyedScoped<BusinessService>("key-of-service-1");
builder.Services.AddKeyedScoped("key-of-service-2", (sp, key) => new BusinessService(... custom dependencies ...));
builder.Services.AddSingleton<IAMQPSerializer, NewtonsoftAMQPSerializer>();
builder.Services.MapQueue<BusinessService, BusinessCommandOrEvent>(config => config
.WithDispatchInChildScope()
.WithKeyedService("key-of-service-1")
.WithAdapter((svc, msg) => svc.DoSomethingAsync(msg))
.WithQueueName("events1")
.WithPrefetchCount(1)
);
builder.Services.MapQueue<BusinessService, BusinessCommandOrEvent>(config => config
.WithDispatchInChildScope()
.WithKeyedService("key-of-service-2")
.WithAdapter((svc, msg) => svc.DoSomethingAsync(msg))
.WithQueueName("events2")
.WithPrefetchCount(1)
);
This approach is designed to decouple RabbitMQ consumers from business logic, ensuring that business code remains unaware of the queue consumption context.
The result is incredibly simple, decoupled, agnostic, more reusable, and highly testable code.
This consumer is focused on creating a resilient consumer using manual acknowledgments.
- The flow produces a
BasicReject
without requeue for serialization failures (e.g., incorrectly formatted messages), you will use dead-lettering to ensure these messages are not lost. - The flow produces a
BasicNack
with requeue for processing failures, allowing for message reprocessing. - Minimal API design style made without reflection
- Extensible with support for custom serializers and encoders
Full support for OpenTelemetry on publishing or consuming RabbitMQ messages.
Refactored to use RabbitMQ.Client 7x (with IChannel instead IModel)