<?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: Leonardo Lemos</title>
    <description>The latest articles on DEV Community by Leonardo Lemos (@jleonardolemos).</description>
    <link>https://dev.to/jleonardolemos</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F442611%2F2cef7dbd-6ab2-4063-b9b6-34b8bd79167b.jpeg</url>
      <title>DEV Community: Leonardo Lemos</title>
      <link>https://dev.to/jleonardolemos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jleonardolemos"/>
    <language>en</language>
    <item>
      <title>Imagem Docker. Para rodar projetos PHP em menos de 30 segundos.</title>
      <dc:creator>Leonardo Lemos</dc:creator>
      <pubDate>Tue, 06 Jan 2026 15:02:25 +0000</pubDate>
      <link>https://dev.to/convenia/imagem-docker-para-rodar-projetos-php-em-menos-de-30-segundos-dee</link>
      <guid>https://dev.to/convenia/imagem-docker-para-rodar-projetos-php-em-menos-de-30-segundos-dee</guid>
      <description>&lt;h2&gt;
  
  
  Release da imagem PHP Full da Convenia, com PHP 8.5
&lt;/h2&gt;

&lt;p&gt;Nesta semana, lançamos a &lt;strong&gt;versão 8.5 da nossa imagem Docker para aplicações PHP&lt;/strong&gt;, agora com &lt;strong&gt;PHP 8.5&lt;/strong&gt; e nesse post vamos falar um pouco de como utilizar essa imagem.&lt;/p&gt;

&lt;p&gt;Na &lt;strong&gt;Convenia&lt;/strong&gt;, a maior &lt;em&gt;HR Tech&lt;/em&gt; do Brasil, utilizamos essa imagem &lt;strong&gt;de forma padronizada em todos os nossos projetos&lt;/strong&gt;. Isso significa que qualquer desenvolvedor da equipe sabe exatamente como iniciar, testar e implantar aplicações sem surpresas no ambiente — seja em desenvolvimento, homologação ou produção. &lt;a href="https://dev.to/convenia/utilizando-uma-imagem-base-para-suas-aplicacoes-php-1g8a"&gt;Nesse post&lt;/a&gt; abordamos todas as vantagens de padronizar a forma de executar aplicações&lt;/p&gt;

&lt;p&gt;Essa imagem está disponível desde a versão &lt;strong&gt;8.1&lt;/strong&gt;, sendo amplamente &lt;strong&gt;testada e utilizada&lt;/strong&gt; em produção em centenas de projetos e pipelines da Convenia e já acumula mais de 10K de pulls no &lt;a href="https://hub.docker.com/r/convenia/php-full" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  O que é a imagem Docker PHP Full?
&lt;/h2&gt;

&lt;p&gt;A imagem Docker &lt;strong&gt;PHP Full&lt;/strong&gt; é um ambiente containerizado que já contém tudo o que aplicações PHP precisam para rodar com segurança e desempenho:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP pré-configurado com versões modernas&lt;/li&gt;
&lt;li&gt;Servidor web integrado&lt;/li&gt;
&lt;li&gt;Ferramentas úteis como Composer, cron e extensões comuns&lt;/li&gt;
&lt;li&gt;Testes de integridade e atualizações regulares&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Convenia mantém esse padrão há diversas versões — do &lt;strong&gt;PHP 8.1 até o novo 8.5&lt;/strong&gt; — garantindo estabilidade e suporte contínuo para todos os projetos da empresa.&lt;/p&gt;




&lt;h2&gt;
  
  
  Como usar a imagem
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Instalação
&lt;/h3&gt;

&lt;p&gt;Para começar com a imagem PHP Full da Convenia, basta usar o Docker ou Docker Compose.&lt;/p&gt;

&lt;h4&gt;
  
  
  Usando Docker
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 80:80 convenia/php-full:8.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ao acessar o seu localhost será possível ver uma documentação similar a essa&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%2Fo0pfg0js1hbg5pqqortx.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.amazonaws.com%2Fuploads%2Farticles%2Fo0pfg0js1hbg5pqqortx.png" alt="Documentação da imagem sendo servida pela própria imagem em execução" width="652" height="1053"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Essa página é a própria documentação da imagem sendo servida pelo PHP + nginx. No proximo exemplo com o docker compose vemos como executar a nossa aplicação dentro da imagem, é só montar um volume ou copiar a aplicação para o diretório &lt;code&gt;/var/www/app&lt;/code&gt; dentro do container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usando com Docker Compose
&lt;/h3&gt;

&lt;p&gt;Crie um arquivo &lt;code&gt;docker-compose.yml&lt;/code&gt; na raiz do seu projeto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;convenia/php-full:8.5&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/var/www/app&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois, execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse arquivo pode ser colocado na raiz de um projeto Laravel por exemplo, e ao subir a stack do compose o projeto já deve executar sem maiores problemas.&lt;/p&gt;

&lt;p&gt;Na verdade é possível colocar esse arquivo em qualquer aplicação PHP que tenha a pasta public como entrypoint do request, aplicações Symfony, Laravel. Se o entrypoint for na raiz apenas acrescente a pasta public ao montar o volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/var/www/app/public&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Atualizações automáticas
&lt;/h3&gt;

&lt;p&gt;A imagem é construída e atualizada semanalmente para garantir o uso de versões seguras e livres de vulnerabilidades conhecidas, com escaneamento contínuo de dependências. O último build pode ser consultado no &lt;a href="https://github.com/convenia/php-full-8.1-image/actions" rel="noopener noreferrer"&gt;workflow do Github Actions&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Segurança
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;O serviço FPM (porta 9000) é exposto &lt;strong&gt;apenas internamente no container&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;É recomendado &lt;strong&gt;bloquear a porta 9000&lt;/strong&gt; em ambientes externos&lt;/li&gt;
&lt;li&gt;A imagem já vem com as configurações de segurança recomendadas para produção. Como desabilitar mensagens de erros e afins.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Exemplos de customização
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Substituir configuração do Nginx
&lt;/h3&gt;

&lt;p&gt;Você pode substituir a configuração padrão do servidor web:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;convenia/php-full:8.5&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./nginx.conf:/etc/nginx/http.d/default.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assim adapta o container às regras específicas do seu projeto.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tarefas agendadas com cron
&lt;/h2&gt;

&lt;p&gt;A imagem já inclui o binário &lt;code&gt;cron&lt;/code&gt;, facilitando tarefas agendadas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Adicione um arquivo &lt;code&gt;crontab&lt;/code&gt; com os comandos desejados;&lt;/li&gt;
&lt;li&gt;Copie para o container:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; crontab /etc/crontabs/root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Inicie cron no container:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; mycronimage crond &lt;span class="nt"&gt;-l&lt;/span&gt; 2 &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso facilita execução de comandos como o scheduler do Laravel. &lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Seguem uma listinha com as vantagens da utilização dessa imagem em projetos PHP:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistência entre ambientes&lt;/li&gt;
&lt;li&gt;Facilidade de uso&lt;/li&gt;
&lt;li&gt;Atualizações contínuas&lt;/li&gt;
&lt;li&gt;Padrão único para toda a empresa&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>php</category>
    </item>
    <item>
      <title>Spot By NetApp análise da utilização do Elasticgroup na Convenia</title>
      <dc:creator>Leonardo Lemos</dc:creator>
      <pubDate>Fri, 26 May 2023 20:24:37 +0000</pubDate>
      <link>https://dev.to/convenia/spot-by-netapp-analise-da-utilizacao-do-elasticgroup-na-convenia-519h</link>
      <guid>https://dev.to/convenia/spot-by-netapp-analise-da-utilizacao-do-elasticgroup-na-convenia-519h</guid>
      <description>&lt;p&gt;Nesse post tenho como objetivo contar um pouco de como está sendo a utilização da &lt;a href="https://spot.io/" rel="noopener noreferrer"&gt;Spot by NetApp&lt;/a&gt;. Importante analisar que a Spot tem um monte de produtos voltados para cloud, nesse post vou analisar apenas o &lt;a href="https://spot.io/resources/aws-autoscaling/understanding-ec2-auto-scaling-groups/" rel="noopener noreferrer"&gt;Elasticgroup&lt;/a&gt; que é o produto da Spot voltado para gerenciar as instâncias EC2, similar ao &lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/auto-scaling-groups.html" rel="noopener noreferrer"&gt;AutoScalingGroup(ASG)&lt;/a&gt; da AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OBS: Sempre que me referir a "Spot" com inicial maiúscula estou me referindo à empresa SpotByNetapp e sempre que eu me referir a "spot" com inicial minúscula estou me referindo ao tipo de instancia spot da AWS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Na Convenia já utilizávamos instâncias spot para todo serviço que não fossem crítico e admitissem interrupções. No mês da black friday utilizamos exclusivamente instâncias “On Demand” para evitar interrupções massivas devido ao esgotamento da oferta spot, esse foi o resultado:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F9ygrilmgtnpk6zo4vyjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F9ygrilmgtnpk6zo4vyjr.png" alt="Imagem mostrando um spike de custo no mês da black friday. aproximademente o dobro do custo."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No gráfico acima existe um spike de custo em dezembro causado pela utilização das instancias "On Demand". Após analisar o custo fizemos uma reunião de &lt;a href="https://dev.to/convenia/transformando-falhas-em-oportunidades-a-importancia-da-reuniao-de-postmortem-45jg"&gt;post mortem&lt;/a&gt; e decidimos avaliar o &lt;a href="https://spot.io/resources/aws-autoscaling/understanding-ec2-auto-scaling-groups/" rel="noopener noreferrer"&gt;Elasticgroup&lt;/a&gt; da Spot que promete fallback para “on demand” caso a oferta spot se esgote.&lt;/p&gt;

&lt;p&gt;De inicio posso dizer que gostamos de resultado final e passamos todos as instâncias para serem gerenciadas pelo Elasticgroup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como foi a integração??
&lt;/h2&gt;

&lt;p&gt;Sempre que precisamos testar qualquer coisa dentro da Convenia fazemos uma &lt;a href="https://blog.randon.com.br/proof-of-concept/" rel="noopener noreferrer"&gt;POC&lt;/a&gt; com algum serviço interno para entender realmente os pontos negativos e positivos, e foi isso que fizemos.&lt;/p&gt;

&lt;p&gt;O primeiro passo para integração é fazer a ligação da conta da Spot com a sua conta da AWS seguindo o &lt;a href="https://docs.spot.io/cloud-analyzer/getting-started/connect-account-multiple-organizations-to-single-master-payer" rel="noopener noreferrer"&gt;passo a passo&lt;/a&gt; na própria documentação da Spot, esse passo apesar de muito bem explicado contém algumas etapas, caso haja dúvida na implantação ou mesmo em relação as possibilidades que a Spot pode trazer sugiro uma conversa com o pessoal da &lt;a href="https://www.realcloud.systems/" rel="noopener noreferrer"&gt;RealCloud&lt;/a&gt; que nos ajudou bastante a entender os produtos da spot e entender como fazer essa migração.&lt;/p&gt;

&lt;p&gt;Após linkar a conta da AWS com a Spot seguimos um wizard que importa as configurações do AutoScalingGroup da aws para um Elasticgroup dentro da spot. Apenas isso foi necessário para ter as instancias sendo gerenciadas pela Spot, após a criação do Elastic group podemos inativar o nosso AutoScalingGroup(setando a capacidade desejada, mínima e máxima para 0) ou mesmo remove-lo.&lt;/p&gt;

&lt;p&gt;No nosso caso, fazer essa migração para o primeiro serviço durou cerca de uma hora. Continuamos monitorando para ver como o Elasticgroup faria as trocas de instancias para conseguir mais saving e fizemos mais alguns testes para forçar refresh de instancias e interrupções para entender como ele lida com isso, na nossa experiência não tivemos nem uma surpresa, simplesmente não perde em nada para o ASG da AWS, ainda tem algumas funcionalidades a mais como vou falar mais para frente.&lt;/p&gt;

&lt;p&gt;Um outro ponto importante que vale ressaltar durante a integração é que a RealCloud nos acompanhou durante todo o processo, e o billing começou a rodar apenas após a integração de todos os serviços.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quais as vantagens da adoção da Spot?
&lt;/h2&gt;

&lt;p&gt;A resposta lógica para essa pergunta seria custo mas vou começar falando das coisas que não tem haver com custo, o custo vamos analisar mais profundamente no próximo tópico, então seguem as outras vantagens:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fallback para “On Demand”&lt;/strong&gt;: Esse é um dos principais motivadores para a adoção na minha opinião, é muito difícil prever a oferta spot com eficiência, a AWS promete avisar sobre interrupções com 2 minutos de antecedência, dessa forma é possível construir automações para lidar com a interrupção mas eu particularmente não gosto de lidar com isso artesanalmente. O que acabamos fazendo é manter as instancias mais críticas com “On Demand”. Poder confiar que o Elasticgroup vai fazer o fallback corretamente te permite spotizar até as instancias mais críticas e evita que você faça a loucura de voltar todas as instancias para “On Demand” nas vesperas de uma black friday causando aquele spike de custo mostrado no inicio.&lt;/p&gt;

&lt;p&gt;Abaixo os logs do Elasticgroup fazendo a substituição de instancias spot pot "On Demand":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5/21/2023, 5:30 AM, INFO, Updating group
5/21/2023, 5:30 AM, INFO, Falling back to on-demand, no spot instances could be launched.
5/21/2023, 5:30 AM, INFO, Scheduling action: [scale] ( , scaleTargetCapacity: 5 , scaleMinCapacity: 5 , scaleMaxCapacity: 8)
5/21/2023, 5:30 AM, INFO, Instances: [i-01b3bc7d84018f540,i-0762fba5e0348ef70,i-0d204f6f3e1b6a9a5,i-04182456095d6e21d] have been launched. Reason: Group update
5/21/2023, 5:30 AM, INFO, Updating Group Succeed
5/21/2023, 5:31 AM, INFO, Instance [i-0d204f6f3e1b6a9a5,i-01b3bc7d84018f540,i-04182456095d6e21d,i-0762fba5e0348ef70] successfully registered to load balancer: rh-live-core-84
5/21/2023, 5:31 AM, INFO, Instance [i-0d204f6f3e1b6a9a5,i-01b3bc7d84018f540,i-04182456095d6e21d,i-0762fba5e0348ef70] successfully registered to load balancer: rh-live-internal-core-84
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Scheduled scalling:&lt;/strong&gt; O Elasticgroup nos permite definir alguns schedulers no formato de cron para escalar as maquinas em determinados horários. Na Convenia deixamos no mínimo duas instancias rodando para os serviços principais durante o dia e por ser um sistema muito focado no publico brasileiro, o tráfego cai bastante durante a noite, nesse caso podemos configurar esse scheduler para desligar as instancias redundantes pela madrugada, trazendo um saving ainda maior&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ff3f64vz336uqrkxdmegg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ff3f64vz336uqrkxdmegg.png" alt="Imagem mostrando a configuração do scheduler da spot, diminuindo a capacidade durante a noite e retomando pela manhâ"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simplicidade na organização de recursos:&lt;/strong&gt; Para manter práticas do CodeReview e manter uma padronização mínima dos serviços utilizamos o &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;terraform&lt;/a&gt;. Geralmente ao criar o autoscaling dentro da AWS acabamos criando uma serie de recursos avulsos, como scaling policies, asg attachments entre outros. Ao adicionar o recurso de Elasticgroup no nosso terraform notamos uma boa simplificação visto que o Elasticgroup é um único recursão que aglomera todas as funcionalidades, no final trocamos diversos recursos do terraform por apenas um.&lt;/p&gt;

&lt;p&gt;Abaixo um exemplo real de um Elasticgroup criado pelo terraform na Convenia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"spotinst_elastigroup_aws"&lt;/span&gt; &lt;span class="s2"&gt;"core_webserver_live_asg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"core_webserver_live_asg"&lt;/span&gt;
  &lt;span class="nx"&gt;spot_percentage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="nx"&gt;orientation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"balanced"&lt;/span&gt;
  &lt;span class="nx"&gt;draining_timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;
  &lt;span class="nx"&gt;fallback_to_ondemand&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;desired_capacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="nx"&gt;min_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="nx"&gt;max_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
  &lt;span class="nx"&gt;capacity_unit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"instance"&lt;/span&gt;

  &lt;span class="nx"&gt;scaling_target_policy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;policy_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CPU track scaling"&lt;/span&gt;
    &lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS/EC2"&lt;/span&gt;
    &lt;span class="nx"&gt;metric_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CPUUtilization"&lt;/span&gt;
    &lt;span class="nx"&gt;statistic&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"average"&lt;/span&gt;
    &lt;span class="nx"&gt;unit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"percent"&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;
    &lt;span class="nx"&gt;cooldown&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;instance_types_ondemand&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.small"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_types_spot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"t3.small"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"t3a.small"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;subnet_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet_3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet_4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Linux/UNIX"&lt;/span&gt;

  &lt;span class="nx"&gt;target_group_arns&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;aws_alb_target_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;core_live_target_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;aws_alb_target_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;core_internal_live_target_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;security_groups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;core_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;enable_monitoring&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nx"&gt;ebs_optimized&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nx"&gt;image_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vault_generic_secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;swarm_base_image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"image_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;ebs_block_device&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;device_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/dev/sda1"&lt;/span&gt;
    &lt;span class="nx"&gt;delete_on_termination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;volume_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"32"&lt;/span&gt;
    &lt;span class="nx"&gt;volume_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gp3"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;user_data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templatefile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"user_data.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;swarm_token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;swarm_token&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Workload"&lt;/span&gt;
    &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"live-core"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Name"&lt;/span&gt;
    &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"live-core-webserver-elasticgroup"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;iam_instance_profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-super-secret-policy arn"&lt;/span&gt;
  &lt;span class="nx"&gt;health_check_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"TARGET_GROUP"&lt;/span&gt;
  &lt;span class="nx"&gt;health_check_grace_period&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;

  &lt;span class="nx"&gt;scheduled_task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;is_enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;task_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scale"&lt;/span&gt;
    &lt;span class="nx"&gt;cron_expression&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0 23 * * *"&lt;/span&gt;
    &lt;span class="nx"&gt;scale_target_capacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nx"&gt;scale_min_capacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nx"&gt;scale_max_capacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;scheduled_task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;is_enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;task_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scale"&lt;/span&gt;
    &lt;span class="nx"&gt;cron_expression&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"30 8 * * *"&lt;/span&gt;
    &lt;span class="nx"&gt;scale_target_capacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="nx"&gt;scale_min_capacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="nx"&gt;scale_max_capacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ignore_changes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;desired_capacity&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Custo sobre o saving: Talvez esse seja uma dos principais atrativos na venda dos serviços pela Spot, o modelo de billing da Spot é sobre o seu saving logo não existe a possibilidade de você pagar por algo que você não vai ter, retorno garantido! Se seu billing não diminui você não paga nada.&lt;/p&gt;

&lt;p&gt;Esses foram algumas das vantagens que notamos nessa migração mas confesso que a Convenia não utiliza todo o poder da Spot, ainda existe possibilidade para spotizar instancias statefull entre outras coisas, independente do seu case acho difícil acreditar que a Spot não tem alguma solução que possa trazer algum tipo de saving, quanto maior o seu sistema(em quantidade de maquinas) mais passa a fazer sentido essa adoção.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mas e o Custo??
&lt;/h2&gt;

&lt;p&gt;O principal motivador para a adoção da Spot é redução de custos, essa é a bandeira deles! A Convenia tem uma infra relativamente pequena e para quantificar esse saving adequadamente precisamos nos atentar a alguns detalhes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F9ygrilmgtnpk6zo4vyjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F9ygrilmgtnpk6zo4vyjr.png" alt="Imagem mostrando um spike de custo no mês da black friday. aproximademente o dobro do custo."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como mostrado no gráfico de billing acima, que contém apenas custo “On Demand e Spot”, em setembro de 2022 e abril de 2023 o custo permanece mais ou menos no mesmo patamar, isso é bom pois nesses últimos meses nossa infra cresceu um pouco, spotizar as instancias críticas que antes não eram spot nos permitiu esse resultado.&lt;/p&gt;

&lt;p&gt;A spot tem um custo sobre o saving: nossa conta da spot varia em torno de 130 Bidens. Quando adicionamos esse custo ao valor mensal mostrado no billing da aws notamos que a conta de abril de 2023 supera um pouco a conta de setembro de 2022, isso se deve ao crescimento da nossa infra nesses meses mas mesmo sem esse crescimento não acredito que o saving teria sido expressivo a ponto de justificar a adoção da Spot apenas por custo. Importante notar que quanto maior for a sua infra maior é a justificativa para a adoção, talvez a Convenia não tenha tido resultados tão expressivos pelo tamanho da infra e pelo fato de já utilizar Instancias spot para alguns serviços, claro que o saving poderia ser ainda maior se spotizarmos as instancias statfull que temos.&lt;/p&gt;

&lt;p&gt;Nosso total de desconto devido a instancias spot ficavam em torno de 65%, a taxa de Spot é de 28% sobre esse saving de 65 mas o que ninguem conta é que para uma empresa brasileira utilizando a Spot teremos algumas impostos que somados com a taxa da Spot podem arranhar os 40% desse saving.&lt;/p&gt;

&lt;p&gt;Outra detalhe muito importante que é radicalmente negligenciado é o custo da mão de obra, por mais que a integração seja simples, trocar os ASGs massivamente pelo Elasticgroup exige uma validação mínima além de um acompanhamento para ver se está funcionando como previsto e se está atingindo o saving previsto, no fim o time vai morrer com alguns dias em cima dessa integração e mão de obra é caro!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;No final, analisando o conjunto de vantagens e desvantagens julgo como positiva a utilização da Spot, se tivesse que julgar apenas a redução de custo com computing já não faria tanto sentido, mas ter um fallback para “on demand” automático, simples e confiável é a maior das vantagens para min, visto que não vamos ver spikes de custo nas épocas de black friday, o que já compensaria o preço pago pelo serviço da spot.&lt;/p&gt;

&lt;p&gt;Essa conclusão de custo é muito particular da Convenia que tem uma infra relativamente pequena e já era bem espotizada antes. Conforme o crescimento da empresa acredito que o saving proporcionado passa a ser cada vez mais relevante. No aws summit de 2022 o Nubank mostrou um pouco do case deles de utilização da Spot, com o volume deles a spotização pode trazer um valor absurdo de custos, pra quem quiser ver esse case mais de perto só ver &lt;a href="https://www.youtube.com/watch?v=8rtuCSldLDY" rel="noopener noreferrer"&gt;aqui no youtube&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>convenia</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>Mongo Atlas, porque utilizar o recurso de VPC Peering no seu cluster?</title>
      <dc:creator>Leonardo Lemos</dc:creator>
      <pubDate>Thu, 29 Dec 2022 12:39:52 +0000</pubDate>
      <link>https://dev.to/convenia/mongo-atlas-porque-utilizar-o-recurso-de-vpc-peering-no-seu-cluster-32cn</link>
      <guid>https://dev.to/convenia/mongo-atlas-porque-utilizar-o-recurso-de-vpc-peering-no-seu-cluster-32cn</guid>
      <description>&lt;p&gt;Nesse post quero compartilhar uma dica bem valiosa que adotamos na &lt;a href="https://convenia.com.br" rel="noopener noreferrer"&gt;Convenia&lt;/a&gt;. Nossa stack é composta de microserviços e utilizamos extensamente MongoDB e RabbitMQ, esse último para comunicação entre os serviços, como mostrado &lt;a href="https://imasters.com.br/php/aprenda-agora-arquitetura-event-driven-com-laravel-pigeon" rel="noopener noreferrer"&gt;nesse post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Toda infra da Convenia roda dentro da AWS e a AWS não tem uma boa opção gerenciada de MongoDB ou RabbitMQ, então utilizamos o &lt;a href="https://www.mongodb.com/atlas/database" rel="noopener noreferrer"&gt; Mongo Atlas&lt;/a&gt; e o &lt;a href="https://www.cloudamqp.com/" rel="noopener noreferrer"&gt;CloudAmqp&lt;/a&gt; para aliviar o trabalho de manutenção nesses serviços.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problemas
&lt;/h2&gt;

&lt;p&gt;Vamos tomar como exemplo a comunicação entre o serviço gerenciado do Mongo Atlas e a nossa aplicação, após provisionar tudo da maneira mais simples devemos chegar a essa arquitetura:&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%2Fmxp72kpmz1g7tzybt5v1.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.amazonaws.com%2Fuploads%2Farticles%2Fmxp72kpmz1g7tzybt5v1.png" alt="Diagrama Mongo Atlas sem VPC Peering" width="800" height="662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na verdade isso funciona relativamente bem, a aplicação funcionaria de maneira aceitável no entando parece um caminho longo da nossa aplicação(API) até o banco de dados o que não é o ideal, na verdade o banco de dados deve ficar o mais "perto" possível da nossa aplicação, na arquitetura acima eu consigo pontuar alguns problemas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Custo: o &lt;a href="https://docs.aws.amazon.com/pt_br/vpc/latest/userguide/vpc-nat-gateway.html" rel="noopener noreferrer"&gt;NAT gateway&lt;/a&gt; faz parte de toda arquitetura de rede que inclui uma subnet privada, a grosso modo ele é responsável por prover acesso à internet aos componentes internos da rede. O gande problema é que esse NAT é um elemento caro na infraestrutura pois tem um custo apenas por existir(pois tem ip público) e também cobra um custo relativo ao tráfego que passa por ele. No nosso caso os dados da nossa aplicação estão passando por ele para chegar ao MongoDB, o mesmo não aconteceria se o banco estivesse dentro da mesma subnet da aplicação, nesse caso a aplicação conseguiria conversar diretamente com o banco.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance: Claro, sair pelo internet gateway para internet e passar "sei la por onde" até chegar no nosso banco de dados vai introduzir um pouco de latência e naturalmente diminuir a resiliência da aplicação, o mesmo não aconteceria com um banco na mesma subnet da nossa aplicação.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Segurança: De todos os pontos, este é o item mais preocupante, principalmente no caso da Convenia que trafega dados pessoais em época de LGPD. A conexão entre a aplicação e os serviços externos é criptografada, isso significa que é relativamente segura pois mesmo se o tráfego for interceptado os dados não podem ser acessados. Mas mesmo com a criptografia é desconcertante ter o tráfego suscetível a um ataque, seria melhor se esses dados nem passassem pelo mundo externo.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Solução
&lt;/h2&gt;

&lt;p&gt;Pensando bem, nosso cluster de MongoDB está provisionado dentro da AWS, nossa aplicação também está dentro da AWS, nossa aplicação realmente precisa fazer toda essa volta para chegar até o banco? Para o nosso bem a resposta para essa pergunta é "não", quase todos os possíveis serviços que você pode pensar em consumir oferecem uma maneira de garantir que a comunicação com eles permaneça inteiramente dentro da AWS, a maneira mais comum é o &lt;a href="https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html" rel="noopener noreferrer"&gt;VPC Peering&lt;/a&gt; que tanto o &lt;a href="https://www.mongodb.com/docs/atlas/security-vpc-peering/" rel="noopener noreferrer"&gt;Mongo Atlas&lt;/a&gt; quanto o &lt;a href="https://www.cloudamqp.com/blog/amazon-vpc-peering.html" rel="noopener noreferrer"&gt;CloudAmqp&lt;/a&gt; fornecem. O &lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt; por exemplo fornece um &lt;a href="https://docs.datadoghq.com/agent/guide/private-link/?tab=useast1" rel="noopener noreferrer"&gt;endpoint&lt;/a&gt; para acesso direto, dessa forma todo o tráfego permanece dentro da rede interna da AWS e nem passa pelo NAT Gateway.&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%2F6xx355ur6kt1n7b9n1x5.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.amazonaws.com%2Fuploads%2Farticles%2F6xx355ur6kt1n7b9n1x5.png" alt="Diagrama Mongo Atlas com VPC Peering" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após criar o VPC Peering do Mongo Atlas como &lt;a href="https://www.mongodb.com/docs/atlas/security-vpc-peering/" rel="noopener noreferrer"&gt;nesse passo a passo&lt;/a&gt;, chegamos na arquitetura acima, essa arquitetura é muito mais segura, performática e elimina o custo do NAT Gateway. A outra arquitetura nem deveria ser considerada, principalmente se a empresa trabalha com dados sensíveis. Perceba que no diagrama acima nem aparecem os "Internet Gateways", isso porque a partir de agora a aplicação vai consumir a aplicação diretamente através do Peering.&lt;/p&gt;

&lt;p&gt;Com relação aos problemas apontados anteriormente, uma vez que não precisamos mais sair para internet para chegar até nosso banco de dados, o custo com Tráfego do Nat Gateway deve reduzir, talvez se nossa aplicação não precisar de algum outro recurso interno seria possível até remover o NAT Gateway.&lt;/p&gt;

&lt;p&gt;O banco de dados já estava próximo à nossa aplicação, nossa aplicação apenas não conseguia chegar até ele da maneira mais fácil, após fazer o Peering nossa aplicação passa a conhecer o melhor caminho até o banco, ficando a poucos saltos do banco e melhorando a performance e resiliência.&lt;/p&gt;

&lt;p&gt;Por último devemos notar que agora é impossível de um elemento desconhecido interceptar o tráfego entre nossa aplicação e o banco, isso diminui muito a superfície de ataque que um suposto Hackerzinho malvado poderia utilizar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Últimas configurações
&lt;/h2&gt;

&lt;p&gt;Agora já conseguimos atingir 90% dos nossos objetivos, mas para finalizar precisamos impedir que o Mongo Atlas seja acessível externamente. Geralmente esse tipo de serviço gerenciado relacionado a infraestrutura tem uma área de Network como a do Atlas:&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%2Fivluxb4oxaobf129bhn8.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.amazonaws.com%2Fuploads%2Farticles%2Fivluxb4oxaobf129bhn8.png" alt="Configurando regras de acesso ao cluster de mongo" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A imagem acima contém as regras de acesso da Convenia para o Atlas, temos dois peerings logo temos 2 regras de acesso permitindo apenas acessos vindos das 2 VPCs da Convenia. Aqui o ideal é remover qualquer regra permitindo o intervalo &lt;code&gt;0.0.0.0/0&lt;/code&gt;, esse intervalo permite acesso global ao cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;A utilização de recursos como VPC Peering e VPC Endpoints podem aumentar a performance e melhorar a segurança da sua aplicação, bem como diminuir o custo com tráfego no Nat gateway. Aliás mantenha distância do Nat gateway, saia para internet apenas quando for realmente necessário. &lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>aws</category>
      <category>convenia</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Utilizando uma imagem base para suas aplicações PHP</title>
      <dc:creator>Leonardo Lemos</dc:creator>
      <pubDate>Thu, 28 Jul 2022 18:04:00 +0000</pubDate>
      <link>https://dev.to/convenia/utilizando-uma-imagem-base-para-suas-aplicacoes-php-1g8a</link>
      <guid>https://dev.to/convenia/utilizando-uma-imagem-base-para-suas-aplicacoes-php-1g8a</guid>
      <description>&lt;p&gt;Para garantir a padronização das aplicações e facilitar o trabalho do dia a dia, na &lt;a href="//convenia.com.br"&gt;Convenia&lt;/a&gt;, utilizamos uma imagem base para todas as aplicações. O proposito desse post é mostrar as vantagens dessa abordagem e como criar suas aplicações a partir dessa imagem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Porque padronizar a imagem base?
&lt;/h2&gt;

&lt;p&gt;Na &lt;a href="//convenia.com.br"&gt;Convenia&lt;/a&gt; temos uma stack bem homogênea quase que inteiramente em &lt;a href="https://www.php.net/" rel="noopener noreferrer"&gt;PHP&lt;/a&gt;, manter uma imagem diferente em cada projeto aumenta a chance de aparecer um problema específico com uma versão específica de uma biblioteca que está instalada apenas em um projeto, o desenvolvedor que mantém esse projeto terá que resolver esse problema e não terá como reaproveitar o conhecimento dos amigos visto que esse problema ocorreu apenas no projeto dele.&lt;/p&gt;

&lt;p&gt;Seguindo a mesma linha de raciocínio imagine o quão diferente os setups podem parecer sem uma padronização, manter essa padronização ajuda bastante quando o desenvolvedor muda de projeto ou mesmo quando ele faz um "merge request" em um outro projeto, dessa forma ganhamos agilidade por não ter que estudar especificidades de cada projeto.&lt;/p&gt;

&lt;p&gt;Novas features e correções de bugs são centralizadas em apenas uma imagem ao invés de serem feitas em cada projeto.&lt;/p&gt;

&lt;p&gt;Bom e finalmente eliminamos o famoso "na minha maquina funciona". Já que estamos utilizando um container padronizado no ambiente de desenvolvimento porque não utilizar o mesmo container em ambiente de produção(com alguns cuidados claro)? Isso praticamente erradica a possibilidade de ocorrer um erro proveniente da discrepância entre uma biblioteca visto que quase todas as bibliotecas vão ser idênticas. Esse risco só não é nulo porque inevitavelmente vamos ter que instalar alguma extensão antes de enviar para produção(opcache por exemplo).&lt;/p&gt;

&lt;p&gt;Em algum momento teremos que gerar uma nova imagem a partir da imagem base contendo a aplicação para rodar em produção. Se todos os projetos tem a necessidade de ter um webserver por exemplo porque não adicionar esse webserver já na imagem base? Isso vai tornar a etapa de build do pipeline muito mais rápida visto que passamos a maioria das coisas comuns aos projetos para a imagem base, temos um deploy mais ágil!&lt;/p&gt;

&lt;h2&gt;
  
  
  Utilizando a imagem base
&lt;/h2&gt;

&lt;p&gt;Para rodar uma aplicações PHP da forma convencional são necessários 2 containers, o &lt;a href="https://www.php.net/manual/pt_BR/install.fpm.php" rel="noopener noreferrer"&gt;PHP-FPM&lt;/a&gt; e um servidor web como &lt;a href="https://www.nginx.com/" rel="noopener noreferrer"&gt;nginx&lt;/a&gt; ou &lt;a href="https://httpd.apache.org/" rel="noopener noreferrer"&gt;apache&lt;/a&gt;. Rodar esses 2 componentes separadamente, apesar de ser o recomendado, demanda que o desenvolvedor mergulhe em detalhes referentes à comunicação entre os containers e até mesmo configuração de volumes, muitas vezes esses detalhes não são interessantes no momento, desenvolvedores iniciantes se sentem bem perdidos nesses detalhes.&lt;/p&gt;

&lt;p&gt;Analisando outras stacks como nodejs vamos perceber que é relativamente mais simples containerizar esse tipo de aplicação, pois o próprio processo do node já é capaz de servir os requests sendo necessário apenas um container, mas e se podessemos rodar uma aplicação PHP com a mesma simplicidade? Essa é a proposta da imagem &lt;a href="https://hub.docker.com/r/convenia/php-full" rel="noopener noreferrer"&gt;PHP Full&lt;/a&gt; que utilizamos aqui na convenia como imagem base de todas as aplicações PHP.&lt;/p&gt;

&lt;p&gt;Vamos executar a imagem PHP Full com o seguinte comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -p 80:80 convenia/php-full:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O comando acima executa um container apartir da image &lt;code&gt;convenia/php-full:latest&lt;/code&gt; e binda a porta 80 do container à porta 80 do host, logo ao entrar no nosso localhost pelo browser(em windows e macOS pode ser necessário utilizar o IP da vm do docker, dependendo do seu setup) devemos ver a página da documentação:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fic7jr0iph4i64wxx0ovf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fic7jr0iph4i64wxx0ovf.png" alt="Página padrão do container contendo a documentação"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Essa documentação é um arquivo PHP, isso significa que a imagem já está pronta para servir nossa aplicação, tudo que precisamos fazer é colocar nossa aplicação dentro do diretório &lt;code&gt;/var/www/app&lt;/code&gt; que é onde a imagem procura por padrão.&lt;/p&gt;

&lt;p&gt;Para fazer um lab bem rápido vamos executar um comando no terminal para instalar uma aplicação &lt;a href="https://laravel.com/" rel="noopener noreferrer"&gt;Laravel&lt;/a&gt; fresh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --user 1000 --rm -v $(pwd):/app composer create-project laravel/laravel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso deve utilizar o &lt;a href="https://getcomposer.org/" rel="noopener noreferrer"&gt;composer&lt;/a&gt; para criar uma aplicação Laravel bem na pasta em que executamos o comando. Em seguida vamos executar o seguinte comando para criar um container "colocando" a aplicação Laravel que criamos no passo anterior dentro da pasta &lt;code&gt;/var/www/app&lt;/code&gt;, é nessa pasta que o webserver dentro do container olha para servir a aplicação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -p 80:80 -v $(pwd)/laravel:/var/www/app convenia/php-full:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após executar o comando podemos entrar no nosso browser novamente porém agora o resultado é esse:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fpaknc650djxtq735n2wo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpaknc650djxtq735n2wo.png" alt="Página padrão do Laravel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pronto finalmente estamos rodando nossa aplicação PHP dentro do container, e tudo que fizemos foi copiar a aplicação para uma pasta específica dentro do container, esse é o propósito dessa imagem, prover uma forma simples e segura para executar aplicações PHP podendo ser utilizado em ambiente de desenvolvimento e produção.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando um arquivo Docker Compose
&lt;/h2&gt;

&lt;p&gt;Já sabemos como utilizar nossa imagem porém em um ambiente real podemos sentir a necessidade de rodar mais de um container, containers de serviços como mysql e redis para nossa aplicação e o próprio container da nossa aplicação pode começar a ficar complexo pois pode surgir a necessidade de utilizar mais volumes, podemos definir uma network para isolar nossa aplicação. Logo o comando para rodar esse container vai ficando cada vez mais complexo e rodar a stack completa se torna uma tarefa árdua. Para resolver esse problema utilizamos um arquivo &lt;code&gt;docker-compose.yml&lt;/code&gt; que deve ser criado na raiz do projeto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;convenia/php-full:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/var/www/app&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;80:80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse arquivo contém todos os detalhes que incluímos via linha de comando quando rodamos nosso container e ele é versionado junto com a aplicação, dessa forma qualquer pessoa que pegar o projeto consegue executar a stack toda com um único comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após executar esse comando devemos ver o mesmo resultado de quando utilizamos o comando anterior, devemos ver a aplicação rodando no browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Com certeza padronizar uma imagem base nos seus projetos não vai resolver todos os seus problemas mas ajuda a diminuir a complexidade na implantação e evita silos de conhecimento relacionados ao setup local, espero ter contribuído com esse post, deixe sua opinião nos comentários ou me da um grito no &lt;a href="https://twitter.com/ConveniaTech" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>php</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Os cuidados para um deploy "Zero Down Time"</title>
      <dc:creator>Leonardo Lemos</dc:creator>
      <pubDate>Tue, 07 Dec 2021 21:36:15 +0000</pubDate>
      <link>https://dev.to/convenia/os-cuidados-para-um-deploy-zero-down-time-5a3</link>
      <guid>https://dev.to/convenia/os-cuidados-para-um-deploy-zero-down-time-5a3</guid>
      <description>&lt;p&gt;Atualmente existem ferramentas que nos ajudam a executar containers em produção e a maioria delas trazem funcionalidades preciosas como "Health Checks", "Limite de recursos" e até mesmo prometem um deploy &lt;a href="https://avikdas.com/2020/06/30/scalability-concepts-zero-downtime-deployments.html"&gt;"Zero Down Time"&lt;/a&gt;, o foco deste post é nesse último Item. Na &lt;a href="https://convenia.com.br/"&gt;Convenia&lt;/a&gt; utilizamos o &lt;a href="https://docs.docker.com/engine/swarm/"&gt;Docker Swarm&lt;/a&gt; para gerenciar alguns containers em produção e o Docker Swarm entrega esse tipo de deploy "Zero Down Time" através de uma simples configuração, porém após alguns testes em uma API sob stress constatamos que sempre ocorriam alguns erros no momento do deploy. Aprofundando a análise do que poderia causar esses erros, percebemos que podemos cometer alguns "equívocos" que nos impedem de ter um deploy verdadeiramente sem Down Time e ainda podemos constatar que esses "equívocos" são comuns em outras ferramentas como &lt;a href="https://kubernetes.io/pt-br/"&gt;Kubernetes&lt;/a&gt; também, dai saiu a motivação para escrever esse post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graceful ShutDown
&lt;/h2&gt;

&lt;p&gt;A grosso modo podemos definir "Graceful shutdown" como a maneira "natural" em que um processo é desligado. Muitos processos abrem sockets e trabalham com estado em memória então para esses processos desligarem naturalmente muito provavelmente eles vão fechar as conexões abertas e persistirem o estado, que está na memória, no HD para que não haja perda de dados e para poder retomarem as atividades sem maiores problemas quando forem reiniciados, em uma queda de energia por exemplo, os processos não tem esse privilégio, nesse caso podemos nos deparar com erros durante a próxima inicialização do processo, isso é conhecido como "Hard Shutdown".&lt;/p&gt;

&lt;p&gt;A boa notícia é que todos os softwares mais difundidos fazem isso por padrão, o &lt;a href="https://www.nginx.com/"&gt;nginx&lt;/a&gt; quando recebe o "sinal" de desligamento espera a resposta de todos os requests que estão em execução no momento, antes de desligar de fato. Esse "Graceful Shutdown" é importante porque o deploy consiste na "troca" de um processo com a versão antiga do software pelo mesmo processo contendo a versão nova, ao desligar o processo contendo a versão antiga, os requests que estiverem em execução não podem falhar, pois eles ainda estão sendo respondidos pelo processo antigo enquanto os novos requests já estão sendo servidos pelo processo novo como mostrado na imagem abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kti9Rd0V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ft293jsqsbem1iyh7dbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kti9Rd0V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ft293jsqsbem1iyh7dbf.png" alt="Troca dos containers em um deploy" width="741" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na &lt;a href="https://convenia.com.br"&gt;Convenia&lt;/a&gt; temos muitos listeners feitos com o &lt;a href="https://convenia.github.io/Pigeon/#/"&gt;Pigeon&lt;/a&gt;, nesse caso não estamos falando de um webserver e sim de um &lt;a href="https://convenia.github.io/Pigeon/#/EVENT_DRIVEN?id=listening-for-events"&gt;consumer&lt;/a&gt; que "ouve" uma fila do &lt;a href="https://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt;, você já deve imaginar que para "ouvir" essa fila temos que ter uma conexão aberta com o &lt;a href="https://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt; então nada mais justo que fechar as conexões no momento em que o processo for desligado, é justamente isso que o &lt;a href="https://convenia.github.io/Pigeon/#/"&gt;Pigeon&lt;/a&gt; faz no código a seguir:&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="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;listenSignals&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="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'AMQP_WITHOUT_SIGNALS'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'AMQP_WITHOUT_SIGNALS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nb"&gt;pcntl_async_signals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nb"&gt;pcntl_signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SIGTERM&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="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'signalHandler'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nb"&gt;pcntl_signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SIGINT&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="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'signalHandler'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nb"&gt;pcntl_signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SIGQUIT&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="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'signalHandler'&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;signalHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$signalNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$signalNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;SIGTERM&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;// 15 : supervisor default stop&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;quitHard&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;SIGQUIT&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;// 3  : kill -s QUIT&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;quitHard&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;SIGINT&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="c1"&gt;// 2  : ctrl+c&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;quit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;break&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;Nesse código definimos handlers para os sinais de desligamento que o processo pode receber e chamamos os métodos &lt;code&gt;quit()&lt;/code&gt; e &lt;code&gt;quitHard()&lt;/code&gt; que têm como objetivo fechar a conexão com o RabbitMQ. Até agora falamos muito sobre esses sinais que os processos podem receber de outro processos, ou até mesmo do kernel, mas caso você não estaja familiarizado ou se não souber exatamente a diferença entre eles, você pode ficar um pouco mais por dentro &lt;a href="https://www.ctl.io/developers/blog/post/gracefully-stopping-docker-containers/"&gt;nesse artigo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  PID 1
&lt;/h2&gt;

&lt;p&gt;Quando utilizamos &lt;a href="https://www.docker.com/"&gt;docker&lt;/a&gt; para executar nossa aplicação em produção e fazemos um deploy, um container com a nova versão da aplicação é iniciado,o container com a versão antiga da aplicação recebe o sinal "SIGTERM" que é um pedido formal de desligamento, caso o container demore mais que 10 segundos para  desligar ele será morto, logo o processo dentro do container tem 10 segundos para fazer o seu "Graceful Shutdown". A grande pegadinha é que dentro do container apenas o processo de ID 1 vai receber esse sinal, se dentro do container iniciarmos um outro processo antes da nossa aplicação, esse processo vai portar o id 1 e não a nossa aplicação. Agora que temos outro processo recebendo os sinais de desligamento no lugar da nossa aplicação, nunca vamos ter a oportunidade de fazer um Graceful Shutdown porque nunca saberemos o momento de desligar, por mais que esse pareça um erro bobo na verdade isso acaba acontecendo com uma certa frequência como por exemplo nos &lt;a href="https://docs.docker.com/engine/reference/builder/"&gt;Dockerfiles&lt;/a&gt; a seguir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:latest&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O Dockerfile acima está com o &lt;code&gt;ENTRYPOINT&lt;/code&gt; no formato de array, se você entrar dentro do container gerado por esse Dockerfie e executar o comando &lt;code&gt;pstree&lt;/code&gt; verá o seguinte output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_7H596n2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/du8k1y9gv6pgpl7bii58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_7H596n2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/du8k1y9gv6pgpl7bii58.png" alt="Output do comando pstree em um container corretamente configurado" width="756" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perceba que o "nginx" é o primeiro processo mais a esquerda, isso significa que atingiremos o objetivo de ter um Graceful Shutdown visto que o nginx vai receber os sinais pessoalmente e ele sabe muito bem como lidar, para tirar a dúvida podemos executar um &lt;code&gt;docker stop&lt;/code&gt; no container em execução e provavelmente veremos o container sendo desligado quase instantaneamente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:latest&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; nginx -g 'daemon off;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Mudança no dockerfile acima foi sutil, mas faz toda a diferença, com a sintaxe corrida do &lt;code&gt;ENTRYPOINT&lt;/code&gt; o comando em questão é executato pelo shell dentro do container, segue o output do &lt;code&gt;pstree&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1i-Qiwoa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7d1x9fa3ekg7ei2x0oll.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1i-Qiwoa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7d1x9fa3ekg7ei2x0oll.png" alt="output do comando pstree em um container mal configurado" width="756" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora o processo mais a esquerda é o &lt;code&gt;sh&lt;/code&gt;, ele quem receberá os sinais de desligamento e por acaso não sabe muito bem o que fazer com esses sinais, se você executar um &lt;code&gt;docker stop&lt;/code&gt; nesse container verá que demora 10 segundos para parar, dessa forma não faremos o "Graceful Shutdown" e nunca teremos um verdadeiro deploy "Zero Down Time" porque sempre que o container com a versão antiga do código morrer, vai levar os requests que estão em execução para a vala junto. Logo devemos nos assegurar que nosso processo está recebendo o sinal de desligamento corretamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vários processos no mesmo container
&lt;/h2&gt;

&lt;p&gt;É conceitualmente correto que o container contenha apenas um processo(PID 1), mas é relativamente comum aparecer a necessidade de executar mais de um processo no mesmo container. Tomando como exemplo uma aplicação &lt;a href="https://www.php.net/"&gt;PHP&lt;/a&gt;, que não é capaz de responder como uma aplicação completa(HTTP) apenas com o processo do &lt;a href="https://www.php.net/manual/pt_BR/install.fpm.php"&gt;PHP-FPM&lt;/a&gt;, pois necessita de um &lt;a href="https://www.nginx.com/resources/glossary/reverse-proxy-server/"&gt;reverse proxy&lt;/a&gt; como &lt;a href="https://www.apache.org/"&gt;apache&lt;/a&gt; ou &lt;a href="https://www.nginx.com/"&gt;nginx&lt;/a&gt; para isso, como poderíamos fazer um único container  contendo tanto nginx como php-fmp e que funcione como uma aplicação completa capaz de entender o protocolo HTTP? A própria &lt;a href="https://docs.docker.com/config/containers/multi-service_container/"&gt;documentação do docker nos traz algumas recomendações&lt;/a&gt; sendo que dentre elas a melhor seria utilizar o &lt;a href="http://supervisord.org/"&gt;supervisord&lt;/a&gt; como processo principal no container, cuidando dos outros processos. O supervisord é capaz de propagar os sinais de desligamento que recebe para os processos filhos, sendo assim tanto o nginx quanto o PHP-FPM terão a possibilidade de fazer um "Graceful Shutdown" assim que o supervisord receber o sinal SIGTERM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prioridade dos processos dentro do container
&lt;/h2&gt;

&lt;p&gt;Bom temos uma aplicação PHP sendo executada em produção e seguimos tudo que foi falado até agora, estamos executando tanto o php-fpm quanto o nginx, ambos rodando sobre o supervisord, porém por incrível que pareça estamos notando o erro &lt;code&gt;502&lt;/code&gt; durante o deploy. Isso acontece porque durante o deploy um novo container é criado e o supervisord simplesmente não sabe qual processo deve iniciar primeiro, se por acaso o nginx estiver pronto para receber um request, mas o php-fpm ainda não foi corretamente iniciado então temos um &lt;code&gt;502&lt;/code&gt;. Resolver esse problema de prioridade entre os processos é relativamente simples, o supervisord tem uma flag priority que tem o proposito de dizer quem é o processo de maior prioridade, entre outras palavras esse processo deve ser criado primeiro e morrer por último, a seguir segue uma configuração real de uma aplicação da &lt;a href="https://convenia.com.br"&gt;Convenia&lt;/a&gt; em produção:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[supervisord]
nodaemon=true

[program:nginx]
command = nginx -c /etc/nginx/nginx.conf  -g 'daemon off;'
user = app
autostart = true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
redirect_stderr=true

[program:php-fpm]
command = /usr/sbin/php-fpm7 -F
priority = 1
user = app
autostart = true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
redirect_stderr=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse é um arquivo de configuração do supervisord, perceba a configuração do php-fpm &lt;code&gt;priority = 1&lt;/code&gt;, essa configuração vai instruir o supervisord a criar o php-fpm primeiro e a desligar ele por último, agora sim temos um deploy perfeito, sem downtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Para alcançar um deploy perfeito não adianta simplesmente utilizar os orquestradores mais poderosos do mercado, precisamos tomar alguns cuidados com o nosso container e aplicação também:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Garantir que a aplicação é capaz de fazer um Graceful Shutdown&lt;/li&gt;
&lt;li&gt;Garantir que a aplicação está recebendo os sinais de desligamento corretamente, ou mantendo a aplicação como sendo o primeiro processo dentro do container, ou utilizando alguma ferramenta como o supervisord que propaga os sinais que ela recebe.&lt;/li&gt;
&lt;li&gt;Garantir que os processos estão sendo iniciados e desligados na ordem correta, caso o container rode mais de um processo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tomado esses cuidados estamos prontos para ter um deploy sem Downtime.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>deploy</category>
      <category>php</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Arquitetura Event Driven, quando da errado</title>
      <dc:creator>Leonardo Lemos</dc:creator>
      <pubDate>Mon, 21 Jun 2021 14:15:40 +0000</pubDate>
      <link>https://dev.to/convenia/arquitetura-event-driven-quando-da-errado-1bjf</link>
      <guid>https://dev.to/convenia/arquitetura-event-driven-quando-da-errado-1bjf</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;No &lt;a href="https://dev.to/convenia/event-driven-com-laravel-pigeon-2753"&gt;post anterior&lt;/a&gt; expliquei um pouco como funciona uma arquitetura orientada a eventos e como implementamos essa arquitetura na &lt;a href="https://convenia.com.br/" rel="noopener noreferrer"&gt;Convenia&lt;/a&gt;. Comentei um pouco sobre o nosso tratamento de erros e hoje pretendo me aprofundar mais nesse assunto.&lt;/p&gt;

&lt;p&gt;Assim como no post anterior gostaria de enfatizar que as escolhas de arquitetura e stack fazem sentido para o nosso tamanho e previsão de crescimento. Possivelmente para você não faça sentido fazer tudo da mesma forma como fazemos uma vez que cada projeto é único com as suas particularidades, mesmo assim é provável que você consiga tirar algo de bom desse post.&lt;/p&gt;

&lt;p&gt;Nesse post as palavras "mensagem" e "evento" representam a mesma coisa mas em contextos diferentes, a grosso modo "mensagem" é o nome dado a informação em transito através de um &lt;a href="https://en.wikipedia.org/wiki/Message_broker" rel="noopener noreferrer"&gt;message broker&lt;/a&gt; e evento é o nome dado para a mensagem em um contexto "orientado a eventos", "listener" é o nome dado ao processo responsável por "ouvir" eventos. &lt;/p&gt;

&lt;h2&gt;
  
  
  Como os serviços se comunicam?
&lt;/h2&gt;

&lt;p&gt;A seguir vamos analisar um exemplo simples utilizando o &lt;a href="https://convenia.github.io/Pigeon/#/" rel="noopener noreferrer"&gt;Pigeon&lt;/a&gt; bem parecido com o do post anterior:&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="nc"&gt;Pigeon&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'employee.created'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Scooby Doo'&lt;/span&gt; 
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No exemplo acima estamos emitindo o evento &lt;code&gt;employee.created&lt;/code&gt; que tem como body o nome do colaborador, para ouvir esse evento em outro serviço com o Pigeon temos esse código:&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="nc"&gt;Pigeon&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'employee.created'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ResolverContract&lt;/span&gt; &lt;span class="nv"&gt;$resolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;//doing nice things&lt;/span&gt;

            &lt;span class="nv"&gt;$resolver&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;ack&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$resolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;//send to sentry&lt;/span&gt;

            &lt;span class="nv"&gt;$resolver&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;consume&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima faz algumas coisas&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configura o Pigeon para ouvir o evento &lt;code&gt;employee.created&lt;/code&gt; em outro serviço, com a chamada &lt;code&gt;Pigeon::events('employee.created')&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Define um callback para "lidar" com o evento passando uma &lt;a href="https://www.php.net/manual/en/class.closure.php" rel="noopener noreferrer"&gt;Closure&lt;/a&gt; através do método &lt;code&gt;-&amp;gt;callback()&lt;/code&gt;, essa Closure será executada cada vez que o evento &lt;code&gt;employee.created&lt;/code&gt; for "ouvido".&lt;/li&gt;
&lt;li&gt;Define um fallback através do método &lt;code&gt;-&amp;gt;fallback()&lt;/code&gt;, essa closure será executada sempre que acontecer uma exception dentro do callback.&lt;/li&gt;
&lt;li&gt;O método &lt;code&gt;-&amp;gt;consume()&lt;/code&gt; começa a consumir a fila de fato.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O Pigeon utiliza &lt;a href="https://www.rabbitmq.com/" rel="noopener noreferrer"&gt;RabbitMQ&lt;/a&gt; para intermediar a comunicação entre os dois serviços, se tentarmos mostrar isso em um diagrama teremos o seguinte:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fenp81dxveq6e8ryrhogi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fenp81dxveq6e8ryrhogi.png" alt="diagrama de evento comsumido com sucesso"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No momento em que o serviço lê a mensagem do RabbitMQ a mensagem fica em um estado "unacked", isso significa que o Rabbit está esperando a confirmação dessa mensagem por parte de quem a leu. O RabbitMQ não entregará essa mensagem a mais ninguem até receber a confirmação de que algo deu certo ou a confirmação de que algo deu errado(rejeição) com a mensagem. No passo 4 retratado no diagrama acima fazemos uma confirmação na mensagem para o RabbitMQ saber que ela foi processada corretamente, apenas depois de receber essa confirmação o RabbitMQ remove a mensagem da fila.&lt;/p&gt;

&lt;p&gt;O diagrama acima mostra um caminho muito feliz mas vamos imaginar que logo após ler a mensagem, o serviço ouvinte "morre" devido a algum problema de hardware antes de confirmar a mensagem:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fvy0kfc9l5s1o45agjdnu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fvy0kfc9l5s1o45agjdnu.png" alt="diagrama mostrando erro fatal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;De forma similar ao exemplo anterior, após a leitura da mensagem o Rabbit coloca a mensagem no estado "unacked", ao processar a mensagem um erro muito inesperado acontece e o Listener(processo) morre no passo 3, nesse momento o Rabbit sabe que deve voltar a mensagem para o estado de "ready", assim outro listener pode tentar processar essa mesma mensagem, isso é possível porque os listeners mantém uma conexão aberta com o Rabbit e quando o processo do listener morre a conexão com o Rabbit é "cortada", nesse momento ele sabe que deve "liberar" todas as mensagens que aquele listener leu mas não confirmou.&lt;/p&gt;

&lt;p&gt;Até o momento mostramos fluxos saudáveis e falhas de terceiros que podem acontecer esporadicamente mas e quando nosso próprio código que consome a mensagem está quebrado? E quando a própria mensagem está quebrada?&lt;/p&gt;

&lt;h2&gt;
  
  
  Dead Letter Exchange para o Resgate
&lt;/h2&gt;

&lt;p&gt;Vamos tentar imaginar a seguinte situação onde o código do nosso próprio listener está quebrado:&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="nc"&gt;Pigeon&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'employee.created'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ResolverContract&lt;/span&gt; &lt;span class="nv"&gt;$resolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;doesNotExists&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nv"&gt;$resolver&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;ack&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;consume&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No código acima repare a chamada para a função &lt;code&gt;doesNotExists()&lt;/code&gt;, como o próprio nome já diz essa função não existe e quando esse listener tentar consumir uma mensagem ele vai entrar no fluxo retratado na imagem 2. O grande problema é que normalmente utilizamos algum recurso  como o &lt;a href="http://supervisord.org/" rel="noopener noreferrer"&gt;supervisord&lt;/a&gt; para "reviver" os processos que morrem e quando esse listener "voltar a vida" ele vai entrar no fluxo da imagem 2 novamente entrando em um looping.&lt;/p&gt;

&lt;p&gt;Temos uma mensagem sendo consumida em looping, ela será consumida corretamente apenas se o código do listener for corrigido, isso causa vários problemas como mostrado no &lt;a href="https://dev.to/convenia/event-driven-com-laravel-pigeon-2753"&gt;post anterior&lt;/a&gt;. O RabbitMQ, não por acaso, tem um recurso chamado &lt;a href="https://www.rabbitmq.com/dlx.html" rel="noopener noreferrer"&gt;dead letter exchange&lt;/a&gt; que serve para esse tipo de situação e com esse recurso a mensagem pode ser enviada em uma exchange separada para ser tratada posteriormente, como mostra o fluxo a seguir:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fvvyzooaniyh4rmofcane.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fvvyzooaniyh4rmofcane.png" alt="diagrama mostrando fluxo de um erro previsível"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para esse fluxo acontecer precisamos "rejeitar" explicitamente a mensagem através do método &lt;code&gt;fallback&lt;/code&gt; mostrado no inicio do artigo e retratado novamente a seguir:&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$resolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//send to sentry&lt;/span&gt;

    &lt;span class="nv"&gt;$resolver&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;No código acima a chamada &lt;code&gt;$resolver-&amp;gt;reject(false);&lt;/code&gt; é a chamada que rejeita explicitamente a mensagem, caso você não defina um fallback, o Pigeon tem um fallback padrão que rejeitará a mensagem caso a env &lt;code&gt;PIGEON_ON_FAILURE&lt;/code&gt; esteja presente com o valor &lt;code&gt;reject&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  LetterThief
&lt;/h2&gt;

&lt;p&gt;Após rejeitar a mensagem, ela irá para uma dead letter exchange de onde podemos armazenar essa mensagem problemática em uma fila, avaliar mais tarde ou dar um tratamento digno para ela ali mesmo. No caso da &lt;a href="//convenia.com.br"&gt;Convenia&lt;/a&gt; desenvolvemos um serviço chamado "LetterThief" que é responsável por gerenciar as mensagens que foram rejeitadas e avisar o time quando ocorre uma rejeição:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fn9mroe7jjke7gvdr2yh9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fn9mroe7jjke7gvdr2yh9.png" alt="listagem de mensagens rejeitadas"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Acima está retratada a listagem das mensagens rejeitadas, nesse serviço conseguimos filtrar as mensagens pela suas propriedades, Toda rejeição ocorre em uma fila e exchange específicas e em um certo momento. Os filtros são capazes de trazer rejeições que ocorreram em uma determinada fila ou em um determinado momento.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F8fajcrbdbyot6ahqf7k3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F8fajcrbdbyot6ahqf7k3.png" alt="página de detalhes da mensagem rejeitada"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ná página de detalhes da mensagem rejeitada temos todas as informações da mensagem, sabemos de que serviço ela veio, sabemos qual foi o listener que a rejeitou e o mais importante, temos o &lt;code&gt;correlation_id&lt;/code&gt; que será utilizado para confrontar o erro com as exceptions que cairem no &lt;a href="https://sentry.io" rel="noopener noreferrer"&gt;sentry&lt;/a&gt;. Com essas informações sabemos exatamente o porque uma mensagem foi rejeitada e o envio para o sentry é feito pelo listener logo antes de rejeitar a mensagem:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fr1lkiqj224y43bx97o0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fr1lkiqj224y43bx97o0d.png" alt="mensagem de erro no sentry"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fpul5vsye1pc5swp0t3yj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpul5vsye1pc5swp0t3yj.png" alt="Tags no sentry"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As imagens acima mostram um erro real que ocorreu no ambiente de produção e a parte de tags contendo o &lt;code&gt;correlation_id&lt;/code&gt;, infelizmente não posso mostrar a exception com mais detalhes para não expor dados sensíveis :/&lt;/p&gt;

&lt;p&gt;Legal, o serviço traz bastante visibilidade para os erros que ocorreram mas como os desenvolvedores são avisados sobre o ocorrido? O LetterThief tem uma integração com o slack assim toda a equipe é notificada quando um erro ocorre e pode agir imediatamente para resolver o problema.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F763qcx7uuf9kd7hkr64r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F763qcx7uuf9kd7hkr64r.png" alt="notificação no slack enviada pelo letter thief"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na imagem acima vemos a notificação que chega no slack, ela contém a fila onde ocorreu o problema e o link para a mensagem no serviço do LetterThief, dessa forma o desenvolvedor responsável já sabe que deve corrigir o problema o quanto antes.&lt;/p&gt;

&lt;p&gt;Você deve estar se perguntando o que acontece com a mensagem após a correção do problema já que muito provavelmente ela deveria causar algum efeito no sistema mas acabou não causando devido ao erro ocorrido, após resolver o erro o desenvolvedor tem a capacidade de reenviar a mensagem através do LetterThief.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F77ir3me8wi8jztgg1e8o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F77ir3me8wi8jztgg1e8o.png" alt="modal de confirmação para reenviar a mensagem"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na imagem acima é possível ver o botão "TRY MESSAGE AGAIN" que resulta nessa confirmação que está sendo exibida, após a confirmação, a mensagem será reenviada diretamente para a fila de onde o erro foi causado, dessa forma o processamento deve ocorrer normalmente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cuidados adicionais
&lt;/h2&gt;

&lt;p&gt;Você deve estar se deparando com algumas questões após ter chego até aqui, a verdade é que para tudo isso funcionar corretamente temos que ter alguns cuidados que são garantidos em um fluxo rígido de &lt;a href="https://www.youtube.com/watch?v=_7W9pqWPyfc" rel="noopener noreferrer"&gt;code review&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Os listeners devem ser &lt;a href="https://developer.mozilla.org/pt-BR/docs/Glossary/Idempotent" rel="noopener noreferrer"&gt;idempotentes&lt;/a&gt;, como pode ocorrer um erro durante o processamento, no caso de uma criação no banco de dados por exemplo, não podem haver registros duplicados, devemos fazer a opção por uma função de &lt;code&gt;upsert&lt;/code&gt; ao invés de um &lt;code&gt;create&lt;/code&gt;, isso vai evitar que dois registros sejam criados quando a mensagem for reenviada, lembrando que a mensagem pode ser reenviada mais de uma vez.&lt;/li&gt;
&lt;li&gt;O listener deve obrigatoriamente enviar a mensagem para o sentry e logo em seguida rejeitar a mensagem no fallback, muita lógica não é bem vinda aqui pois não podem haver falhas dentro do fallback, isso causaria a devolução da mansagem para a fila e o problema de reprocessamento infinito apresentado no inicio do post.&lt;/li&gt;
&lt;li&gt;Devemos ser cuidadosos ao avaliar datas dentro do listener, não devemos nunca avaliar o momento em que a mensagem chega no listener, sempre devemos avaliar a data em que o evento foi emitido, isso chega obrigatoriamente com todo o evento, dessa forma evitamos de processar uma data errada devido ao delay da mensagem.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Toda arquitetura distribuída tem uma complexidade mais elevada, observabilidade e tratamento de erros são pautas de muitas talks e donos da preocupação de várias equipes. Sem dúvidas em uma arquitetura orientada a eventos precisamos de uma forma de lidar com erros no processamento de mensagens assíncronas. No caso específico da Convenia a melhor saída foi fazer nosso próprio serviço que atende exatamente ao que precisamos. Existem outras opções de message broker como &lt;a href="https://kafka.apache.org/" rel="noopener noreferrer"&gt;kafka&lt;/a&gt; que podem trazer soluções já prontas para esse problema, o que te economizará o trabalho de desenvolver e manter a solução, de qualquer forma é muito importante ter uma solução similar a essa para auxiliar a equipe no dia a dia.&lt;/p&gt;

&lt;p&gt;O LetterThief foi desenvolvido com a premissa da segurança em relação a perda de mensagens, se reparar cuidadosamente vai ver que utilizando o LetterThief é impossível perder uma mensagem no meio do caminho, ou ela foi processada corretamente ou foi parar no LetterThief, caso o desenvolvedor do listener tenha sido muito transgressor ao implementar o listener, a mensagem voltará para a fila, independente da opção que adotarmos acho essa premissa de "nunca perder a mensagem" importante para se levar em consideração.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>microservices</category>
      <category>pigeon</category>
    </item>
    <item>
      <title>Laravel &amp; MongoDB na Convenia</title>
      <dc:creator>Leonardo Lemos</dc:creator>
      <pubDate>Thu, 18 Mar 2021 02:03:31 +0000</pubDate>
      <link>https://dev.to/convenia/laravel-mongodb-na-convenia-3g5e</link>
      <guid>https://dev.to/convenia/laravel-mongodb-na-convenia-3g5e</guid>
      <description>&lt;p&gt;Olá pessoal nesse post vou falar um pouco de como utilizamos &lt;a href="https://www.mongodb.com" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt; com o &lt;a href="https://laravel.com/" rel="noopener noreferrer"&gt;Laravel&lt;/a&gt; na &lt;a href="https://convenia.com.br/" rel="noopener noreferrer"&gt;Convenia&lt;/a&gt; e quais vantagens que ele nos trouxe com documentos embedados.&lt;/p&gt;

&lt;p&gt;Como desenvolvedores estamos muito familiarizados com o modelo Relacional, por isso é comum cometermos o erro de modelar qualquer tipo de banco da mesma forma que modelamos um relacional. Para enfatizar a diferença na modelagem vamos tentar imaginar uma modelagem relacional para a tela mostrada na imagem seguinte, em seguida vamos tentar reproduzir a mesma modelagem com o MongoDB:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ftojzihor8wyw9hjfksa6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftojzihor8wyw9hjfksa6.png" alt="Imagem da tela do colaborador"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na imagem acima temos 2 seções, o card a esquerda contendo informações gerais do colaborador e o card na direita contendo "Endereço", de uma forma mais clássica modelariamos isso em 2 tabelas, sempre tendo em mente evitar qualquer tipo de duplicidade de informação:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fqixrxa2tpfriotnv4u4k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fqixrxa2tpfriotnv4u4k.png" alt="Imagem da modelagem de tabelas no MySql"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do lado da aplicação precisaremos de 2 &lt;a href="https://laravel.com/docs/4.2/eloquent" rel="noopener noreferrer"&gt;models&lt;/a&gt; para "dar vida" a essas 2 tabelas, a model do Laravel é uma implementação de &lt;a href="https://www.martinfowler.com/eaaCatalog/activeRecord.html" rel="noopener noreferrer"&gt;active record&lt;/a&gt;, nesse padrão geralmente mapeamos uma tabela por model, essa model fará o papel da nossa entidade e também tem a responsabilidade de acessar o banco de dados.&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;//app/Models/Colaborador.php&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Colaborador&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&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;HasFactory&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;enderecos&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;Endereco&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'id_colaborador'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;//app/Models/Endereco.php&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Endereco&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&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;HasFactory&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;No nosso caso a Model principal é a model de &lt;code&gt;Colaborador&lt;/code&gt;, ela mantém uma ligação com a model &lt;code&gt;Endereco&lt;/code&gt; através do método &lt;code&gt;enderecos&lt;/code&gt; que por si é um relacionamento &lt;code&gt;HasMany&lt;/code&gt; (Colaborador possui vários Endereços), sempre que quisermos trazer o colaborador junto com o endereço temos 2 opções:&lt;/p&gt;

&lt;p&gt;Podemos fazer um &lt;a href="https://martinfowler.com/eaaCatalog/lazyLoad.html" rel="noopener noreferrer"&gt;Lazy Loading&lt;/a&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="nc"&gt;Colaborador&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;find&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;enderecos&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cep&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima resultará em 2 consultas: A primeira trazendo o colaborador e a segunda trazendo o endereço, a segunda por sua vez é executada no momento da chamada do relacionamento &lt;code&gt;enderecos&lt;/code&gt; (representado pelo -&amp;gt;enderecos). A estrutura da consulta ficará da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt;
  &lt;span class="nv"&gt;"colaboradors"&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
  &lt;span class="nv"&gt;"colaboradors"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;limit&lt;/span&gt;
  &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt;
  &lt;span class="nv"&gt;"enderecos"&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
  &lt;span class="nv"&gt;"enderecos"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"id_colaborador"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="nv"&gt;"enderecos"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"id_colaborador"&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como você pode ver 2 consultas não é grande problema, mas se tivessemos uma coleçao de colaboradores teriamos o famoso problema de N+1 consultas, então com 10 colaboradores teriámos uma consulta para trazer os colaboradores e em seguida 10 consultas, para trazer os endereços de cada colaborador um a um, para resolver esse problema podemos fazer um &lt;a href="https://laravel.com/docs/4.2/eloquent#eager-loading" rel="noopener noreferrer"&gt;Eager loading&lt;/a&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="nc"&gt;Colaborador&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'enderecos'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora estamos trazendo todos os colaboradores com endereço, a chamada ao método &lt;code&gt;with&lt;/code&gt; instrui o ORM a trazer os endereços de todos os colaboradores em uma única consulta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt;
  &lt;span class="nv"&gt;"colaboradors"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt;
  &lt;span class="nv"&gt;"enderecos"&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
  &lt;span class="nv"&gt;"enderecos"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"id_colaborador"&lt;/span&gt; &lt;span class="k"&gt;in&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perceba que o resultado da consulta de colaboradores é utilizado na consulta de endereços, e o próprio Eloquent sabe como combinar os dados de endereço com os dados de colaboradores por baixo dos panos.&lt;/p&gt;

&lt;p&gt;Com a explicação acima podemos chegar a conclusão de que a consulta acima não é tão problemática mas vamos pensar que o colaborador na vida real tem muito mais relacionamentos, apenas como exercício tente estruturar os relacionamentos seguintes na sua cabeça: &lt;code&gt;absences&lt;/code&gt;, &lt;code&gt;contactInformation&lt;/code&gt;, &lt;code&gt;dependents&lt;/code&gt;, &lt;code&gt;documents&lt;/code&gt;, perceba que em um cenário real o número de queries separadas que precisamos fazer pode nos trazer um problema de performance, esse exemplo é um exemplo real, é um pedacinho da nossa entidade de colaborador, não seria exagero falar em dezenas de relacionamentos.&lt;/p&gt;

&lt;p&gt;Agora que já vimos um exemplo de modelagem no MySQL (relacional), como ficaria esse mesmo exemplo no MongoDB e o quais seriam as vantagens?&lt;/p&gt;

&lt;p&gt;O MongoDB é um banco de dados baseado em documentos, descrevemos o que é armazenado no formato JSON, e diferente do MySql ele tem um schema flexível, isso significa que você pode mudar a estrutura do documento de acordo com a necessidade, sem se preocupar em criar migrações.&lt;/p&gt;

&lt;p&gt;O pacote mais difundido para se trabalhar com o mongo no Laravel é o &lt;a href="https://github.com/jenssegers/laravel-mongodb" rel="noopener noreferrer"&gt;Laravel MongoDB&lt;/a&gt;, ele "imita" o eloquent garantindo a mesma interface, e nos traz bastantes possibilidades, veja como ficaria a mesma modelagem em uma única coleção no mongo:&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="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;db.colaboradors.find().pretty()&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;"_id"&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;ObjectId(&lt;/span&gt;&lt;span class="s2"&gt;"604c12d75c9f4250b731d662"&lt;/span&gt;&lt;span class="err"&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;"name"&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="s2"&gt;"Miss Amelia Schuppe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cargo"&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="s2"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"departamento"&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="s2"&gt;"aut"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"salario"&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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data_admissao"&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="s2"&gt;"2021-03-13"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"endereco"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"cep"&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="s2"&gt;"15014140"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"endereco"&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="s2"&gt;"125 Hammes Extensions&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;South Bernadinebury, OR 51480"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"bairro"&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="s2"&gt;"East Alf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"cidade"&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="s2"&gt;"West Tremayne"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"uf"&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="s2"&gt;"Wisconsin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"numero"&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="mi"&gt;200&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;"updated_at"&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;ISODate(&lt;/span&gt;&lt;span class="s2"&gt;"2021-03-13T01:18:15.747Z"&lt;/span&gt;&lt;span class="err"&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;"created_at"&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;ISODate(&lt;/span&gt;&lt;span class="s2"&gt;"2021-03-13T01:18:15.747Z"&lt;/span&gt;&lt;span class="err"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O MongoDB nos apresenta um paradigma de modelagem diferente, em um banco de dados relacional nos baseamos nas entidades em si e na suas relações, buscamos uma maior normalização e evitamos duplicidade, ao modelar as coleções no MongoDB fazemos a modelagem baseado na consulta, podemos inclusive ter 2 coleções diferentes de colaboradores para consultas diferentes, claro que isso nos traz alguns desafios na escrita pois teremos que manter ambas as coleções atualizadas, mas ganhamos muita performance na leitura, não é errado inclusive pensarmos em manter o mesmo dado duplicado no mesmo documento porém em formatos diferentes, uma versão "crua" do dado e outra versão formatada pronta para exibição.&lt;/p&gt;

&lt;p&gt;No documento modelado acima temos o endereço embedado na entidade colaborador, quando consultarmos o colaborador, com apenas uma consulta, recebemos o seu endereço "de graça", imagine que no exemplo real da Convenia onde temos uma série de relacionamentos do colaborador, apenas uma consulta basta para trazer o colaborador por completo, isso traz um ganho de performance bem interessante!&lt;/p&gt;

&lt;p&gt;O pacote nos traz um relacionamento especial para tratar documentos embedados, o &lt;a href="https://github.com/jenssegers/laravel-mongodb#embedsmany-relationship" rel="noopener noreferrer"&gt;embeds many&lt;/a&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Colaborador&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&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;HasFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'mongodb'&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;enderecos&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;embedsMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;Endereco&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'id_colaborador'&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;Isso permite que você mantenha a mesma modelagem de Entidades no lado da aplicação, tendo uma model específica para o endereço, na hora de armazenar o ORM vai entender que ele deve embedar (fazer o upsert do registro) o endereço dentro do documento de colaborador.&lt;/p&gt;

&lt;p&gt;Você pode estar se perguntando "mas e se eu quiser compartilhar o endereço com mais de um colaborador?", nesse caso tenha em mente que os tipos de relacionamentos convencionais (&lt;a href="https://github.com/jenssegers/laravel-mongodb#relationships" rel="noopener noreferrer"&gt;hasMany, hasOne, belongsTo&lt;/a&gt;), ainda estão disponíveis, nesse caso a modelagem fica muito parecida com o a modelagem do MySql, existem casos em que isso é bem vindo:&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="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;db.colaboradors.find().pretty()&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;"_id"&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;ObjectId(&lt;/span&gt;&lt;span class="s2"&gt;"604c1572e8a76d528004f023"&lt;/span&gt;&lt;span class="err"&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;"name"&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="s2"&gt;"Jaycee Cole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cargo"&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="s2"&gt;"deleniti"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"departamento"&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="s2"&gt;"optio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"salario"&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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data_admissao"&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="s2"&gt;"2021-03-13"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"endereco"&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="s2"&gt;"604c1572e8a76d528004f022"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&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;ISODate(&lt;/span&gt;&lt;span class="s2"&gt;"2021-03-13T01:29:22.757Z"&lt;/span&gt;&lt;span class="err"&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;"created_at"&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;ISODate(&lt;/span&gt;&lt;span class="s2"&gt;"2021-03-13T01:29:22.757Z"&lt;/span&gt;&lt;span class="err"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;db.enderecos.find().pretty()&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;"_id"&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;ObjectId(&lt;/span&gt;&lt;span class="s2"&gt;"604c1572e8a76d528004f022"&lt;/span&gt;&lt;span class="err"&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;"cep"&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="s2"&gt;"15014140"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"endereco"&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="s2"&gt;"36740 Jenkins Mountain Suite 890&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Port Rubie, UT 17199"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bairro"&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="s2"&gt;"Lennashire"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cidade"&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="s2"&gt;"Isaiahbury"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"uf"&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="s2"&gt;"California"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"numero"&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&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;ISODate(&lt;/span&gt;&lt;span class="s2"&gt;"2021-03-13T01:29:22.736Z"&lt;/span&gt;&lt;span class="err"&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;"created_at"&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;ISODate(&lt;/span&gt;&lt;span class="s2"&gt;"2021-03-13T01:29:22.736Z"&lt;/span&gt;&lt;span class="err"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como mostrado acima, peceba que é possível reproduzir no MongoDB qualquer tipo de estrutura que fariamos em um banco relacionar, porém precisamos ter em mente que o MongoDB não tem a operação de &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/join.html" rel="noopener noreferrer"&gt;&lt;code&gt;join&lt;/code&gt;&lt;/a&gt;, a operação similar no MongoDB seria o &lt;a href="https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/" rel="noopener noreferrer"&gt;lookup&lt;/a&gt; mas utilizando o "Laravel MongoDB" presisamos fazer uma &lt;a href="https://github.com/jenssegers/laravel-mongodb#mongodb-specific-operations" rel="noopener noreferrer"&gt;query raw&lt;/a&gt;, que fica bem desajeitada apesar de cumprir perfeitamente o seu papel.&lt;/p&gt;

&lt;p&gt;Muito bem, mas agora como eu decido qual tipo de relacionamento devo utilizar?&lt;/p&gt;

&lt;h2&gt;
  
  
  O protagonismo da Entidade
&lt;/h2&gt;

&lt;p&gt;Bem não existe uma receita perfeita para decidir o que devemos embedar ou não, mas temos sinais que devemos avaliar para tomar essa decisão, todos esses sinais vão determinar o protagonismo da entidade no seu sistema, vamos avaliar a entidade de endereço como exemplo.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Precisamos compartilhar aquele endereço entre vários colaboradores? Caso não precise compartilhar o endereço, é um bom sinal de que o correto seria embedar esse dado, se precisarmos compartilhar o endereço pode ser mais inteligente definir uma coleção de endereços pois evita o esforço de atualizar vários colaboradores ao atualizar um único endereço.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Onde precisamos exibir o endereço, sempre presicamos exibir a entidade no qual ele pertence(colaborador)? Aqui você pode argumentar que estamos deixando o layout guiar a modelagem, mas de fato o layout diz muito sobre a importância de uma certa Entidade no sistema, se o endereço sempre é exibido junto com o colaborador devemos suspeitar de que o endereço não tem o protagonismo necessário para compor sua própria coleção.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As rotas(REST) em que o endereço aparecem, são rotas do próprio endereço ou são rotas do colaborador? Se a informação do endereço forem exibidas somente em rotas do colaborador ou em uma rota filha do colaborador(&lt;a href="https://laravel.com/docs/5.1/controllers#restful-nested-resources" rel="noopener noreferrer"&gt;nested resource&lt;/a&gt;) então devemos entender isso como um sinal de que o endereço deve ser embedado no colaborador.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Existem muitos procedimentos que levam em consideração apenas o endereço? Vamos entender a busca como um procedimento, em geral não é comum buscarmos colaboradores por endereço, apenas por dados como nome, departamento, cargo, isso também sinaliza o baixo protagonismo da model de Endereço.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Existe muito acesso ao colaborador apenas para ver o Endereço?  É possível que todas as afirmações acima te levem a entender que você deve embedar o endereço porém é possível que as pessoas utilizem muito o endereço por algum motivo(envio de correspondência talvez), nesse caso pode ser necessário dar ao endereço sua própria coleção, isso vai impedir as leituras de endereço de concorrerem com as leituras de colaborador, claro que para isso funcionar a tela de endereço deve ser remodelada para não exibir outros dados, óbvio também que o designer do time não vai gostar disso né, mas aí é ver se vale ou não a pena.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Se você confrontar suas entidades com a lista acima vai ver que na maioria dos casos as entidades que devem ser embedadas são o que chamamos de "entidades fracas", são entidades que não tem o porquê de existir sem uma "entidade forte" na qual ela pertence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bancos relacionais e campos json
&lt;/h2&gt;

&lt;p&gt;Quase todos os bancos relacionais modernos trazem algum tipo de solução de schema flexível, eu estaria sendo muito parcial se não comentasse sobre isso, o MySQL por exemplo, tem &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/json.html" rel="noopener noreferrer"&gt;campos json&lt;/a&gt; que nos permitem os mesmos resultados citados acima, bem como a mesma análise de "protagonismo da entidade", isso conversaria muito bem com o Laravel e seu recurso de &lt;a href="https://laravel.com/docs/8.x/eloquent-mutators#custom-casts" rel="noopener noreferrer"&gt;custom casts&lt;/a&gt;, sem dúvida você deve levar essa possibilidade em consideração, no nosso caso acabamos fazendo a opção pelo MongoDB por outras vantagens como o &lt;a href="https://docs.mongodb.com/manual/core/aggregation-pipeline/" rel="noopener noreferrer"&gt;aggregation pipeline&lt;/a&gt;, que é uma ferramenta muito poderosa para filtrar e transformar os resultados, feature essa que necessita de um único post somente para ela.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;O MongoDB é uma ferramenta muito completa e com certeza tem muito a oferecer, no entanto a maioria das aplicações são perfeitamente atendidas com o bom e velho banco relacional, se esse for o seu caso sugiro que você faça a opção pelo conhecido, pois toda nova tecnologia traz uma curva de aprendizado bem como os seus próprios desafios, com o MongoDB não é diferente, pretendo escrever um posto mostrando todos os desafios que passamos com ele, e não foram poucos :)&lt;/p&gt;

&lt;p&gt;Espero ter contrinuído de alguma forma, até a próxima!&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>laravel</category>
      <category>php</category>
    </item>
    <item>
      <title>Event Driven com Laravel Pigeon</title>
      <dc:creator>Leonardo Lemos</dc:creator>
      <pubDate>Mon, 30 Nov 2020 01:24:54 +0000</pubDate>
      <link>https://dev.to/convenia/event-driven-com-laravel-pigeon-2753</link>
      <guid>https://dev.to/convenia/event-driven-com-laravel-pigeon-2753</guid>
      <description>&lt;p&gt;Olá pessoal, &lt;a href="https://martinfowler.com/articles/201701-event-driven.html" rel="noopener noreferrer"&gt;event driven&lt;/a&gt; é uma arquitetura muito difundida em microserviços por promover desacoplamento entre os diferentes serviços, na &lt;a href="https://convenia.com.br/" rel="noopener noreferrer"&gt;Convenia&lt;/a&gt; fizemos a opção por essa arquitetura e nesse artigo gostaria de expor um pouco do que fizemos e como fizemos.&lt;/p&gt;

&lt;p&gt;Primeiramente gostaria de dizer que as escolhas que fizemos foram levando em consideração nossa escala e previsão de crescimento, essa stack pode não ser a ideal para você e por se tratar de um tema bem amplo e sem uma definição formal, tomamos algumas iniciativas que fazem muito sentido para nós, mas podem não se encaixar bem no seu case, apesar disso é provável que o texto a seguir seja construtivo caso você esteja pensando em fazer algo assíncrono a desacoplado.&lt;/p&gt;

&lt;h3&gt;
  
  
  Como funciona uma arquitetura guiada por eventos?
&lt;/h3&gt;

&lt;p&gt;Todo framework maduro atualmente vem com algum tipo de &lt;a href="https://www.npmjs.com/package/js-event-bus" rel="noopener noreferrer"&gt;event bus&lt;/a&gt;, se você está familiarizado com esse conceito  basta pensar nisso de uma forma mais ampla, ao invés de haver uma classe que dispara um evento e N classes de Listeners que ouvem esse evento, teriamos um serviço emissor e N serviços "ouvintes", caso isso não tenha ajudado na compreensão vou explicar um pouco melhor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbpfxingvrzyvxcesu1zm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbpfxingvrzyvxcesu1zm.png" alt="diagrama de exemplo de evento"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na imagem acima temos alguns elementos importantes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Serviço emissor(Serviço de pedidos): é o serviço de onde o estimulo é originado, ele é responsável apenas por fechar o pedido e ao finalizar ele grita: "Pedido Finalizado"! Dessa forma todos os outros serviços que se importam por esse "estimulo" podem reagir a ele. O serviço emissor não conhece os serviços que "se importam" pela mensagem dele, a atuação dele acaba integralmente após a emissão da mensagem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Serviços Ouvintes (representados à direita da figura): Os três serviços se importam pela mensagem emitida pelo serviço de pedidos mas não conhecem o serviço de pedidos em si, dai para frente eles podem fazer o que quiserem com a mensagem emitida sem influenciar o serviço emissor ou mesmo sem influenciar outros ouvintes, muito diferente do que aconteceria em uma abordagem procedural.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Message broker(a peça central): Esse elemento é o responsável por promover o desacoplamento temporal entre o emissor e os ouvintes, isso significa que a mensagem é emitidas quando o emissor acha conveniente e sem que ele se importe se os ouvintes vão poder ouvir a mensagem naquele momento, os ouvintes podem ouvir a mensagem quando eles quiserem, se caso um ouvinte está offline ou mesmo "quebrado", o message broker vai "segurar" a mensagem até que aquele ouvinte esteja apto a recebe lá.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Como isso tudo se encaixa no ecossistema da Convenia?
&lt;/h3&gt;

&lt;p&gt;Ao escolher as peças da stack procuramos manter o mínimo de complexidade possível para que a stack não nos onere e para que o desenvolvimento permaneça simples, fizemos a opção pelo &lt;a href="https://www.rabbitmq.com/" rel="noopener noreferrer"&gt;rabbitmq&lt;/a&gt; pela simplicidade de uso e setup e por ser uma opção robusta o bastante.&lt;/p&gt;

&lt;p&gt;Do lado das aplicações utilizamos &lt;a href="https://www.php.net/" rel="noopener noreferrer"&gt;PHP&lt;/a&gt; com o framework &lt;a href="https://laravel.com/" rel="noopener noreferrer"&gt;Laravel&lt;/a&gt; e isso nos trouxe bastante agilidade no desenlvolvimento das aplicações, porém quase não existem soluções para fazer todos os serviços funcionarem como uma unidade, então fizemos muita coisa por conta própria.&lt;/p&gt;

&lt;p&gt;Logo no inicio começamos a fazer a comunicação entre os serviços com a lib mais difundida de php, a amqplib, que apesar de cumprir o seu papel e ser performática o bastante, parece desajeitada e é praticamente imockavel(intestável), além de nos obrigar a escrever um boilerplate deselegante, para resolver esse problema escrevemos o &lt;a href="https://convenia.github.io/Pigeon/#/" rel="noopener noreferrer"&gt;Pigeon&lt;/a&gt;(sugiro a leitura da documentação), ele envelopa a amqplib nos dando a possibilidade de testar as emissões e escrever um código verdadeiramente elegante, o Pigeon também nos permite tratar o rabbitmq como descartável, ao invés de manter um arquivo de definição de filas versionado, o Pigeon é capaz de criar as filas on the fly e já fazer os binds corretamente, se algo der errado apenas subimos outra instancia do rabbit e tudo irá funcionar.&lt;/p&gt;

&lt;p&gt;Aqui está um exemplo de emissão de um evento com o Pigeon:&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="nc"&gt;Pigeon&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sample.event'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'scooby'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'doo'&lt;/span&gt; 
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para ouvir o evento acima poderíamos fazer o seguinte código:&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="nc"&gt;Pigeon&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sample.event'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Closure&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromCallable&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'httpCallback'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Closure&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromCallable&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'httpFallback'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;consume&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No exemplo acima definimos uma &lt;a href="https://www.php.net/manual/en/class.closure" rel="noopener noreferrer"&gt;Closure&lt;/a&gt; para um callback(quando tudo ocorre bem) e outra para um fallback(quando algo da errado), o Pigeon é bem completo e pretendo entrar em mais detalhes em um outro post, mas por hora acredito que isso demonstra bem como ocorrem a comunicação entre os serviços.&lt;/p&gt;

&lt;h3&gt;
  
  
  Como projetar um Listener?
&lt;/h3&gt;

&lt;p&gt;O listener é a classe que vai conter o código que ouve os eventos(código apresentado no item anterior), esse código abre um soquet com o rabbitmq e fica esperando por eventos, isso significa que o processo do listener nunca vai "morrer", por isso precisamos tomar alguns cuidados especiais pois por padrão o programador PHP está acostumado com o ciclo de vida do request, que é bem efêmero e isso nos permite fazer algumas transgressões que quando cometidas em um Listener pode dar muita dor de cabeça, seguem alguns cuidados necessários.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Execute o listener sempre sobre o &lt;a href="http://supervisord.org/" rel="noopener noreferrer"&gt;supervisor&lt;/a&gt;: Isso não é só aconselhado para programas escritos em PHP, o &lt;a href="https://www.vaultproject.io/" rel="noopener noreferrer"&gt;vault&lt;/a&gt; é uma ferramenta para gerenciar segredos escrita em &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;go&lt;/a&gt; e é aconselhável rodar ele sobre o supervisor também, isso porque o supervisor pode reviver o seu processo caso ele morra, sem ele seu listener morreria e você precisaria fazer um processo manual para revive-lo e com listener morto o serviço está "surdo". &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure corretamente o supervisor: é importante dar atenção para as configurações do supervisor relacionadas a tentativas, em casos extremos o supervisor fica tentando reiniciar o processo que está quebrado infinitamente causando alto consumo de CPU, em setups mais simples onde o webserver fica na mesma instancia do Listener isso seria catastrófico pois o próprio webserver seria impactado, pense que se você utiliza algum serviço para gerenciar exceções como o &lt;a href="https://sentry.io/welcome/" rel="noopener noreferrer"&gt;sentry&lt;/a&gt;, normalmente existe uma cota, você pode estourar essa cota com as infinitas tentativas do supervisor e perder visibilidade.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Separe as instancias de listeners das instancias de webserver: para evitar a situação do item anterior seria uma boa prática ter instancias de "Worker", que rodam apenas esse listener, essas instancias tendem a ser mais simples também pois não precisam ser acessadas via HTTP, em clouds como a &lt;a href="https://aws.amazon.com/pt/" rel="noopener noreferrer"&gt;aws&lt;/a&gt; isso economizaria o custo e esforço de setup com &lt;a href="https://aws.amazon.com/pt/elasticloadbalancing" rel="noopener noreferrer"&gt;load balancer&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Evite desperdiçar recursos: Agora o processo se mantêm vivo por muito tempo então devemos tomar o cuidado de fechar as conexões que abrimos e não fazer nem um tipo de procedimento que pode se acumular durando o consumos das mensagens do rabbitmq, suponha que você copie uma imagem para redimensiona-la e esquece de apagar essa imagem no final do processo, logo logo você ficará sem armazenamento, o mesmo é muito comum de acontecer com uso de memória.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://laravel.com/docs/8.x/artisan#writing-commands" rel="noopener noreferrer"&gt;Comandos do artisan&lt;/a&gt;: Comandos do artisan são feitos para procedimentos pontuais, coisas efêmeras também e não para "long running tasks", apesar da transgressão conceitual eles são uma opção de estrutura para fazer um listener, dentro dele você terá acesso a todas as estruturas do Laravel, apenas tenha em mente que comandos do artisan consomem um tando de memória considerável.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Idempotência: As mensagens podem ser reenviadas, logo a mesma mensagem pode chegar no mesmo listener 2 vezes, seu listener precisa processar essa mensagem de forma Idempotente, imagine que ele crie um registro no banco com um id "auto increment", se a mensagem chegar novamente ele não pode criar um outro registro, talvez um upsert seria a saída nesse caso.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rejeite a mensagem em caso de falha: isso vamos explicar no próximo item...&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  E quando as coisas dão errado?
&lt;/h3&gt;

&lt;p&gt;Certamente em algum momento as coisas vão quebrar, o listener quebrado tenta reprocessar a mensagem o número de vezes que foi estipulado pela configuração do supervisor e depois vai morrer, nesse caso a mensagem ficará represada na fila até o listener ser corrigido, após o deploy da correção a mensagem é consumida corretamente e tudo volta ao normal.&lt;/p&gt;

&lt;p&gt;A situação acima não é a ideal pois o listener muitas vezes morre apenas para uma determinada mensagem, o rabbitmq tem uma configuração de &lt;a href="https://www.rabbitmq.com/dlx.html" rel="noopener noreferrer"&gt;dead letter exchange&lt;/a&gt; que nos permite enviar a mensagem para um local específico em caso de falha, no nosso caso rejeitamos todas as mensagem que causaram alguma falha.&lt;/p&gt;

&lt;p&gt;Agora recebemos todas as mensagens quebradas em um único lugar mas deveríamos dar mais visibilidade possibilidades para essas mensagens, então criamos o LetterThief(Ladrão de cartas), um serviço que tem como objetivo notificar toda a falha e nos dá a possibilidade de reenviar as mensagens que causaram essa falha.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fl30.space%2Fstorage%2Fconvenia-event-driven%2Fletters-list.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fl30.space%2Fstorage%2Fconvenia-event-driven%2Fletters-list.png" alt="imagem da alistagem do ladrão de cartas"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A imagem acima mostra a interface do Ladrão, com algumas ações com destaque para a ação "Try again", esse botão nos dá a possibilidade de reenviar a mensagem para o serviço de onde ela foi originada.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fl30.space%2Fstorage%2Fconvenia-event-driven%2Finternal-letter.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fl30.space%2Fstorage%2Fconvenia-event-driven%2Finternal-letter.png" alt="imagem da página interna de uma mensagem no ladrão de cartas"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ao abrir uma mensagem temos várias informações como metadados, headers e body, isso vai nos permitir reproduzir e corrigir o erro em ambiente local, somente após o deploy do fix devemos reenviar a mensagem.&lt;/p&gt;

&lt;p&gt;Somente represar erros em um lugar específico não adiante, temos que soar um alarme avisando esse erro, no nosso caso temos um canal do slack onde caem todos os erros, a figura a sseguir mostra como isso funciona:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fl30.space%2Fstorage%2Fconvenia-event-driven%2Fslack.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fl30.space%2Fstorage%2Fconvenia-event-driven%2Fslack.png" alt="imagem de notificação de mensagem quebrada no slack"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ao ver a notificação no slack sabemos qual desenvolvedor é responsável para corrigir a falha justamente pelo nome da fila, esse desenvolvedor focará imediatamente nesse fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusão
&lt;/h3&gt;

&lt;p&gt;Toda arquitetura distribuída será relativamente mais complexa que um serviço em um único repositório, mas acredito que conseguimos chegar em uma arquitetura relativamente simples e segura, se comparado com o padrão utilizado em microserviços no mercado, cada case tem uma necessidade específica então dificilmente isso funcionará completamente para você mas pode ser que você tire alguma ideia disso tudo, e principalmente, jamais distribua sua arquitetura se não for realmente necessário.&lt;/p&gt;

&lt;p&gt;Espero ter contribuído de alguma forma....&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>microservices</category>
      <category>pigeon</category>
    </item>
    <item>
      <title>Laravel, single table inheritance</title>
      <dc:creator>Leonardo Lemos</dc:creator>
      <pubDate>Mon, 03 Aug 2020 12:28:18 +0000</pubDate>
      <link>https://dev.to/convenia/laravel-single-table-inheritance-5dcn</link>
      <guid>https://dev.to/convenia/laravel-single-table-inheritance-5dcn</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Olá pessoal, nesse artigo gostaria de compartilhar uma técnica de herança que utilizamos na ferramenta de &lt;a href="https://convenia.com.br/folha-de-pagamento" rel="noopener noreferrer"&gt;folha de pagamentos da Convenia&lt;/a&gt; e que nos ajuda muito quando precisamos calcular coisas como totais e somas em tabelas.&lt;/p&gt;

&lt;p&gt;Nossa aplicação de Folha é feita em Laravel então os exemplos que vocês vão ver são feitos em Laravel porém acredito que as técnicas podem ser reproduzidas em qualquer framework ou linguagem, não posso esquecer de ressaltar que para melhor compreensão os exemplos foram bem simplificados.&lt;/p&gt;

&lt;p&gt;Implementar herança no seu design de classes é algo bem trivial, talvez uma das primeiras coisas que você deve aprender ao começar estudar orientação a objetos, mas quando essas heranças devem refletir no banco de dados (principalmente bancos de dados relacionais) logo você deve começar a perceber o quão desajeitado isso pode se tornar, isso em parte pelo fato dos bancos relacionais não terem um mecanismo de herança, normalmente o que se faz é linkar tabelas separadas que representam as classes.&lt;/p&gt;

&lt;p&gt;Tanto para contextualizar como para aprofundamento acho importante citar as técnicas mais comuns de herança, que são &lt;a href="https://martinfowler.com/eaaCatalog/concreteTableInheritance.html" rel="noopener noreferrer"&gt;Concrete Table Inheritance&lt;/a&gt;, &lt;a href="https://martinfowler.com/eaaCatalog/classTableInheritance.html" rel="noopener noreferrer"&gt;Class Table Inheritance&lt;/a&gt; e &lt;a href="https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html" rel="noopener noreferrer"&gt;Single Table Inheritance&lt;/a&gt;, esta última será a que iremos explorar nesse artigo, ambas são explicadas com bem mais propriedade no livro &lt;a href="https://martinfowler.com/books/eaa.html" rel="noopener noreferrer"&gt;Patterns of Enterprise Application Architecture&lt;/a&gt; do &lt;a href="https://martinfowler.com/aboutMe.html" rel="noopener noreferrer"&gt;Martin Fowler&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mão na massa
&lt;/h2&gt;

&lt;p&gt;Para começar vamos mostrar uma das primeiras telas no passo a passo de fechamento, a tela de seleção de colaboradores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0bgk3zaawygoja8m7srf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0bgk3zaawygoja8m7srf.png" alt="Tela de seleção"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nessa tela selecionamos quais colaboradores devem fazer parte do cálculo de fechamento, porém existe uma outra entidade que entra automáticamente no calculo do colaborador, o seu dependente, agora imagine que se esses registros fossem mantidos em tabelas separadas para pegar um simples "total de participantes" ou mesmo os "valores totais", teriamos que fazer muitos rodeios como "Joins" e até mesmo dois procedimentos separados e uma posterior soma já no lado do PHP, desajeitado certo? mas se isso fosse mantido em apenas uma tabela ajudaria bastante, dessa forma a implementação ficaria da seguinte forma:&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'participants'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Blueprint&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

            &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;unsignedInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'payroll_id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;unsignedInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'parent_id'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'inactive'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'job_description'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'department'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;timestamps&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;Esse é o código da nossa migração que gera a tabela &lt;code&gt;participants&lt;/code&gt;, esse código já nos revela algo importante, teremos a entidade mais genérica &lt;code&gt;Participant&lt;/code&gt;, além da entidade &lt;code&gt;Employee&lt;/code&gt; e &lt;code&gt;Dependent&lt;/code&gt; então o raciocínio é trazer todos os registros ao buscar com a model &lt;code&gt;Participant&lt;/code&gt; e trazer os registros específicos ao buscas com as models mais específicas.&lt;/p&gt;

&lt;p&gt;O colaborador tem todos os campos com exceção do campo &lt;code&gt;parent_id&lt;/code&gt; já o dependente tem apenas o nome e o campo &lt;code&gt;parent_id&lt;/code&gt;, importante notar que todo campo que é obrigatório apenas para uma entidade, não deve ser marcado como obrigatório no banco, isso torna o seu modelo de banco de dados um pouco mais frágil então a boa prática aqui é sempre manter a disciplina utilizando as mais altas abstrações para manipular os registros, evitar ao máximo alterar o banco diretamente, dessa forma pode ser muito interessante implementar até mesmo uma camada de serviços para as operações, na nossa versão de produção utilizamos serviços de forma opaca para cada operação que precisamos fazer, isso significa que salvo algumas exceções não chamamos a model diretamente.&lt;/p&gt;

&lt;p&gt;O campo &lt;code&gt;type&lt;/code&gt; é muito importante é ele que discrimina qual é o tipo do registro, é muito importante manter o preenchimento de campo de forma automática, para que não haja chance de esquecer em manipulações futuras.&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App&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;Illuminate\Database\Eloquent\Relations\BelongsTo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dependent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Participant&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;InheritanceLock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'participants'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$hidden&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'job_description'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'department'&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'payroll_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'parent_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&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;employee&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;BelongsTo&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="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'parent_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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A model de &lt;code&gt;Dependent&lt;/code&gt; é bem parecida com a model &lt;code&gt;Employee&lt;/code&gt; com a exceção de que ela declara o relacionamento contrário, ela pertence a um employee.&lt;/p&gt;

&lt;p&gt;Bem interessante notar também a declaração do array &lt;code&gt;$hidden&lt;/code&gt; ele omite esses valores ao retornar uma model de dependente, os campos omitidos são referentes ao colaborador e não devem ser exibidos nos registros de dependentes&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App&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;App\Scopes\InheritanceLockScope&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;trait&lt;/span&gt; &lt;span class="nc"&gt;InheritanceLock&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;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;bootInheritanceLock&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="nv"&gt;$typeSetting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;creating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$typeSetting&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;saving&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$typeSetting&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;addGlobalScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InheritanceLockScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você deve ter notado essa trait inserida nas models mais específicas, ela é responsável por preencher automáticamente o campo &lt;code&gt;type&lt;/code&gt; e aplicar automáticamente o filtro pelo type em um &lt;a href="https://laravel.com/docs/7.x/eloquent#global-scopes" rel="noopener noreferrer"&gt;Global Scope&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O ciclo de vida e operações de uma model emitem eventos no Laravel, assim é possível escutar e declarar listeners para esses eventos, esse código declara um listener para os eventos de criação que preenche o campo &lt;code&gt;type&lt;/code&gt; com &lt;code&gt;self::class&lt;/code&gt;, esse é o nome da classe que "usa" a trait.&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Scopes&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;Illuminate\Database\Eloquent\Builder&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;Illuminate\Database\Eloquent\Model&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;Illuminate\Database\Eloquent\Scope&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InheritanceLockScope&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Scope&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$type&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;$type&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;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Apply the scope to a given Eloquent query builder.
     */&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;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Builder&lt;/span&gt; &lt;span class="nv"&gt;$builder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="nv"&gt;$model&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="nv"&gt;$builder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereType&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;type&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;Essa é a classe do global scope, a função de um global scope é adicionar uma condição cada vez que acontece uma consulta, nesse caso cada vez que retornarmos os registros do banco ele deve adicionar um &lt;code&gt;WHERE type = "ModelName"&lt;/code&gt; na consulta, isso nos garante que nunca vamos trazer um dependente no meio de colaboradores e vice versa.&lt;/p&gt;

&lt;p&gt;Dessa forma nossa implementação está completa, a seguir a utilização:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmlg0da52su964na4sd8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmlg0da52su964na4sd8m.png" alt="tinker inserção de dependentes e colaboradores"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como pode ser evidenciado no tinker criamos um colaborador e um dependente em sequencia e ao buscar os registros nas models específicas apenas os registros epecíficos são selecionados&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqtm61tezcgqmx06exfuh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqtm61tezcgqmx06exfuh.png" alt="Dependentes e Colaboradores get all"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ao chamar a model mais generica conseguimos todos os registros&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa6xcwcqpa8ge7z8xwkl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa6xcwcqpa8ge7z8xwkl9.png" alt="relacionamentos"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Os relacionamentos também funcionam como esperado&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Conseguimos ver os seguintes pontos positivos na utilização dessa técnica de herança:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existe apenas uma tabela para você se preocupar.&lt;/li&gt;
&lt;li&gt;Você não precisa fazer joins e unions para retornar os registros em uma única tacada.&lt;/li&gt;
&lt;li&gt;se você precisar adicionar um campo que ja existia em um tipo de model no outro tipo, não será necessário criar uma nova migração.&lt;/li&gt;
&lt;li&gt;Somas são bem facilitadas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mas também devemos nos preocupar com os seguintes possíveis problemas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A confusão gerada ao olhar a tabela uma vez que alguns campos fazem sentido e outros não&lt;/li&gt;
&lt;li&gt;Colunas utilizadas apenas por um subtipo podem gastar espaço atoa dependendo do banco utilizado.&lt;/li&gt;
&lt;li&gt;A tabela pode acabar ficando muito grande e cheia de indexes, o que pode ser um problema de performance.&lt;/li&gt;
&lt;li&gt;E principalmente, a modelagem fica mais frágil na parte do banco de dados.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Na nossa experiência colhemos bons frutos com a utilização da técnica, cabe a você avaliar seus ganhos e implementar, ou não.&lt;/p&gt;

&lt;p&gt;Espero ter contribuido de alguma forma!!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
