Azure Service Bus: filas, tópicos e quando usar cada um

Azure Service Bus: filas, tópicos e quando usar cada um

Topicos: Microsoft Azure

Publicado em 21/10/2026 por Admin

Em aplicações modernas, especialmente em arquiteturas baseadas em microsserviços, nem todas as operações precisam acontecer imediatamente ou de forma síncrona. Em muitos cenários, é mais interessante permitir que um serviço envie uma mensagem e continue a sua execução, enquanto outro componente processa essa informação de forma independente.

É nesse contexto que entra o Azure Service Bus, um serviço de mensageria gerenciado da Microsoft Azure que permite comunicar diferentes aplicações e componentes de forma confiável, reduzindo o acoplamento entre sistemas e facilitando a construção de arquiteturas distribuídas.

No entanto, ao começar a trabalhar com o Azure Service Bus, uma dúvida bastante comum surge: quando devemos utilizar uma fila (Queue) e quando faz mais sentido utilizar um tópico (Topic) com subscrições (Subscriptions)?

Embora os dois mecanismos permitam a comunicação assíncrona entre componentes, eles atendem a necessidades diferentes. Escolher a estrutura correta influencia diretamente a organização da arquitetura, a escalabilidade dos consumidores e a forma como os eventos são distribuídos pelos serviços.

Neste artigo, vamos entender como funciona o Azure Service Bus, quais são as diferenças entre filas e tópicos, como essas estruturas podem ser utilizadas em aplicações .NET e quais critérios considerar ao tomar essa decisão.

O que é o Azure Service Bus?

O Azure Service Bus é um serviço de mensageria empresarial totalmente gerenciado que permite trocar mensagens entre aplicações, serviços e sistemas distribuídos.

Em vez de obrigar dois componentes a comunicarem diretamente entre si, podemos utilizar o Service Bus como intermediário.

Imagine uma aplicação de comércio eletrônico desenvolvida em ASP.NET Core. Quando um cliente finaliza uma compra, o sistema precisa realizar várias operações:

  • Registar o pedido.

  • Processar o pagamento.

  • Atualizar o stock.

  • Enviar um e-mail de confirmação.

  • Atualizar o sistema de faturação.

  • Notificar outros serviços interessados na compra.

Uma abordagem tradicional poderia executar todas essas operações durante a mesma requisição HTTP. Isso aumenta o tempo de resposta e cria dependências entre componentes que deveriam ter maior autonomia.

Além disso, se o serviço responsável pelo envio de e-mails estiver indisponível, uma implementação excessivamente acoplada poderá comprometer o processamento de toda a compra.

Com o Azure Service Bus, a aplicação pode publicar uma mensagem indicando que um pedido foi criado. Os serviços responsáveis pelas tarefas seguintes recebem essa mensagem e executam as suas operações de forma independente, conforme o modelo de comunicação escolhido.

Essa abordagem oferece algumas vantagens importantes:

  • Desacoplamento entre produtores e consumidores.

  • Processamento assíncrono de operações.

  • Capacidade de absorver picos de solicitações.

  • Maior flexibilidade para escalar os componentes.

  • Mecanismos de entrega, confirmação e recuperação de mensagens.

  • Possibilidade de distribuir informações por vários consumidores.

O Service Bus disponibiliza diferentes funcionalidades para implementar esses cenários, mas duas das principais estruturas são as filas e os tópicos com subscrições.

A documentação oficial da Microsoft apresenta essas estruturas como os mecanismos centrais de mensageria do serviço.

O que são filas no Azure Service Bus?

Uma fila, ou Queue, permite implementar um modelo de comunicação conhecido como ponto a ponto.

Nesse modelo, um produtor envia uma mensagem para uma fila e um consumidor recebe essa mensagem para a processar.

A principal característica é que cada mensagem é processada por um único consumidor, mesmo que existam vários consumidores disponíveis a ler a mesma fila.

Isso não significa que apenas um consumidor possa trabalhar com a fila. Pelo contrário: podemos executar várias instâncias do mesmo serviço para aumentar a capacidade de processamento.

O Service Bus distribui as mensagens entre os consumidores concorrentes, permitindo que cada mensagem seja processada por um deles.

Como funciona uma fila?

Imagine que uma aplicação recebe pedidos de clientes e precisa processar cada um deles de forma assíncrona.

Podemos criar uma fila chamada orders-queue.

O fluxo seria o seguinte:

  1. A aplicação recebe uma solicitação para criar um pedido.

  2. O sistema publica uma mensagem na fila orders-queue.

  3. Um serviço consumidor recebe a mensagem.

  4. O consumidor processa o pedido.

  5. Após concluir o processamento com sucesso, confirma a mensagem ao Service Bus.

Visualmente, podemos representar o fluxo desta forma:

Aplicação ASP.NET Core → orders-queue → Serviço de processamento de pedidos

Se houver um grande volume de pedidos, podemos executar várias instâncias do serviço consumidor.

Por exemplo, três instâncias podem trabalhar simultaneamente, recebendo mensagens diferentes da mesma fila.

O objetivo é aumentar a capacidade de processamento sem obrigar a aplicação produtora a conhecer quantos consumidores existem ou onde estão executados.

Quando utilizar filas?

As filas são especialmente úteis quando cada mensagem representa uma tarefa que precisa de ser executada uma única vez no fluxo normal de processamento.

Alguns exemplos incluem:

  • Processamento de pedidos.

  • Geração de documentos.

  • Processamento de ficheiros enviados por utilizadores.

  • Envio de notificações individuais.

  • Execução de tarefas demoradas.

  • Integração entre sistemas que precisam de processar operações de forma assíncrona.

Imagine que uma aplicação recebe 5.000 pedidos durante uma campanha promocional.

Se o serviço de processamento conseguir tratar apenas uma parte desses pedidos por minuto, a fila permite armazenar as mensagens enquanto os consumidores trabalham para reduzir o volume pendente.

O produtor não precisa de executar todo o processamento antes de responder ao cliente, desde que a aplicação tenha uma estratégia adequada para confirmar a aceitação da operação.

Esse comportamento é conhecido como nivelamento de carga, ou load leveling.

A fila ajuda a absorver diferenças temporárias entre a velocidade com que as mensagens são produzidas e a velocidade com que são processadas.

É importante, porém, compreender que uma fila não aumenta automaticamente a capacidade de processamento. Se o volume de mensagens recebidas for persistentemente superior à capacidade dos consumidores, o número de mensagens pendentes continuará a crescer.

Nesse cenário, será necessário aumentar a capacidade dos consumidores, otimizar o processamento ou rever a arquitetura.

O que são tópicos e subscrições no Azure Service Bus?

Os tópicos, ou Topics, implementam um modelo de comunicação conhecido como publicação e subscrição, normalmente designado por publish/subscribe.

A principal diferença em relação às filas é que uma mensagem publicada num tópico pode ser disponibilizada a vários destinatários independentes.

Para isso, o Service Bus utiliza subscrições.

Cada subscrição funciona como uma estrutura de receção associada ao tópico. Quando uma mensagem é publicada, o Service Bus encaminha uma cópia para cada subscrição cujas regras correspondam à mensagem.

Os consumidores recebem as mensagens através das subscrições, e não diretamente do tópico.

Como funciona um tópico?

Vamos voltar ao exemplo do comércio eletrônico.

Quando um pedido é criado, diferentes serviços podem precisar de conhecer esse acontecimento:

  • O serviço de faturação precisa de preparar a fatura.

  • O serviço de notificações precisa de enviar um e-mail ao cliente.

  • O serviço de análise precisa de registar o evento.

  • O serviço de logística precisa de iniciar o processo de expedição.

Se utilizarmos apenas uma fila, os consumidores concorrentes irão distribuir as mensagens entre si. Isso não garante que todos os serviços recebam uma cópia de cada pedido.

Para distribuir o mesmo acontecimento por diferentes sistemas, podemos criar um tópico chamado order-events.

Em seguida, configuramos subscrições como:

  • billing-subscription

  • notifications-subscription

  • analytics-subscription

  • shipping-subscription

O fluxo fica assim:

Aplicação ASP.NET Core → order-events

A partir do tópico, as mensagens são encaminhadas para as subscrições correspondentes:

  • order-events → billing-subscription → Serviço de faturação

  • order-events → notifications-subscription → Serviço de notificações

  • order-events → analytics-subscription → Serviço de análise

  • order-events → shipping-subscription → Serviço de logística

Quando a aplicação publica uma mensagem sobre um novo pedido, cada subscrição elegível recebe a sua própria cópia.

Cada serviço pode processar essa mensagem independentemente dos restantes.

Se o serviço de análise estiver temporariamente indisponível, por exemplo, isso não significa que o serviço de faturação tenha de esperar pela conclusão da análise. Cada subscrição possui o seu próprio fluxo de processamento.

Essa independência é uma das principais vantagens do modelo de publicação e subscrição.

Quando utilizar tópicos?

Os tópicos são indicados quando um mesmo evento precisa de ser comunicado a vários sistemas ou componentes.

Alguns exemplos incluem:

  • Distribuição de eventos de negócio.

  • Comunicação entre microsserviços.

  • Notificação de vários sistemas sobre alterações de estado.

  • Integração com plataformas de faturação, logística e análise.

  • Arquiteturas orientadas a eventos.

  • Distribuição seletiva de mensagens para diferentes consumidores.

Imagine que uma aplicação permite alterar o endereço de um cliente.

Essa alteração pode interessar ao serviço de faturação, ao sistema de entregas e à plataforma de gestão de clientes.

Em vez de implementar chamadas diretas para cada sistema, a aplicação pode publicar um evento, como CustomerAddressChanged.

Cada subscrição recebe uma cópia da mensagem e executa o processamento necessário.

Dessa forma, a aplicação responsável pela alteração do endereço não precisa de conhecer todos os serviços que dependem dessa informação.

Se futuramente for criado um novo serviço que precise de receber esses eventos, basta avaliar a criação de uma nova subscrição e configurar o respetivo consumidor.

Essa flexibilidade ajuda a reduzir o acoplamento entre os componentes da arquitetura.

Azure Service Bus: principais diferenças entre filas e tópicos

A escolha entre filas e tópicos deve começar pela forma como a mensagem precisa de ser distribuída.

A tabela seguinte resume as diferenças mais importantes.

Característica Queue (fila) Topic + Subscription (tópico e subscrição)
Modelo de comunicação Ponto a ponto Publicação e subscrição
Distribuição Cada mensagem é processada por um consumidor no fluxo normal Uma mensagem pode ser disponibilizada a várias subscrições
Consumidores Vários consumidores concorrentes podem partilhar a mesma fila Cada subscrição pode ter os seus próprios consumidores concorrentes
Objetivo principal Distribuir tarefas para processamento Distribuir eventos por diferentes destinatários
Filtragem por destinatário Não existe filtragem de subscrições, como nos tópicos Permite configurar regras de filtragem por subscrição
Exemplos Processar pedidos, gerar documentos e executar tarefas Notificar serviços de faturação, logística e análise
Isolamento do processamento As mensagens são partilhadas entre consumidores da fila Cada subscrição mantém o seu próprio fluxo de mensagens
Complexidade Geralmente menor Pode exigir a gestão de vários tópicos, subscrições e regras

Existe um detalhe importante: uma fila não significa necessariamente um único consumidor, e um tópico não significa necessariamente que cada consumidor recebe todas as mensagens.

Numa fila, vários consumidores podem competir pelo processamento das mensagens.

Num tópico, as subscrições definem quais mensagens ficam disponíveis para cada fluxo de processamento. Dentro de uma mesma subscrição, vários consumidores podem competir pelas mensagens, tal como acontece numa fila.

A diferença essencial está na distribuição das mensagens entre os fluxos de processamento, e não simplesmente no número de instâncias executadas.

É possível filtrar mensagens em tópicos?

Sim. Uma das funcionalidades importantes do Azure Service Bus é a possibilidade de configurar regras de filtragem nas subscrições dos tópicos.

Isso permite distribuir mensagens de acordo com propriedades ou critérios definidos pela aplicação.

Imagine um sistema que publica eventos relacionados com diferentes tipos de pedidos:

  • Pedidos nacionais.

  • Pedidos internacionais.

  • Pedidos prioritários.

  • Pedidos de determinados clientes empresariais.

Em vez de criar um tópico para cada variação, podemos utilizar um tópico comum e definir regras nas subscrições.

Por exemplo, a aplicação publica uma mensagem com propriedades semelhantes a estas:

{
  "orderId": "ORD-10025",
  "country": "PT",
  "priority": "High",
  "eventType": "OrderCreated"
}

Os valores acima são ilustrativos e representam informações que a aplicação pode incluir na mensagem.

Podemos configurar uma subscrição para receber apenas mensagens cuja propriedade country seja igual a PT.

Outra subscrição pode receber apenas mensagens cuja prioridade seja High.

Assim, diferentes componentes podem receber apenas os eventos relevantes para as suas responsabilidades.

O Azure Service Bus permite utilizar regras de filtragem, incluindo filtros SQL e filtros baseados em correlação, conforme as necessidades da aplicação.

É importante distinguir as propriedades utilizadas pelas regras de filtragem do conteúdo arbitrário do corpo da mensagem. A configuração do filtro deve corresponder às propriedades e aos tipos efetivamente publicados pela aplicação.

Também é necessário compreender que uma mensagem pode corresponder a várias subscrições. Nesse caso, cada subscrição correspondente poderá receber a sua própria cópia.

Por outro lado, se nenhuma subscrição corresponder às regras configuradas, a mensagem poderá não ser entregue a qualquer subscrição.

Por isso, as regras de filtragem devem ser testadas cuidadosamente antes de serem utilizadas em produção.

Como implementar uma fila do Azure Service Bus em .NET?

Para compreender melhor a utilização de filas, vamos considerar uma aplicação .NET que publica mensagens e um serviço responsável por as processar.

O exemplo utiliza o SDK oficial Azure.Messaging.ServiceBus.

Antes de começar, são necessários:

  • Uma subscrição Azure.

  • Um namespace do Azure Service Bus.

  • Uma fila criada no namespace.

  • Um projeto .NET com acesso ao Azure Service Bus.

  • Uma identidade ou credencial autorizada a enviar e receber mensagens.

Para projetos novos, é recomendável utilizar autenticação baseada em identidade, como o Microsoft Entra ID, em vez de manter credenciais de acesso diretamente no código.

Instalar o pacote necessário

Num projeto .NET, podemos adicionar o pacote oficial através do comando:

dotnet add package Azure.Messaging.ServiceBus

Este pacote disponibiliza as classes necessárias para criar clientes, enviar mensagens e receber mensagens do Service Bus.

Enviar uma mensagem para uma fila

Considere o seguinte exemplo:

using Azure.Messaging.ServiceBus;

var fullyQualifiedNamespace =
    "meu-namespace.servicebus.windows.net";

var queueName = "orders-queue";

var credential = new DefaultAzureCredential();

await using var client = new ServiceBusClient(
    fullyQualifiedNamespace,
    credential);

ServiceBusSender sender = client.CreateSender(queueName);

var message = new ServiceBusMessage(
    """{"orderId":"ORD-10025","status":"Created"}""")
{
    ContentType = "application/json",
    MessageId = "ORD-10025"
};

await sender.SendMessageAsync(message);

Console.WriteLine("Mensagem enviada para a fila.");

Neste exemplo, DefaultAzureCredential utiliza os mecanismos de autenticação disponíveis no ambiente de execução.

É necessário adicionar também o pacote Azure.Identity ao projeto:

dotnet add package Azure.Identity

O namespace utilizado no exemplo deve ser substituído pelo nome real do recurso criado no Azure.

Além disso, a identidade utilizada pela aplicação precisa de permissões adequadas para enviar mensagens.

O código cria um cliente do Service Bus, obtém um emissor para a fila orders-queue e envia uma mensagem com informações sobre o pedido.

A propriedade MessageId identifica a mensagem e pode ser utilizada em estratégias de deteção de duplicados, quando essa funcionalidade está configurada no recurso.

No entanto, definir um MessageId não significa, por si só, que todas as mensagens duplicadas serão automaticamente eliminadas. A deteção de duplicados depende da configuração da entidade e do nível de serviço utilizado.

Outro detalhe importante é que este exemplo utiliza uma mensagem JSON apenas para ilustrar a comunicação. Numa aplicação real, é preferível definir contratos de mensagens explícitos e controlar a sua evolução para evitar incompatibilidades entre produtores e consumidores.

Receber e processar uma mensagem

Para consumir mensagens de uma fila, podemos utilizar um ServiceBusProcessor.

Esse componente facilita a receção de mensagens através de callbacks e permite configurar o tratamento de mensagens e de erros.

Um exemplo simplificado seria:

using Azure.Messaging.ServiceBus;

var fullyQualifiedNamespace =
    "meu-namespace.servicebus.windows.net";

var queueName = "orders-queue";

var credential = new DefaultAzureCredential();

await using var client = new ServiceBusClient(
    fullyQualifiedNamespace,
    credential);

var processor = client.CreateProcessor(queueName);

processor.ProcessMessageAsync += async args =>
{
    string body = args.Message.Body.ToString();

    Console.WriteLine($"Mensagem recebida: {body}");

    // Executar o processamento do pedido.
    // Confirmar a mensagem apenas depois do sucesso.

    await args.CompleteMessageAsync(args.Message);
};

processor.ProcessErrorAsync += args =>
{
    Console.WriteLine(
        $"Erro no processamento: {args.Exception.Message}");

    return Task.CompletedTask;
};

await processor.StartProcessingAsync();

Console.WriteLine("A processar mensagens. Prima ENTER para terminar.");

Console.ReadLine();

await processor.StopProcessingAsync();

await processor.DisposeAsync();

Este exemplo pressupõe um programa de consola e serve para demonstrar o fluxo básico de processamento.

A implementação utiliza o modo PeekLock, que permite receber uma mensagem sob bloqueio temporário e confirmá-la explicitamente após o processamento.

A chamada a CompleteMessageAsync indica ao Service Bus que a mensagem foi processada e pode ser removida da fila.

Em aplicações reais, a confirmação deve acontecer apenas depois de todas as operações necessárias terem sido concluídas com sucesso.

O código acima não inclui uma estratégia completa de recuperação de erros, cancelamento, observabilidade ou encerramento gracioso. Esses aspetos devem ser considerados num consumidor de produção.

Também é necessário importar o namespace Azure.Identity para utilizar DefaultAzureCredential.

O que acontece quando o processamento falha?

Imagine que o serviço recebe uma mensagem, começa a processar o pedido e encontra um erro ao comunicar com a base de dados.

Se a mensagem não for confirmada e o bloqueio expirar, o Service Bus poderá disponibilizá-la novamente para processamento.

Esse comportamento ajuda a recuperar de falhas temporárias, mas também significa que a mesma mensagem pode ser processada mais de uma vez.

Por esse motivo, os consumidores devem ser preparados para lidar com reentregas.

Uma estratégia importante é tornar o processamento idempotente.

Na prática, isso significa que processar a mesma mensagem mais de uma vez não deve provocar efeitos adicionais indevidos.

Por exemplo, antes de registar um pagamento, a aplicação pode verificar se a operação correspondente já foi concluída. A implementação dessa proteção depende das regras de negócio e da consistência exigida pelo sistema.

O Azure Service Bus disponibiliza ainda filas de mensagens não entregues, conhecidas como Dead-Letter Queues (DLQ), que permitem isolar mensagens que não podem ser processadas normalmente.

Essas filas são importantes para investigar erros persistentes sem deixar que uma mensagem problemática seja repetida indefinidamente.

Como implementar tópicos e subscrições em .NET?

A utilização de tópicos é semelhante à utilização de filas no que diz respeito ao envio de mensagens.

A principal diferença é que, em vez de criar um emissor para uma fila, criamos um emissor para um tópico.

Considere um tópico chamado order-events.

Podemos publicar um evento da seguinte forma:

using Azure.Messaging.ServiceBus;

var fullyQualifiedNamespace =
    "meu-namespace.servicebus.windows.net";

var topicName = "order-events";

var credential = new DefaultAzureCredential();

await using var client = new ServiceBusClient(
    fullyQualifiedNamespace,
    credential);

ServiceBusSender sender = client.CreateSender(topicName);

var message = new ServiceBusMessage(
    """{"orderId":"ORD-10025","eventType":"OrderCreated"}""")
{
    ContentType = "application/json",
    Subject = "OrderCreated",
    MessageId = "ORD-10025"
};

await sender.SendMessageAsync(message);

Console.WriteLine("Evento publicado no tópico.");

A aplicação publica o evento no tópico sem precisar de conhecer todos os serviços que irão consumi-lo.

Para que o evento seja efetivamente processado, é necessário que existam subscrições configuradas e consumidores responsáveis por receber as mensagens dessas subscrições.

Por exemplo, poderíamos criar:

  • billing-subscription

  • notifications-subscription

  • analytics-subscription

Cada subscrição pode ter as suas próprias regras de filtragem, quando necessário, e ser consumida por um serviço independente.

Consumir mensagens de uma subscrição

Para receber mensagens, utilizamos o nome do tópico e o nome da subscrição:

using Azure.Messaging.ServiceBus;

var fullyQualifiedNamespace =
    "meu-namespace.servicebus.windows.net";

var topicName = "order-events";
var subscriptionName = "billing-subscription";

var credential = new DefaultAzureCredential();

await using var client = new ServiceBusClient(
    fullyQualifiedNamespace,
    credential);

var processor = client.CreateProcessor(
    topicName,
    subscriptionName);

processor.ProcessMessageAsync += async args =>
{
    string body = args.Message.Body.ToString();

    Console.WriteLine($"Evento recebido: {body}");

    // Processar o evento de acordo com
    // as responsabilidades deste serviço.

    await args.CompleteMessageAsync(args.Message);
};

processor.ProcessErrorAsync += args =>
{
    Console.WriteLine(
        $"Erro no processamento: {args.Exception.Message}");

    return Task.CompletedTask;
};

await processor.StartProcessingAsync();

Console.WriteLine("A processar eventos. Prima ENTER para terminar.");

Console.ReadLine();

await processor.StopProcessingAsync();

await processor.DisposeAsync();

Tal como no exemplo anterior, trata-se de uma demonstração simplificada. Num ambiente de produção, o serviço deve incluir as estratégias necessárias para tratamento de erros, cancelamento, monitorização e encerramento.

Repare que o consumidor recebe mensagens de billing-subscription, e não diretamente de order-events.

Se existir também uma subscrição chamada notifications-subscription, o serviço de notificações poderá consumir a sua própria cópia da mensagem, sem competir com o serviço de faturação pelas mesmas mensagens.

Essa é a principal diferença entre os dois modelos.

Nas filas, os consumidores partilham o trabalho. Nos tópicos, as subscrições permitem distribuir o mesmo evento por diferentes fluxos de processamento.

Filas ou tópicos: qual escolher numa arquitetura de microsserviços?

A escolha deve partir da intenção da mensagem e das responsabilidades dos serviços envolvidos.

Não é necessário utilizar tópicos em todas as comunicações entre microsserviços. Da mesma forma, utilizar apenas filas pode tornar a distribuição de eventos mais complexa à medida que a arquitetura cresce.

Vamos considerar alguns cenários comuns.

Cenário 1: processar pedidos de forma assíncrona

Imagine uma API responsável por receber pedidos de uma loja virtual.

Depois de validar a solicitação, a aplicação envia uma mensagem para uma fila chamada orders-queue.

Um serviço independente consome as mensagens e realiza o processamento necessário.

Neste cenário, a fila é uma escolha natural quando cada pedido representa uma unidade de trabalho que deve ser processada por um único consumidor no fluxo normal.

Se for necessário aumentar a capacidade, podemos executar várias instâncias do consumidor.

Não precisamos de criar uma subscrição para cada instância. Todas podem consumir a mesma fila, competindo pelas mensagens disponíveis.

Cenário 2: comunicar que um pedido foi criado

Agora considere que, depois de um pedido ser criado, vários serviços precisam de reagir ao acontecimento.

O serviço de faturação prepara a fatura, o serviço de notificações envia um e-mail e o serviço de análise regista o evento.

Neste cenário, um tópico chamado order-events pode ser a melhor opção.

Cada serviço utiliza a sua própria subscrição e recebe uma cópia dos eventos que correspondem às suas regras.

Assim, podemos adicionar novos consumidores sem obrigar o produtor a implementar uma nova integração direta para cada serviço.

Cenário 3: distribuir tarefas para diferentes trabalhadores

Imagine um sistema que gera relatórios a partir de ficheiros enviados pelos utilizadores.

Cada ficheiro submetido representa uma tarefa independente. Vários trabalhadores podem processar essas tarefas em paralelo.

Uma fila permite distribuir as tarefas entre os trabalhadores e absorver picos de solicitações.

Se um ficheiro precisar de ser processado novamente devido a uma falha recuperável, a aplicação pode recorrer aos mecanismos de reentrega do Service Bus, desde que o processamento esteja preparado para esse comportamento.

Nesse cenário, uma fila é normalmente suficiente.

Cenário 4: distribuir eventos de acordo com regras de negócio

Imagine um sistema empresarial que publica eventos sobre alterações de contratos.

Alguns consumidores precisam de receber apenas eventos relacionados com contratos ativos. Outros precisam de receber eventos associados a determinadas categorias de clientes.

Podemos utilizar um tópico e configurar filtros nas respetivas subscrições.

Essa abordagem evita a criação de mecanismos de distribuição personalizados no código da aplicação para cada tipo de consumidor.

No entanto, é importante não exagerar na quantidade de tópicos e subscrições. A estrutura deve refletir necessidades reais de comunicação e regras de negócio compreensíveis.

É possível combinar filas e tópicos?

Sim. Numa arquitetura real, filas e tópicos podem coexistir.

Na verdade, utilizar ambos os mecanismos costuma ser uma abordagem bastante útil.

Imagine o seguinte fluxo:

  1. A API recebe um pedido.

  2. O pedido é enviado para uma fila de processamento.

  3. Um serviço valida e processa a operação.

  4. Quando o pedido é criado com sucesso, o sistema publica um evento num tópico.

  5. Diferentes subscrições distribuem o evento pelos serviços interessados.

Podemos representar essa arquitetura da seguinte forma:

API → orders-queue → Serviço de processamento → order-events

A partir de order-events, o evento pode ser encaminhado para diferentes subscrições:

  • billing-subscription

  • notifications-subscription

  • analytics-subscription

Essa combinação separa duas necessidades distintas.

A fila distribui uma tarefa para processamento. O tópico comunica um acontecimento a diferentes destinatários.

Essa distinção é especialmente importante em sistemas orientados a eventos.

Um comando ou pedido de execução representa uma intenção de realizar uma operação. Um evento representa algo que já aconteceu.

Por exemplo:

  • ProcessOrder pode representar uma solicitação de processamento.

  • OrderCreated pode representar o facto de um pedido ter sido criado.

Embora os nomes sejam apenas exemplos, essa diferença conceptual ajuda a definir contratos de mensagens mais claros.

Também é importante não confundir a publicação de um evento com a garantia de que todas as operações de negócio foram concluídas.

Se um serviço publicar OrderCreated antes de a criação do pedido estar efetivamente confirmada na base de dados, poderá anunciar um acontecimento que não corresponde ao estado real do sistema.

Em aplicações críticas, pode ser necessário utilizar padrões adicionais, como o Transactional Outbox, para coordenar a persistência dos dados e a publicação dos eventos.

Boas práticas ao utilizar o Azure Service Bus

Escolher entre filas e tópicos é apenas uma parte do trabalho. Para construir uma solução confiável, também precisamos de considerar o processamento das mensagens, a segurança e a operação diária do serviço.

1. Torne os consumidores idempotentes

Como as mensagens podem ser entregues novamente, o consumidor não deve assumir que cada mensagem será processada exatamente uma vez.

Sempre que possível, utilize identificadores de negócio e mecanismos de controlo para impedir que uma reentrega provoque operações duplicadas.

Por exemplo, um consumidor responsável por registar um pagamento deve verificar se o pagamento correspondente já foi processado antes de repetir uma operação que possa produzir efeitos financeiros.

A estratégia deve ser adequada ao domínio da aplicação.

2. Utilize o modo PeekLock quando a perda de mensagens não for aceitável

O Azure Service Bus oferece diferentes modos de receção.

No modo PeekLock, a mensagem fica bloqueada temporariamente enquanto o consumidor a processa. Após concluir o processamento, o consumidor confirma a mensagem.

Se o processamento falhar antes da confirmação, a mensagem poderá ser disponibilizada novamente.

Esse comportamento permite implementar estratégias de recuperação, mas exige tratamento adequado de reentregas e de bloqueios expirados.

Já no modo ReceiveAndDelete, a mensagem é removida quando é entregue ao consumidor. Se a aplicação falhar antes de concluir o processamento, poderá perder a mensagem.

Por isso, PeekLock é geralmente a escolha mais adequada quando a confiabilidade do processamento é prioritária.

3. Monitorize as Dead-Letter Queues

As Dead-Letter Queues, ou DLQ, armazenam mensagens que não podem ser processadas normalmente ou que foram encaminhadas para esse mecanismo por alguma regra de negócio.

Quando uma mensagem é enviada para a DLQ, não deve simplesmente ser ignorada.

É importante monitorizar a quantidade de mensagens, investigar os motivos da falha e definir procedimentos para corrigir os problemas.

Em alguns casos, será possível corrigir a causa e reenviar as mensagens para processamento.

Noutros, poderá ser necessário corrigir dados ou encaminhar o problema para uma equipa responsável.

O Service Bus não elimina automaticamente todas as mensagens da DLQ após a sua análise. É necessário definir uma estratégia operacional para as tratar.

4. Configure a deteção de duplicados quando fizer sentido

O Azure Service Bus permite configurar a deteção de duplicados para filas e tópicos nos níveis de serviço compatíveis.

Quando ativada, essa funcionalidade utiliza o MessageId para identificar mensagens repetidas dentro de uma janela temporal configurada.

Isso pode ajudar a evitar que uma tentativa de envio repetida introduza a mesma mensagem mais de uma vez na entidade.

No entanto, a deteção de duplicados não substitui a idempotência do consumidor.

Uma mensagem pode voltar a ser processada devido a uma falha durante o consumo, mesmo quando a deteção de duplicados no envio está configurada.

É importante analisar os dois lados da comunicação.

5. Utilize autenticação baseada em identidade

Evite colocar chaves de acesso ou credenciais diretamente no código-fonte.

Quando possível, utilize o Microsoft Entra ID e identidades gerenciadas para autenticar aplicações executadas em serviços Azure compatíveis.

Conceda apenas as permissões necessárias para cada componente.

Por exemplo, uma aplicação que apenas envia mensagens não precisa necessariamente de ter permissões para administrar o namespace ou receber mensagens de todas as filas.

Essa separação reduz a exposição a acessos indevidos.

6. Defina contratos de mensagens claros

As mensagens trocadas entre serviços são contratos de integração.

Se cada componente interpretar os mesmos campos de forma diferente, a arquitetura torna-se difícil de manter.

Defina nomes consistentes, identificadores, tipos de eventos e regras para evolução dos contratos.

Também é recomendável evitar incluir dados sensíveis desnecessários nas mensagens.

Quando uma alteração de contrato for inevitável, considere como os produtores e consumidores existentes irão lidar com a nova estrutura.

7. Monitorize a capacidade de processamento

O facto de as mensagens estarem armazenadas numa fila não significa que o sistema esteja a processá-las com a velocidade necessária.

Acompanhe métricas como:

  • Quantidade de mensagens pendentes.

  • Número de mensagens na DLQ.

  • Taxa de entrada e saída de mensagens.

  • Erros durante o processamento.

  • Tempo necessário para concluir as operações.

  • Frequência de reentregas.

Se o número de mensagens pendentes aumentar continuamente, poderá ser necessário aumentar a capacidade dos consumidores ou otimizar o processamento.

A monitorização também ajuda a identificar situações em que uma dependência externa, como uma base de dados, está a limitar a capacidade de processamento.

Azure Service Bus, Azure Storage Queues ou Event Grid?

Outra dúvida frequente é saber se o Azure Service Bus deve ser utilizado em todas as integrações assíncronas.

A resposta é não.

A escolha depende das garantias de entrega, das necessidades de distribuição e das funcionalidades exigidas pela arquitetura.

O Azure Service Bus é indicado quando precisamos de um broker de mensagens com recursos como filas, tópicos, subscrições, sessões, deteção de duplicados e Dead-Letter Queues.

O Azure Storage Queues é uma alternativa de enfileiramento que pode ser suficiente para cenários mais simples de distribuição de tarefas, dependendo dos requisitos da aplicação.

Já o Azure Event Grid é orientado para a distribuição de eventos e notificações, sendo especialmente útil quando diferentes sistemas precisam de reagir a acontecimentos em serviços ou aplicações.

Apesar de existirem áreas de sobreposição, os três serviços não são equivalentes.

De forma simplificada:

Serviço Quando considerar
Azure Service Bus Mensageria empresarial, processamento confiável, filas, tópicos, subscrições e controlo de entrega
Azure Storage Queues Enfileiramento de tarefas com necessidades mais simples
Azure Event Grid Distribuição de eventos e notificações para sistemas interessados

A decisão deve considerar o modelo de comunicação, os requisitos operacionais, as garantias de entrega, os limites do serviço e os custos.

Não existe uma solução universalmente melhor para todas as aplicações.

Custos e escalabilidade: o que considerar?

O Azure Service Bus é um serviço gerenciado, mas isso não significa que a sua utilização não exija planeamento de custos.

O valor depende de fatores como o nível de serviço escolhido, o volume de operações, as funcionalidades utilizadas e as características da carga de trabalho.

Antes de colocar uma solução em produção, avalie:

  • O volume médio e máximo de mensagens.

  • A quantidade de produtores e consumidores.

  • O número de tópicos e subscrições.

  • A necessidade de funcionalidades específicas de cada nível.

  • A capacidade de processamento dos consumidores.

  • Os requisitos de disponibilidade e recuperação.

Os níveis de serviço disponíveis não oferecem exatamente as mesmas funcionalidades e condições operacionais. Por isso, confirme os requisitos na documentação e na página de preços oficiais antes de tomar uma decisão.

Também é importante considerar que tópicos com muitas subscrições podem aumentar o volume total de mensagens armazenadas e processadas.

Quando uma mensagem é encaminhada para várias subscrições, cada subscrição mantém o seu próprio fluxo de processamento. Essa distribuição oferece flexibilidade, mas deve ser considerada na estimativa de utilização e de custos.

Da mesma forma, adicionar mais consumidores não garante uma melhoria proporcional do desempenho. A capacidade da base de dados, as dependências externas, a concorrência e os limites do serviço podem tornar-se fatores de restrição.

Uma arquitetura eficiente é aquela que utiliza os recursos necessários para cumprir os requisitos definidos, sem introduzir complexidade desnecessária.

Conclusão

O Azure Service Bus é uma ferramenta importante para construir aplicações distribuídas mais resilientes, permitindo que diferentes componentes comuniquem sem depender de chamadas síncronas entre si.

No entanto, aproveitar corretamente o serviço exige compreender a diferença entre filas e tópicos.

As filas são indicadas quando queremos distribuir tarefas entre consumidores concorrentes e garantir que cada mensagem seja processada por um único consumidor no fluxo normal de trabalho.

Os tópicos, por sua vez, são úteis quando um acontecimento precisa de ser distribuído por vários serviços independentes. Com as subscrições e as regras de filtragem, podemos definir quais mensagens cada componente deve receber.

Em arquiteturas de microsserviços, também é perfeitamente possível combinar os dois modelos. Uma fila pode gerir tarefas de processamento, enquanto um tópico distribui eventos para os serviços interessados nos resultados.

A escolha deve partir de uma pergunta simples: estamos a distribuir uma tarefa para ser executada ou a comunicar um acontecimento que pode interessar a vários componentes?

Se a intenção for distribuir trabalho entre consumidores concorrentes, comece por avaliar uma fila. Se precisar de distribuir o mesmo evento por diferentes sistemas, avalie um tópico com subscrições.

Essa decisão, aliada a boas práticas de idempotência, monitorização, segurança e tratamento de erros, ajuda a construir soluções .NET mais robustas e preparadas para crescer.

Para quem desenvolve aplicações com ASP.NET Core, Azure Functions e microsserviços, dominar esses conceitos é um passo importante para desenhar integrações assíncronas mais simples de manter e mais resilientes a falhas.