Quando começas a trabalhar com sistemas distribuídos, uma das primeiras necessidades que surge é a capacidade de comunicar entre serviços de forma assíncrona e desacoplada. É aqui que entram o RabbitMQ como message broker e o MassTransit como framework que simplifica toda a integração no ecossistema .NET. Num outro post introduzimos o RabbitMQ e fizemos a sua instalação. Neste, vamos ver como configurar o masstransit e um primeiro exemplo de como tudo funciona. Vamos montar o cenário mais básico — mas totalmente funcional — para perceberes como tudo encaixa: configurar o bus, publicar uma mensagem e consumi‑la num consumer.
O fluxo básico de trabalho com MassTransit é sempre o mesmo:
- Definimos o contrato da mensagem (o evento).
- Criamos um consumer que sabe lidar com esse evento.
- Configuramos o bus do MassTransit para usar RabbitMQ.
- Publicamos uma mensagem e deixamos o consumer tratá‑la.
A primeira coisa é criar um projeto .NET (uma Web API serve perfeitamente) e o RabbitMQ a correr localmente. Para verificar se o RabbitMQ está a correr, basta aceder a http://localhost:15672 (se seguiste os passos de instalação que partilhei). Depois instalas os pacotes MassTransit e MassTransit.RabbitMQ, que são o núcleo da integração. A partir daqui, o fluxo é sempre o mesmo: defines o contrato da mensagem, crias um consumer que sabe lidar com ela e configuras o MassTransit para ligar tudo ao RabbitMQ.
Começamos pelo contrato. Uma mensagem nada mais é do que um record simples, imutável, que representa o evento que queres comunicar. Por exemplo:
public record OrderSubmitted
(
Guid OrderId,
string CustomerName,
decimal Total
);
Este objeto é o que vai viajar pela queue. Não tem lógica, não tem comportamento — apenas dados. Isto mantém o sistema previsível e fácil de evoluir.
Depois criamos o consumer, que é a peça responsável por reagir quando uma mensagem deste tipo chega ao RabbitMQ. O MassTransit trata de toda a parte chata: deserialização, binding, gestão de conexões. Por norma damos o nome da ação com Consumer no sufixo, mas pode ser qualquer coisa, na verdade. Esta classe é composta por IConsumer<T> em que T é o evento a consumir. E basta implementar o método Consume. Como argumento, temos um ConsumeContext<OrderSubmitted> context:. É neste contexto que vamos conseguir aceder ao nosso objecto para processar a informação. O nosso evento está em context.Message. é no Consume que vamos fazer todo o processamento necessário. Por exemplo: Envio de email de confirmação, ou gravar a informação na base de dados, ou chamar um outro serviço para enviar a encomenda para o armazém, por exemplo.
public class OrderSubmittedConsumer : IConsumer<OrderSubmitted>
{
private readonly ILogger<OrderSubmittedConsumer> _logger;
public OrderSubmittedConsumer(ILogger<OrderSubmittedConsumer> logger)
{
_logger = logger;
}
public Task Consume(ConsumeContext<OrderSubmitted> context)
{
var message = context.Message;
_logger.LogInformation(
"Order received. Id: {OrderId}, Customer: {Customer}, Total: {Total}",
message.OrderId,
message.CustomerName,
message.Total
);
return Task.CompletedTask;
}
}
Com o consumer criado, falta ligar tudo no Program.cs. É aqui que o MassTransit é registado no DI e onde configuramos o RabbitMQ como transporte. A configuração é bastante declarativa: defines o host, as credenciais e a queue onde o consumer vai escutar. Algo assim:
builder.Services.AddMassTransit(x =>
{
x.AddConsumer<OrderSubmittedConsumer>();
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host("localhost", "/", h =>
{
h.Username("guest");
h.Password("guest");
});
cfg.ReceiveEndpoint("order-submitted-queue", e =>
{
e.ConfigureConsumer<OrderSubmittedConsumer>(context);
});
});
});
O MassTransit faz o resto: cria a exchange, cria a queue, faz o binding e garante que o consumer está registado e pronto a receber mensagens. Não precisas de criar nada manualmente no RabbitMQ.
Para completar o ciclo, criamos um endpoint HTTP que publica uma mensagem. O MassTransit expõe o IPublishEndpoint, que é injetado automaticamente e sabe exatamente para onde enviar a mensagem:
app.MapPost("/orders", async (IPublishEndpoint publish, OrderRequest request) =>
{
var message = new OrderSubmitted(
Guid.NewGuid(),
request.CustomerName,
request.Total
);
await publish.Publish(message);
return Results.Accepted($"/orders/{message.OrderId}", new { message.OrderId });
});
Quando fazes um POST para /orders a API publica o evento, o RabbitMQ recebe‑o e o consumer processa‑o. O ciclo fica completo e tens o teu primeiro fluxo assíncrono a funcionar. No RabbitMq, no separador queues e streams vais encontrar a nova queue que foi criada com o nome order-submitted-queue. Neste caso, em que coloquei um breakpoint no consumer, podes ver que tem um total de 1 pedido e 1 pedido que está Unacked, o que significa que o pedido está a ser processado, mas ainda não terminou. Quando está ready na queue, significa que o pedido foi recebido, mas ainda não foi processado.

O mais interessante é que, apesar de simples, este padrão é exatamente o que se usa em sistemas reais: contratos bem definidos, consumidores isolados, filas dedicadas e um bus que abstrai toda a complexidade. A partir daqui podes evoluir para retries, dead‑letter queues, sagas, routing avançado e muito mais — mas a base é sempre esta.