<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Meriéli Manzano</title>
    <description>The latest articles on DEV Community by Meriéli Manzano (@merielimanzano).</description>
    <link>https://dev.to/merielimanzano</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F905006%2F77d91549-141c-41e3-9e92-bb7cc00ebed4.png</url>
      <title>DEV Community: Meriéli Manzano</title>
      <link>https://dev.to/merielimanzano</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/merielimanzano"/>
    <language>en</language>
    <item>
      <title>RabbitMQ na Prática: Manual de Decisão, Padrões e Resiliência</title>
      <dc:creator>Meriéli Manzano</dc:creator>
      <pubDate>Tue, 07 Jul 2026 15:16:27 +0000</pubDate>
      <link>https://dev.to/merielimanzano/rabbitmq-na-pratica-manual-de-decisao-padroes-e-resiliencia-1ad6</link>
      <guid>https://dev.to/merielimanzano/rabbitmq-na-pratica-manual-de-decisao-padroes-e-resiliencia-1ad6</guid>
      <description>&lt;p&gt;Em arquiteturas modernas baseadas em microsserviços e eventos, a comunicação assíncrona é o que mantém os sistemas coesos e resilientes — mas a escolha do message broker errado pode transformar uma solução elegante em um pesadelo operacional de latência, perda de dados e retrabalho. É justamente para navegar por esse campo minado de decisões que este manual foi pensado.&lt;/p&gt;

&lt;p&gt;Escrevo aqui com o objetivo de descrever, dissecar e posicionar a tecnologia &lt;strong&gt;RabbitMQ&lt;/strong&gt; no cenário atual, oferecendo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A compreensão do seu modelo mental e arquitetura interna.&lt;/li&gt;
&lt;li&gt;  Os atributos de qualidade que ela entrega (e os que ela sacrifica).&lt;/li&gt;
&lt;li&gt;  Os padrões de uso onde ela se destaca e as armadilhas comuns.&lt;/li&gt;
&lt;li&gt;  Um guia prático de resiliência e operação&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ao final da leitura, você terá subsídios para avaliar se esta tecnologia resolve as dores específicas do seu domínio de negócio.&lt;/p&gt;

&lt;h3&gt;
  
  
  O que é RabbitMQ?
&lt;/h3&gt;

&lt;p&gt;É um &lt;em&gt;message broker&lt;/em&gt; open-source, que possibilita intermediar mensagens para comunicar sistemas de forma assíncrona. Tem suporte e plugins para várias linguagens de programação.&lt;/p&gt;

&lt;p&gt;Surgiu em 1 de julho de 2007 e implementa o protocolo AMQP “Advanced Message Queuing Protocol - AMQP” na versão 0-9-1, que é a versão mais usada desse protocolo, além de suportar múltiplos protocolos adicionais, sendo amplamente utilizado para desacoplar serviços, construir arquiteturas orientadas a eventos e processar cargas de trabalho assíncronas.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Principais Protocolos:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;AMQP:&lt;/strong&gt; É um protocolo avançado de enfileiramento de mensagens baseado no TCP (Transmission Control Protocol), por isso é rápido e confiável. Uma das características desse protocolo é justamente o fato de que um publisher nunca publica mensagens diretamente na fila. A mensagem sempre é encaminhada à &lt;strong&gt;Exchange&lt;/strong&gt; que, de acordo com seu tipo, irá encaminhar para uma determinada fila ou a descartar. Escolha para sistemas corporativos que precisam de roteamento complexo e confiabilidade.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;MQTT:&lt;/strong&gt; O Message Queue Telemetry Transport é um protocolo para troca de mensagens em ambientes onde se tem alguma restrição de hardware, como pouca memória disponível, por exemplo, ou uma limitação de banda. Teve um crescimento notável pela adoção em dispositivos móveis. O objetivo desse protocolo é ser mais simples que o AMQP, utilizado em cenários de mensagens simples e diretas. Escolha para projetos de IoT ou dispositivos com recursos limitados.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;STOMP:&lt;/strong&gt; O Simple (or Streaming) Text Oriented Message Protocol é um protocolo baseado em texto, construído para trabalhar com middlewares orientados à mensagem. Possui uma estrutura similar ao AMQP, com cabeçalho, propriedades e corpo da mensagem, porém não lida com tópicos ou filas. Ele usa uma semântica de string de destino. Escolha para integração com sistemas legados ou aplicações web simples.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Origem e Mantenedores
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Criado por:&lt;/strong&gt; Rabbit Technologies (posteriormente adquirida pela VMware, hoje parte da Broadcom).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Data de lançamento:&lt;/strong&gt; 2007.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Governança:&lt;/strong&gt; Projeto open-source com governança comunitária; versão comercial (Tanzu RabbitMQ) sob tutela da Broadcom.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Licenciamento:&lt;/strong&gt; Mozilla Public License 2.0 (MPL-2.0) – permissiva para uso comercial.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Características Técnicas e Atributos de Qualidade
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Tipo de sistema:&lt;/strong&gt; Message Broker tradicional, um agente intermediador entre um produtor e um consumidor.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Modelo de mensagen&lt;/strong&gt;s: Filas com processamento FIFO “First in First out“, roteamento flexível (fanout, topic, direct, etc.).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Padrão principal:&lt;/strong&gt; Work queue/ request-reply “é composto de filas, do qual temos uma requisição que é passada para fila e depois o processamento da mensagem”.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Retenção de mensagens:&lt;/strong&gt; Normalmente até serem consumidas (ou TTL configurado), depois de processada sai do RabbitMQ.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Entrega das mensagens:&lt;/strong&gt; focado em “entregar e sumir” (at-least-once / at-most).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Ordenação:&lt;/strong&gt; ordenação por fila, pode se perder com múltiplos consumers porque embora as mensagens permaneçam na ordem correta dentro da fila, os consumidores podem recebê-las fora de ordem devido a competição pelas mensagens e a variação de processamento de cada consumidor, fazendo com que uma mensagem entregue depois seja confirmada antes de uma entregue antes. Um possível contorno é adotar mecanismos como SAC ou particionamento por chave de roteamento.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Throughput:&lt;/strong&gt; Muito bom, mas limitado comparado ao Kafka, benchmarks afirmam que uma Quorum Queue pode atingir em testes controlados cerca de 30.000 mensagens/segundo com mensagens de 1KB.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Latência:&lt;/strong&gt; Muito baixa, ótimo para processamentos assíncronos. Em testes práticos, a primeira mensagem de uma fila pode ser entregue em apenas &lt;strong&gt;9 ms.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Escalabilidade:&lt;/strong&gt; Escala bem, mas com mais esforço de configuração, pois cada nó adicionado ao cluster exige decisões arquiteturais sobre topologia, tipo de fila, replicação, particionamento de rede e limites de recursos.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Tolerância a falhas:&lt;/strong&gt; Bom suporte a HA / clusters (High Availability), oferecendo na versão 4.x um modelo de alta disponibilidade robusto, consistente e baseado em padrões comprovados da indústria, como o Raft.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Complexidade operacional:&lt;/strong&gt; Mais simples de começar e administrar, em poucos minutos é possível ter um broker em execução na máquina local.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Componentes Principais e Fluxo de Dados
&lt;/h2&gt;

&lt;p&gt;Entendidas as métricas e os atributos de qualidade surge a pergunta natural: &lt;em&gt;&lt;strong&gt;como&lt;/strong&gt;&lt;/em&gt; o RabbitMQ entrega tudo isso? O RabbitMQ introduz uma camada de sofisticação sobre filas simples. Seus blocos fundamentais são:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Producers:&lt;/strong&gt; Aplicações que enviam mensagens as colocando na fila. Um produtor nunca envia diretamente para uma fila — envia para uma &lt;em&gt;exchange&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Exchanges:&lt;/strong&gt; É como uma central de triagem dos Correios, ele recebe mensagens dos producers e decide para quais filas elas vão, com base em regras chamadas &lt;em&gt;bindings&lt;/em&gt;. Baseado em quatro tipos principais:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-   **Direct:** Roteia por correspondência exata da _routing key_.
-   **Fanout:** Transmite para todas as filas vinculadas.
-   **Topic:** Roteia por correspondência de padrão na _routing key_.
-   **Headers:** Roteia com base em atributos do cabeçalho da mensagem.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Queues:&lt;/strong&gt; &lt;em&gt;Buffers&lt;/em&gt; que armazenam mensagens até que sejam consumidas.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Consumers:&lt;/strong&gt; Aplicações que recebem e processam mensagens das filas.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O RabbitMQ foi escrito em Erlang, linguagem projetada para sistemas que precisam operar continuamente sem falhas. Nele a mensagem da fila é persistente, ou seja ela fica na fila até ser processada e depois ela é descartada, mas não fica persistida eternamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Roteamento Estratégico: Quando Usar Direct, Fanout ou Topic
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Direct Exchange
&lt;/h4&gt;

&lt;p&gt;A Exchange direta envia a mensagem para uma fila específica, em que temos uma routing key (chave de roteamento) configurada e todas as filas que possuírem essa chave receberão essa mensagem.&lt;/p&gt;

&lt;p&gt;Ele possui um algoritmo padrão chamado round-robin e os consumidores recebem as mensagens de forma balanceada. Isto é, se tivermos cinco consumidores para aquele tipo de exchange, a cada envio um deles irá receber.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1e8ejuca7lappk54dniv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1e8ejuca7lappk54dniv.png" alt="Direct Exchange" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caso de uso:&lt;/strong&gt; &lt;em&gt;para rotear comandos específicos (ex:&lt;/em&gt; &lt;code&gt;comando.recalcular.calculo&lt;/code&gt;&lt;em&gt;) para filas de workers especializados.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Fanout Exchange
&lt;/h3&gt;

&lt;p&gt;Esse tipo simplesmente ignora qualquer 'endereço' (routing key) que você coloque na mensagem e a transmite para TODAS as filas que estiverem vinculadas a ela. O vínculo (binding) é direto e sem chave — a fila apenas se 'inscreve' para receber tudo o que a exchange transmitir. Não importa se você colocar uma routing key, ela será totalmente ignorada.&lt;/p&gt;

&lt;p&gt;Cenários que fazem sentido esse tipo de fila é por exemplo quando temos processos paralelos baseado em uma única mensagem ou evento importante, assim várias filas vão receber para processar a mensagem de uma forma específica em cada uma das filas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ak8x5hx5d73o2fqbbzj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ak8x5hx5d73o2fqbbzj.png" alt="Fanout Exchange" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caso de uso:&lt;/strong&gt; para notificações de sistema (ex: 'Pedido Criado') que disparam múltiplos processos independentes (envio de e-mail, atualização de estoque, faturamento).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Topic Exchange
&lt;/h3&gt;

&lt;p&gt;Permite que você crie regras personalizadas para entregar mensagens usando &lt;em&gt;curingas&lt;/em&gt; (wildcards) que funcionam como um ‘filtro de palavras’. As palavras são separadas por pontos (&lt;code&gt;.&lt;/code&gt;), e você usa dois símbolos mágicos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;*&lt;/code&gt; &lt;strong&gt;(asterisco):&lt;/strong&gt; Substitui &lt;strong&gt;exatamente uma palavra&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;#&lt;/code&gt; &lt;strong&gt;(hash):&lt;/strong&gt; Substitui &lt;strong&gt;zero ou mais palavras&lt;/strong&gt; (ou seja, ‘todo o resto’).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt; Se uma fila estiver vinculada com a regra &lt;code&gt;*.error&lt;/code&gt;, ela receberá mensagens com &lt;code&gt;db.error&lt;/code&gt; ou &lt;code&gt;api.error&lt;/code&gt; (uma palavra antes do &lt;code&gt;error&lt;/code&gt;), mas NÃO receberá &lt;code&gt;db.mysql.error&lt;/code&gt; (duas palavras antes). Se a regra for &lt;code&gt;payment.#&lt;/code&gt;, ela receberá &lt;code&gt;payment&lt;/code&gt; (zero palavras depois), &lt;code&gt;payment.created&lt;/code&gt; (uma palavra) ou &lt;code&gt;payment.created.success&lt;/code&gt; (várias palavras) — pois o &lt;code&gt;#&lt;/code&gt; engole todo o resto que vier depois.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwxzqe2krog65thmr2r2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwxzqe2krog65thmr2r2q.png" alt="Topic Exchange" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caso de uso:&lt;/strong&gt; &lt;em&gt;para logs ou eventos de domínio onde você precisa de filtros granulares (ex:&lt;/em&gt; &lt;code&gt;*.erro.banco&lt;/code&gt;&lt;em&gt;).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  O que ocorre em caso de falhas?
&lt;/h2&gt;

&lt;p&gt;Até agora, exploramos o fluxo ideal: produtor publica, exchange roteia, consumidor processa e fim. Mas você sabe que o mundo real é implacável — &lt;em&gt;exceções acontecem, serviços caem e mensagens corrompem&lt;/em&gt;. É aqui que o modelo conceitual que acabamos de ver se conecta diretamente com a estratégia de resiliência. Quando uma exceção ocorre durante o processamento, a aplicação pode entrar em um loop infinito, travando todo o fluxo da fila. A primeira linha de defesa contra isso é configurar &lt;strong&gt;retry com limites claros&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Defina o número máximo de tentativas com &lt;code&gt;max-attempts&lt;/code&gt; e habilite o &lt;code&gt;retry&lt;/code&gt;. Caso contrário, após esgotar as tentativas (ex: 4 falhas consecutivas), a mensagem simplesmente será descartada — a menos que você a direcione para uma fila de contingência.&lt;/p&gt;

&lt;h3&gt;
  
  
  A estratégia da Dead Letter Queue (DLQ)
&lt;/h3&gt;

&lt;p&gt;Para evitar a perda definitiva de mensagens problemáticas, o RabbitMQ permite redirecioná-las para uma fila especial chamada &lt;strong&gt;Dead Letter Queue (DLQ)&lt;/strong&gt; — onde as mensagens aguardam uma decisão manual ou automatizada.&lt;/p&gt;

&lt;p&gt;Uma mensagem vai para a DLQ quando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A fila excede o comprimento máximo;&lt;/li&gt;
&lt;li&gt;  A mensagem expira (TTL - Time to Live);&lt;/li&gt;
&lt;li&gt;  A mensagem ultrapassa o tamanho permitido;&lt;/li&gt;
&lt;li&gt;  Ocorrem erros diversos ou falhas de processamento.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No RabbitMQ, as mensagens não vão diretamente para filas, mas sim via &lt;em&gt;exchanges&lt;/em&gt;. Para implementar a DLQ, você precisa criar uma &lt;strong&gt;Dead Letter Exchange (DLX)&lt;/strong&gt;. Por exemplo: a exchange &lt;code&gt;pagamento.dlx&lt;/code&gt; encaminha as mensagens com falha para a fila &lt;code&gt;pagamentos.detalhes-avaliacao-dlq&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ao receber uma mensagem na DLQ, você tem algumas opções de tratamento:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Persistir os dados em um banco de dados para análise futura;&lt;/li&gt;
&lt;li&gt;  Direcioná-la para outra fila (reprocessamento em lote);&lt;/li&gt;
&lt;li&gt;  Registrá-la em um sistema de logs para auditoria e depuração.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Uma DLQ crescendo de forma atípica não é um sinal de “tudo bem”; é um indicador claro de que há um problema estrutural no consumidor que exige intervenção humana imediata ou a implementação de um circuit breaker para evitar que o sistema continue processando dados incorretamente e poluindo ainda mais a fila morta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resiliência Operacional
&lt;/h2&gt;

&lt;p&gt;O RabbitMQ frequentemente ocupa uma posição crítica na arquitetura — ele é o “correio” que mantém seus sistemas conversando. Se ele cair, toda a comunicação assíncrona para. Por isso, &lt;strong&gt;alta disponibilidade (HA)&lt;/strong&gt; é um requisito fundamental para o ambiente de produção.&lt;/p&gt;

&lt;p&gt;A prática mais comum para garantir HA é montar um &lt;strong&gt;Cluster&lt;/strong&gt; com múltiplas instâncias do RabbitMQ rodando em paralelo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Documentação oficial:&lt;/strong&gt; &lt;a href="https://www.rabbitmq.com/docs/clustering" rel="noopener noreferrer"&gt;Clustering Guide - RabbitMQ&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Como funciona os clusters na prática:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Você cria várias instâncias do RabbitMQ (nós).&lt;/li&gt;
&lt;li&gt; Em seguida, executa o comando de &lt;em&gt;join&lt;/em&gt; para conectá-las em um único cluster lógico.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Mas ter um cluster não é suficiente por si só. Você precisa configurar como as mensagens serão tratadas caso um dos nós falhe. Para isso, entram em cena as &lt;strong&gt;Políticas (Policies)&lt;/strong&gt; — regras que definem o comportamento do cluster diante de falhas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O que uma política define?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Espelhamento (Mirroring):&lt;/strong&gt; você decide se as mensagens serão replicadas para &lt;strong&gt;todos&lt;/strong&gt; os nós do cluster ou apenas para um subconjunto deles.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Escopo de aplicação:&lt;/strong&gt; a política pode ser aplicada apenas a &lt;strong&gt;Exchanges&lt;/strong&gt;, apenas a &lt;strong&gt;Queues&lt;/strong&gt;, ou a ambos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exemplo prático:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Imagine um cluster com 3 nós. Você pode criar uma política chamada &lt;code&gt;ha-all&lt;/code&gt; que replica todas as filas para os 3 nós. Assim, se um nó cair, as mensagens ainda estarão disponíveis nos outros dois.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Critérios de Decisão para o RabbitMQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Quando Usar
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Desacoplamento de serviços:&lt;/strong&gt; conectar sistemas diferentes sem depender de chamadas diretas entre eles, ou com comunicação assíncrona, absorvendo picos de carga.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Controlar desempenho:&lt;/strong&gt; Regular o fluxo de dados para não sobrecarregar (ou subutilizar) o sistema.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Aumentar tolerância a falhas:&lt;/strong&gt; Evitar perda de mensagens e permitir recuperação após erros.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Facilitar a escalabilidade&lt;/strong&gt;: Permitir vários consumidores para o mesmo produtor, com componentes independentes.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Lidar com alto volume de entrada&lt;/strong&gt;: Processar requisições de forma assíncrona e em “fire-and-forget” (“disparar e esquecer” é um padrão de comunicação assíncrona amplamente utilizado em sistemas de mensageria e microsserviços).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Amortecer a saída do sistema:&lt;/strong&gt; Enfileirar dados antes de enviá-los, como em streaming de vídeo e notificações.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Roteamento complexo:&lt;/strong&gt; Quando diferentes tipos de mensagens precisam ser roteados para diferentes filas com base em regras (ex: logs por severidade, notificações por canal).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Quando Evitar
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Streaming de alta vazão (&amp;gt;100k msg/seg por partição):&lt;/strong&gt; Kafka é mais adequado quando você precisa guardar um histórico ordenado completo de tudo o que aconteceu no sistema (para poder ‘voltar no tempo’ e reconstruir estados) e também quando você precisa mover e processar um volume gigantesco de dados em tempo real, na casa dos milhões de eventos por segundo.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Reprocessamento de eventos históricos:&lt;/strong&gt; RabbitMQ não mantém mensagens consumidas por padrão (exceto Streams - uma alternativa avançada para cenários que exigem &lt;strong&gt;replay de eventos&lt;/strong&gt;); Kafka é superior para replay de eventos.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Exactly-once semantics nativas:&lt;/strong&gt; RabbitMQ entrega a mensagem ‘pelo menos uma vez’ para garantir que ela nunca se perca, isso significa que, em situações de falha, a mesma mensagem pode ser entregue duas vezes ao seu sistema. Para garantir que cada mensagem seja processada uma exatamente uma única vez, você precisa programar o consumidor para ser ‘idempotente’ — ou seja, capaz de processar a mesma mensagem várias vezes sem causar efeitos colaterais duplicados.”.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Filas extremamente longas (milhões de mensagens):&lt;/strong&gt; O desempenho degrada significativamente; considere Streams ou outra solução.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Ambientes com restrição extrema de memória:&lt;/strong&gt; RabbitMQ é memory-bound “dependente de RAM“ e pode sofrer com paging “paginação forçada varrendo para o disco rígido para liberar espaço, o que consome CPU fazendo tudo ficar lento“ em cenários de baixa memória.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  O Lugar do RabbitMQ na Arquitetura Moderna
&lt;/h2&gt;

&lt;p&gt;O RabbitMQ não é uma tecnologia nova — nasceu em 2007 — mas mantém-se extraordinariamente relevante porque resolve um problema fundamental: &lt;strong&gt;comunicação assíncrona confiável e com roteamento inteligente&lt;/strong&gt;. Em um mundo de microsserviços, eventos e domínios complexos, ele continua sendo uma escolha sólida e madura.&lt;/p&gt;

&lt;p&gt;Mas, como vimos, ele não é uma bala de prata. &lt;strong&gt;Respeitar seus limites é tão importante quanto potencializar seus pontos fortes.&lt;/strong&gt; A decisão de adotá-lo — ou não — deve ser baseada em critérios objetivos, não em modismos ou preferências pessoais.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recomendações para o próximo passo
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Experimente na prática:&lt;/strong&gt; suba um RabbitMQ localmente em minutos e reproduza os cenários de Direct, Fanout e Topic que vimos aqui. Nada substitui a experiência tátil.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Leia a documentação oficial:&lt;/strong&gt; &lt;a href="https://www.rabbitmq.com/docs" rel="noopener noreferrer"&gt;rabbitmq.com/docs&lt;/a&gt; é seu melhor amigo para aprofundamentos.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Explore plugins avançados:&lt;/strong&gt; Shovel, Federation e Management Interface abrem possibilidades que vão além do básico.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Teste cenários de falha propositalmente:&lt;/strong&gt; derrube nós, sobrecarregue filas, simule erros — só assim você conhece os limites reais do sistema.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A melhor tecnologia não é a mais avançada, mas aquela que resolve o problema certo com a complexidade certa.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Com o conhecimento adquirido neste manual, você está pronto para avaliar, projetar e operar sistemas com RabbitMQ de forma confiante e estratégica. A decisão é sua — e agora você tem subsídios para tomá-la com clareza.&lt;/p&gt;

&lt;p&gt;Até a próxima galerinha.&lt;/p&gt;

</description>
      <category>eventdriven</category>
      <category>softwareengineering</category>
      <category>pubsub</category>
    </item>
    <item>
      <title>Dos fundamentos à prática: como construir Entidades ricas de verdade</title>
      <dc:creator>Meriéli Manzano</dc:creator>
      <pubDate>Thu, 11 Jun 2026 16:33:23 +0000</pubDate>
      <link>https://dev.to/merielimanzano/dos-fundamentos-a-pratica-como-construir-entidades-ricas-de-verdade-9jm</link>
      <guid>https://dev.to/merielimanzano/dos-fundamentos-a-pratica-como-construir-entidades-ricas-de-verdade-9jm</guid>
      <description>&lt;p&gt;Você já deve ter ouvido a palavra entidade em diferentes contextos no desenvolvimento de software. No banco de dados, uma entidade é quase uma linha numa tabela. No ORM (ferramenta que converte tabelas do banco em objetos), é uma classe cheia de getters e setters.&lt;/p&gt;

&lt;p&gt;Mas quando falamos de Domain-Driven Design (DDD) — uma abordagem para modelar software complexo focando no negócio — o significado é bem mais específico. E é sobre ele que vamos conversar hoje, sem complicação.&lt;/p&gt;

&lt;p&gt;Imagine que você está construindo um sistema de matrículas para uma escola. No começo, você faz o simples, cria uma classe Aluno com nome, email e cpf e guarda numa tabela. Para alterar o e-mail, você faz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$student&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"new@email.com"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$student&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tudo funciona mas com o tempo, os pedidos mudam:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ao trocar de e-mail, é preciso registrar essa alteração para auditoria.&lt;/li&gt;
&lt;li&gt;Um aluno não pode se matricular se já tiver outro com o mesmo CPF ativo.&lt;/li&gt;
&lt;li&gt;Quando o aluno muda de nome, precisa enviar uma notificação para a secretaria.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Você tenta colocar essas regras nos controllers (camada que recebe requisições HTTP) ou nos services (camada de orquestração). O resultado? O código começa a se repetir com validação de CPF que aparece em três lugares diferentes; a lógica de notificação que se perde entre uma requisição e outra; o Aluno que vira um simples saco de dados, e qualquer parte do sistema pode deixá-lo em um estado inválido (por exemplo, um aluno ativo com CPF vazio).&lt;/p&gt;

&lt;p&gt;Essa dor tem nome: &lt;strong&gt;Modelo Anêmico&lt;/strong&gt;. É quando a classe tem apenas propriedades (atributos) e nenhum comportamento (métodos com regras de negócio). O sistema fica frágil, espalhado e difícil de mudar. É justamente isso que a Entidade do DDD resolve. Ela coloca a identidade e as regras dentro do próprio objeto, protegendo a consistência do negócio.&lt;/p&gt;

&lt;p&gt;No DDD, uma Entidade é um objeto com identidade própria (um ID único). Ela muda ao longo do tempo, mas continua sendo a mesma por causa dessa identidade. Ela contém &lt;strong&gt;atributos&lt;/strong&gt; (dados) e &lt;strong&gt;comportamentos&lt;/strong&gt; (regras de negócio) que garantem sua validade e evolução.&lt;/p&gt;

&lt;p&gt;Pense em uma pessoa. Ela nasce, troca de nome ao casar, muda de endereço várias vezes, talvez até de cor do cabelo. Apesar de todas essas mudanças, essa pessoa continua sendo ela mesma — sua identidade (documento, biometria, história) é o que a define. Uma empresa também: pode mudar de sede, de sócios, de produto principal, mas o CNPJ e a razão social permanecem. No software, a Entidade é exatamente isso: algo que reconhecemos pela identidade, e não pelos seus dados momentâneos.&lt;/p&gt;

&lt;p&gt;Abaixo um exemplo em PHP. Repare que não há set público solto — as mudanças acontecem por métodos nomeados com a intenção do negócio.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Generic base class for all Domain Entities&lt;/span&gt;
&lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Entity&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Entity&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Concrete Student entity&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Student&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Entity&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$cpf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;changeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$newEmail&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;str_contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$newEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'@'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\InvalidArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Invalid email"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// Could register an "EmailChanged" event for audit here&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$newEmail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;deactivate&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Notify the registrar, etc.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getEmail&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCpf&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cpf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Como usar:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$student&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"111.222.333-44"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"João"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"joao@mail.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$student&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;changeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"joao.novo@mail.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// valid&lt;/span&gt;
&lt;span class="nv"&gt;$student&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;deactivate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// $student-&amp;gt;email = "any" → error (private property, no public setter)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perceba: a &lt;strong&gt;identidade&lt;/strong&gt; é o id (passado no construtor). O objeto muda (&lt;code&gt;email&lt;/code&gt; e &lt;code&gt;active&lt;/code&gt;), mas o &lt;code&gt;id&lt;/code&gt; nunca muda. As regras de negócio ficam dentro da própria classe, não espalhadas.&lt;/p&gt;

&lt;p&gt;Exemplos de entidades são: Curso, Aula, Usuário, Progresso, Discussão, Anexo, Tutor, Post, Certificado, Produto, etc.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Não devemos focar o domínio no banco de dados! Há uma propensão de desenvolvedores focarem dados em vez de domínio. Isso pode ocorrer devido a abordagens que colocam a importância no banco de dados. Em vez de projetar conceitos de domínio com comportamentos ricos, pensam principalmente em atributos “colunas” e associações/relacionamentos “chaves estrangeiras”.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Você está projetando pensando em colunas ou em comportamentos de negócio? Pare e reflita: sua classe principal tem regras ou só getters?&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Com o tempo, o sistema se torna uma "&lt;em&gt;Big Ball of Mud&lt;/em&gt;". Essa expressão descreve um software com estrutura indefinida, cheio de dependências emaranhadas. É impossível prever o efeito de uma alteração. As regras de negócio ficam espalhadas por controllers, serviços e até helpers, tornando o sistema frágil e difícil de evoluir. Para converter um modelo anêmico em uma entidade rica (com comportamento), siga estes passos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Remova todos os setters públicos&lt;/strong&gt; da classe. Nenhuma propriedade poderá ser alterada diretamente de fora.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identifique os motivos de mudança&lt;/strong&gt; no negócio (ex: “cancelar pedido”, “aprovar pagamento”, “alterar e-mail do aluno”).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crie métodos públicos nomeados com esses verbos&lt;/strong&gt; (ex: cancelar(), aprovarPagamento(), alterarEmail($novoEmail)). Cada método deve conter as validações e regras necessárias, além de alterar o estado interno.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mova para dentro desses métodos&lt;/strong&gt; toda a lógica que antes estava em serviços ou controllers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proteja a criação&lt;/strong&gt; – use um construtor que exija os dados mínimos para a entidade existir em um estado válido (incluindo o ID de identidade).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ao seguir esses passos, você perceberá que a entidade deixa de ser um mero recipiente de dados. Ela passa a expressar, por si mesma, as regras do negócio. É aqui que alcançamos o verdadeiro propósito do DDD - ter uma entidade validada na aplicação. Isso significa que, se a entidade já existe (ou seja, foi construída respeitando suas invariantes), automaticamente ela já é válida. Você não precisa espalhar validações pelo sistema para garantir sua consistência. E mais, comportamentos ricos dentro da entidade expandem sua capacidade de evoluir sem quebrar o mundo lá fora.&lt;/p&gt;

&lt;p&gt;Esse é o coração do que chamamos de &lt;strong&gt;Modelo Rico&lt;/strong&gt;: um modelo que tem os atributos e os comportamentos, ou seja, os métodos que implementam as regras de negócio. Uma &lt;strong&gt;Entidade no DDD&lt;/strong&gt; não é teoria distante — é uma ferramenta prática para acabar com a bagunça do modelo anêmico e da &lt;em&gt;Big Ball of Mud&lt;/em&gt;. A diferença entre um sistema que só armazena dados e um que expressa o negócio está exatamente aí: nas classes que protegem sua própria identidade e evolução.&lt;/p&gt;

&lt;p&gt;Agora, pare por um momento e olhe para a classe mais crítica do seu projeto atual. Ela tem comportamento ou é só um saco de getters e setters? Se for a segunda opção, você já tem um candidato perfeito para aplicar os cinco passos que vimos.&lt;/p&gt;

&lt;p&gt;Não precisa refatorar tudo de uma vez. Comece pequeno: remova um setter, crie um método com nome de verbo do negócio, mova uma validação para dentro da entidade. O efeito é imediato: testes ficam mais claros, bugs diminuem e seu time volta a conversar sobre regras de negócio, não sobre colunas de banco.&lt;/p&gt;

&lt;p&gt;Dê identidade e comportamento à sua próxima Entidade. Você vai sentir a diferença na primeira regra de negócio que deixar de se repetir. Abra o código e comece agora!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Até a próxima galerinha.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>cleancode</category>
      <category>php</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Dê significado aos seus dados: Objetos de Valor na prática</title>
      <dc:creator>Meriéli Manzano</dc:creator>
      <pubDate>Thu, 28 May 2026 20:29:58 +0000</pubDate>
      <link>https://dev.to/merielimanzano/de-significado-aos-seus-dados-objetos-de-valor-na-pratica-1a0p</link>
      <guid>https://dev.to/merielimanzano/de-significado-aos-seus-dados-objetos-de-valor-na-pratica-1a0p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Pare de usar string para tudo e comece a expressar suas regras de negócio com clareza e segurança&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Você já se pegou repetindo as mesmas validações de CPF, e-mail ou telefone em várias partes do sistema? Sentiu que seus modelos de domínio estão anêmicos, cheios de getters e setters, mas sem comportamento real? Se sim, é hora de conhecer o verdadeiro poder dos &lt;strong&gt;Objetos de Valor&lt;/strong&gt; &lt;em&gt;(Value Objects)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Neste artigo, vamos mergulhar fundo nesse conceito transformador. Você vai entender por que Objetos de Valor garantem consistência, eliminam duplicação e tornam seu código mais expressivo e seguro. Prepare-se para elevar o nível da sua modelagem de domínio!&lt;/p&gt;

&lt;p&gt;Imagine um CPF: &lt;code&gt;123.456.789-00&lt;/code&gt;. O que importa é o número em si, não uma identidade única que o acompanhe. Se dois CPFs têm o mesmo número, eles são &lt;strong&gt;iguais&lt;/strong&gt; – não importa se foram instanciados em momentos diferentes ou em lugares distintos. Essa é a alma do Objeto de Valor.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Definição:&lt;/strong&gt; Um Objeto de Valor é um tipo de classe que representa um conceito abstrato, sem identidade própria (não possui ID). Ele é definido apenas por suas propriedades, encapsulando dentro de si um ou mais valores e suas respectivas regras de negócio.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Características fundamentais:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Imutável:&lt;/strong&gt; uma vez criado, nunca muda. Para alterar, cria-se uma nova instância.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comparado por atributos:&lt;/strong&gt; dois Value Objects são iguais se todos os seus atributos forem iguais.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-validado:&lt;/strong&gt; ao ser instanciado, já garante que o valor é consistente com as regras do domínio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Exemplos clássicos:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPF, CNPJ, CEP, telefone&lt;/li&gt;
&lt;li&gt;Endereço (composto por logradouro, número, cidade, CEP)&lt;/li&gt;
&lt;li&gt;Email, senha hash, duração de tempo, preço, nome próprio, cor, moeda&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Por que priorizar Objetos de Valor?
&lt;/h2&gt;

&lt;p&gt;Muitos desenvolvedores, quando pensam em DDD, correm imediatamente para as Entidades. Mas o próprio Eric Evans traz uma orientação poderosa:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Devemos nos esforçar para modelar utilizando Objetos de Valor, em vez de Entidades, sempre que possível. Mesmo quando um conceito de domínio precisa ser modelado como uma Entidade, o projeto da Entidade deve favorecer o uso de um contêiner de valores, em vez de um contêiner de Entidades filho.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Isso significa que o primeiro local para armazenar nossas regras de negócio é no Objeto de Valor, e por isso uma Entidade precisa compor Objetos de Valor, evitando ter Entidades dentro de Entidades.&lt;/p&gt;

&lt;p&gt;Os benefícios do Objeto de Valor são enormes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reuso imediato: Um Email ou CPF pode ser usado em qualquer lugar (Usuário, Cliente, Fornecedor) sem repetir regras.&lt;/li&gt;
&lt;li&gt;Código mais expressivo: Em vez de string $email, temos Email $email. O tipo já revela a intenção e as restrições.&lt;/li&gt;
&lt;li&gt;Testabilidade facilitada: Testar as regras de um Value Object isolado é trivial.&lt;/li&gt;
&lt;li&gt;Imutabilidade e segurança: Sem side-effects; objetos podem ser compartilhados sem risco.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mesmo que seu projeto não adote formalmente o DDD (Domain-Driven Design, uma abordagem que coloca o negócio e seu vocabulário no centro do código), usar objetos de valor vale a pena porque eles eliminam a duplicação de validações e garantem consistência em qualquer arquitetura.&lt;/p&gt;

&lt;p&gt;Se até aqui você já aprendeu algo, compartilhe este artigo com quem precisa de mais consistência e menos duplicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sinal de alerta para extrair um Value Object
&lt;/h2&gt;

&lt;p&gt;Quando você perceber várias classes com os mesmos atributos (ex.: &lt;code&gt;string $cpf&lt;/code&gt;, &lt;code&gt;string $email&lt;/code&gt;, &lt;code&gt;string $telefone&lt;/code&gt;), cada uma validando do seu próprio jeito, é um forte indicativo de que esses atributos merecem se tornar Objetos de Valor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Objetos de Valor vs Entidades: quando usar cada um?
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Entidades
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identidade:&lt;/strong&gt; Possui um ID único que a rastreia ao longo do tempo e o que a distingue de outras entidades.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutabilidade:&lt;/strong&gt; Mutável (seus atributos podem mudar, mas a identidade permanece).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Igualdade:&lt;/strong&gt; Comparação por identidade (ID)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exemplos de uso:&lt;/strong&gt; Pessoa, Pedido, Conta Bancária, Produto&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Objetos de Valor
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identidade:&lt;/strong&gt; Não tem identificador único; é definido por seus atributos ou por seu valor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutabilidade:&lt;/strong&gt; Imutável.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Igualdade:&lt;/strong&gt; Comparação estrutural (todos os atributos/valores).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exemplos de uso:&lt;/strong&gt; CPF, Email, Endereço, Cor, Dinheiro&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Construindo um Value Object na Prática
&lt;/h2&gt;

&lt;p&gt;Vamos implementar um &lt;code&gt;Email&lt;/code&gt;, usando uma classe abstrata genérica para reaproveitar o método equals e definir por padrão o $value como readonly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Shared/ValueObject.php&lt;/span&gt;
&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Shared&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * @template TInput
 */&lt;/span&gt;
&lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValueObject&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @param  TInput  $value
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @param  self&amp;lt;TInput&amp;gt;  $other
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;self&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @param  self&amp;lt;TInput&amp;gt;  $other
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;notEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;self&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, o Value Object concreto Email:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Domain\Account\User\Domain\ValueObjects&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Domain\Account\User\Domain\Exceptions\InvalidEmailException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Shared\ValueObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * Email value object handles domain validation for emails.
 *
 * @extends ValueObject&amp;lt;string&amp;gt;
 */&lt;/span&gt;
&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Email&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ValueObject&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$trimmed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;mb_strtolower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mb_trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;filter_var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$trimmed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;FILTER_VALIDATE_EMAIL&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvalidEmailException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The email '&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' is invalid."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$trimmed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$local&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$domain&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observações importantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O construtor já valida e normaliza (minúsculas, trim).&lt;/li&gt;
&lt;li&gt;As propriedades $local e $domain são derivadas do valor.&lt;/li&gt;
&lt;li&gt;O objeto é readonly – imutável por design.&lt;/li&gt;
&lt;li&gt;A comparação equals verifica tanto o tipo quanto o valor.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cuidado:&lt;/strong&gt; a lógica de negócio dentro do Value Object deve ser autossuficiente e baseada apenas no valor recebido, e não deve depender de contexto externo (como banco de dados, por exemplo).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Objetos de Valor em uso
&lt;/h2&gt;

&lt;p&gt;Uma Entidade no DDD deve ser um contêiner de valores, não uma colcha de retalhos de tipos primitivos. Veja como fica uma UserEntity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserEntity&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Entity&lt;/span&gt;  &lt;span class="c1"&gt;// Entity pai fornece $id e clone()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;Name&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;?HashedPassword&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Restante do código: factory methods, comportamentos...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note que &lt;code&gt;$name&lt;/code&gt;, &lt;code&gt;$email&lt;/code&gt; e &lt;code&gt;$password&lt;/code&gt; são Objetos de Valor e cada um carrega suas próprias regras. E a Entidade &lt;code&gt;User&lt;/code&gt; não precisa se preocupar com essas validações – elas já foram resolvidas no momento da criação.&lt;/p&gt;




&lt;h3&gt;
  
  
  Dicas para usar Objetos de Valor
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Comece pelos primitivos obcecados:&lt;/strong&gt; qualquer lugar onde você vê &lt;code&gt;string $cpf&lt;/code&gt;, &lt;code&gt;string $cep&lt;/code&gt;, &lt;code&gt;string $telefone&lt;/code&gt; é candidato a Value Object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seja imutável:&lt;/strong&gt; depois de construído, nada pode mudar. Para alterar, crie um novo objeto.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Valide no construtor:&lt;/strong&gt; lance exceções específicas do domínio se os dados forem inválidos. Assim, um objeto de valor sempre representa um estado consistente.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implemente equals corretamente:&lt;/strong&gt; compare o valor na classe e todos os atributos relevantes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ao adotar Value Objects, você:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Elimina duplicação de validações.&lt;/li&gt;
&lt;li&gt;Torna o código autodocumentado.&lt;/li&gt;
&lt;li&gt;Previne estados inválidos.&lt;/li&gt;
&lt;li&gt;Facilita a evolução do domínio.&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Desafio:&lt;/strong&gt; &lt;em&gt;Liste 3 atributos do seu sistema atual que são primitivos mas deveriam ser Value Objects e deixe aqui nos comentários. Exemplo: placaVeiculo, numCartaoCredito, intervaloData.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Objetos de Valor &lt;strong&gt;não são&lt;/strong&gt; exclusivos para quem adota o DDD – são uma ferramenta prática para acabar com a bagunça dos tipos primitivos e validações repetidas. Eles transformam regras de negócio escondidas em código limpo, seguro e reutilizável.&lt;/p&gt;

&lt;p&gt;Então lembre-se: todo CPF, e-mail ou telefone no seu sistema é um valor que merece respeito. Trate-os como objetos – e eles tratarão bem o seu domínio.&lt;/p&gt;

&lt;p&gt;Agora vá, identifique um atributo extraia seu primeiro Value Object, e sinta a diferença na consistência. Seu eu do futuro vai agradecer.&lt;/p&gt;

&lt;p&gt;Gostou? Então &lt;strong&gt;não perca os próximos&lt;/strong&gt;. Me siga e receba mais artigos sobre código de qualidade e arquitetura de software de verdade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔔 Compartilhe com quem ainda valida CPF com if solto no código.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vamos juntos construir softwares mais expressivos e robustos! &lt;strong&gt;Até a próxima, galerinha&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>cleancode</category>
      <category>object</category>
      <category>php</category>
    </item>
    <item>
      <title>Facades no Laravel entregam velocidade, mas cobram o preço em acoplamento</title>
      <dc:creator>Meriéli Manzano</dc:creator>
      <pubDate>Thu, 14 May 2026 15:15:00 +0000</pubDate>
      <link>https://dev.to/merielimanzano/facades-sao-o-cafe-do-laravel-resolvem-rapido-mas-viciam-e-escondem-o-problema-1o8c</link>
      <guid>https://dev.to/merielimanzano/facades-sao-o-cafe-do-laravel-resolvem-rapido-mas-viciam-e-escondem-o-problema-1o8c</guid>
      <description>&lt;p&gt;As Facades do Laravel são algo que o framework disponibiliza e que vejo muitos projetos usando em controllers, services, entities e até mesmo em repositories. Eu pessoalmente já usei e ainda uso Facade, a produtividade inicial é maravilhosa, o código fica enxuto e você entrega valor rápido porque não precisa instanciar, é só usar a Facade direto, é lindo.&lt;/p&gt;

&lt;p&gt;Só que tenho refletido sobre uma coisa que não aparece no curto prazo, especialmente quando a aplicação cresce e a manutenção se estende por anos.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pense num cenário real:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Você tem uma classe de serviço que usa várias outras Facades internas. Funciona perfeitamente bem, até o dia que você precisar reaproveitar aquela lógica num script isolado fora do Laravel. Ou testar aquela classe sem o framework inteiro carregado.&lt;/p&gt;

&lt;p&gt;Aí você percebe: a classe não diz o que ela precisa. Você só descobre abrindo o código linha por linha. E cada facade é um fio invisível conectado ao Laravel.&lt;/p&gt;

&lt;p&gt;Não é que Facade seja errada. É que elas resolvem um problema (velocidade) e criam outro silencioso (acoplamento implícito). Cada time escolhe seu trade-off. Esse acoplamento forte com o framework vai justamente contra as regras de dependência da Clean Architecture.&lt;/p&gt;

&lt;p&gt;Para entender por que, precisamos saber que Facades do Laravel funcionam como “proxies estáticos” que fornecem acesso a objetos registrados no service container. São uma “ponte” entre uma chamada de método estático e a instância real de um objeto, resolvido em tempo de execução pelo container de injeção de dependência do Laravel.&lt;/p&gt;

&lt;p&gt;Abaixo separei algumas vantagens e desvantagens que vai te ajudar a decidir quando realmente devemos usá-las.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vantagens:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Código mais rápido de escrever e mais fácil de ler, acelerando drasticamente o desenvolvimento e a prototipagem.&lt;/li&gt;
&lt;li&gt;Facilidade para testar usando &lt;code&gt;shouldReceive()&lt;/code&gt; e Fakes para garantir que a lógica real não seja acionada, diferente de métodos estáticos tradicionais. &lt;em&gt;“Obs: Mas já que a IA está criando testes com maestria, será que isso faz realmente a diferença?”&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Tem comportamento Singleton, sendo resolvida uma única vez por requisição.&lt;/li&gt;
&lt;li&gt;Pode ser usada em qualquer lugar, sem a necessidade de passar dependência manualmente pelo construtor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Desvantagens:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Acoplamento que não quebra a testabilidade, mas suja as dependências “ou seja, não é declarada a dependência, ela fica implícita e só é descoberta olhando o corpo dos métodos”. O que dificulta manutenção e reuso.&lt;/li&gt;
&lt;li&gt;Viola a Inversão de Dependência, a classe depende diretamente de uma implementação concreta ou de uma interface resolvida pela Facade, não de uma abstração definida pelo domínio, injetada de fora.&lt;/li&gt;
&lt;li&gt;Lógica acoplada ao Framework, porque Facades só funcionam com o Facade Root inicializado. Se um dia pretender reutilizar aquela classe em um script isolado, pacote PHP puro ou outro framework, precisará reescrever tudo.&lt;/li&gt;
&lt;li&gt;Por serem muito convenientes, facilitam a violação silenciosa do SRP: desenvolvedores adicionam novas responsabilidades indiretas sem que o construtor da classe sinalize o inchaço.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deixo mais dois pontos de atenção que não são necessariamente desvantagens, mas devem ser conhecidos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Embora sejam testáveis, os mocks são aplicados globalmente – afetam todas as chamadas à facade durante o teste, não apenas uma instância específica. Sem uma limpeza explícita (como &lt;code&gt;clearResolvedInstances()&lt;/code&gt; no &lt;code&gt;tearDown&lt;/code&gt;), pode haver vazamento de comportamento entre testes. É um ponto de atenção, mas contornável.&lt;/li&gt;
&lt;li&gt;A facade não é mais rápida que a injeção direta. Cada chamada paga o custo do &lt;code&gt;__callStatic()&lt;/code&gt; e da resolução da chave no container – é tecnicamente mais lenta, mas na prática você nunca vai medir essa diferença.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Facades são ótimas em Controllers, Commands, Jobs (camadas mais próximas da infra). E podem até ser usadas em protótipos e MVPs, são uma excelente escolha para garantir velocidade de entrega.&lt;/p&gt;

&lt;p&gt;Já nas camadas de domínio, serviços de aplicação, objetos de valor, entidades... faz mais sentido injetar uma interface no construtor para desacoplar sua lógica de negócio.&lt;/p&gt;

&lt;p&gt;Tá tudo bem usar Facades. Esse post não é uma regra. É só uma lente que eu peguei dos meus estudos e que, de vez em quando, me faz perguntar: “será que aqui vale a pena declarar a dependência?”&lt;/p&gt;

&lt;p&gt;Fico curiosa pra saber como vocês lidam com isso. Já sentiu alguma dificuldade prática com Facades para testar, evoluir ou reaproveitar lógica? Ou acham que eliminar Facades do Laravel da camada de negócio é uma sofisticação desnecessária?&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>Produtividade com Testes e Vue usando VSCode</title>
      <dc:creator>Meriéli Manzano</dc:creator>
      <pubDate>Mon, 26 Sep 2022 14:18:02 +0000</pubDate>
      <link>https://dev.to/merielimanzano/produtividade-com-testes-e-vue-usando-vscode-593k</link>
      <guid>https://dev.to/merielimanzano/produtividade-com-testes-e-vue-usando-vscode-593k</guid>
      <description>&lt;p&gt;Criar testes bem organizados, separados e com uma boa descrição no Jest ou inserir toda estrutura inicial padrão de um componente Vue, pode ser verboso. Mas configurando o VS Code é possível inserir automaticamente toda pré-estrutura com a escrita de apenas uma palavra.&lt;/p&gt;

&lt;p&gt;Desenvolvendo com o Visual Studio Code você pode turbinar sua produtividade de uma forma muito simples com &lt;strong&gt;Snippets&lt;/strong&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Como funciona?
&lt;/h2&gt;

&lt;p&gt;Os snippets salvos no seu VS Code geram códigos pré-definidos ao digitar uma determinada palavra escolhida na sua configuração.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlv2019kffldwilqcq87.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlv2019kffldwilqcq87.gif" alt=" " width="695" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Como criar um snippet
&lt;/h2&gt;

&lt;p&gt;Com o VS Code aberto vá em &lt;strong&gt;File&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Preferences&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Configure User Snippets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Na paleta de comandos aberta digite a linguagem para qual deseja exibir o snippet, no caso de testes Jest digite &lt;strong&gt;typescript&lt;/strong&gt; se for sua linguagem utilizada.&lt;/p&gt;

&lt;p&gt;Será aberto um arquivo typescript.json com instruções para criar seu snippet.&lt;/p&gt;

&lt;p&gt;Cada snippet tem a seguinte estrutura:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"Nome do Snippet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Palavra&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;que&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;chama&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;snippet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Corpo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;código&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ser&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;gerado&lt;/span&gt;&lt;span class="w"&gt;         
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Descrição&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;detalhes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;código&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Ao escrever o body, em linhas que houver indentação use &lt;strong&gt;&lt;code&gt;\t&lt;/code&gt;&lt;/strong&gt; para aplicar a tabulação necessária. Cada &lt;code&gt;\t&lt;/code&gt; representa um Tab.&lt;/p&gt;

&lt;p&gt;Para indicar as pausas do Tab para digitação de texto pelo código use &lt;strong&gt;&lt;code&gt;$1&lt;/code&gt;&lt;/strong&gt;, substituindo o número pela ordem de inserção desejada em cada trecho.&lt;/p&gt;

&lt;p&gt;Também é possível indicar o tipo de conteúdo a ser inserido em cada pausa com &lt;strong&gt;&lt;code&gt;${1:conteudo}&lt;/code&gt;&lt;/strong&gt; dando uma ideia do que escrever. E para definir um texto igual a ser usado em várias partes do código use o mesmo número de ID em todas partes que o texto for repetir.&lt;/p&gt;

&lt;p&gt;Abaixo montei um snippet completo para Jest usando BDD que você pode aproveitar nos seus códigos ou adaptar como preferir:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;O snippet de Vue é para uso com composition Api usando Typescript e Sass scoped, mas você pode adaptar como preferir:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;O artigo é para produtividade com Testes e Vue, mas também é possível criar snippets para outras linguagens. &lt;/p&gt;

&lt;p&gt;Espero ter ajudado! 😊&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>braziliandevs</category>
      <category>vue</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
