<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged anti-pattern</title>
<link>https://coderlegion.com/tag/anti-pattern</link>
<description>Powered by Question2Answer</description>
<item>
<title>Acoplamento entre CDN, ALB e ECS para entrega de frontend e assets, uma experiência do que não fazer</title>
<link>https://coderlegion.com/21408/acoplamento-entre-cdn-alb-ecs-para-entrega-frontend-assets-uma-experiencia-que-nao-fazer</link>
<description>&lt;p&gt;Existe um padrão recorrente em ambientes legados que, à primeira vista, “funciona”, mas que invariavelmente trava qualquer tentativa de evolução: o acoplamento entre CDN, load balancer e computação para servir tanto aplicação, quanto arquivos estáticos. Esse modelo normalmente não nasce de uma decisão arquitetural consciente, ele emerge aos poucos, à medida que novos requisitos são encaixados em cima de soluções já existentes. O resultado final é um sistema onde ninguém consegue afirmar com segurança de onde vem um arquivo, qual camada é responsável por qual comportamento, ou o que vai quebrar ao desligar um serviço.&lt;/p&gt;
&lt;p&gt;Considere um cenário típico. Um usuário acessa o site:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -I https://example.com/static/images/banner.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A resposta vem com headers mistos:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;HTTP/2 200
server: nginx
x-cache: Miss from cloudfront
via: 1.1 cloudfront.net (CloudFront)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Já nesse ponto há um sinal claro de acoplamento: a requisição passa por uma CDN, mas quem responde é um servidor dentro de um container. Ou seja, a CDN não é a origem real dos assets; ela apenas encaminha.&lt;/p&gt;
&lt;p&gt;O fluxo real costuma ser algo assim:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;[ Client ]
     |
     v
[   CDN   ]
     |
     v
[   ALB   ]
     |
     v
[  ECS    ]
     |
     v
[  EFS    ]
     |
     v
[  ECS    ]
     |
     v
[   ALB   ]
     |
     v
[   CDN   ]
     |
     v
[ Client ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aqui está o problema estrutural: um arquivo estático que deveria ser servido diretamente por um storage otimizado depende de recursos de computação (ECS), rede interna, mount de filesystem (EFS) e health check de container. Qualquer falha em qualquer ponto desse caminho derruba a entrega de um simples &lt;code&gt;.jpg&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Esse tipo de arquitetura gera comportamentos difíceis de prever, por exemplo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Desligar o serviço de frontend não derruba os assets&lt;/li&gt;
&lt;li&gt;Desligar a CDN derruba tudo, mesmo com backend saudável&lt;/li&gt;
&lt;li&gt;Invalidar cache muda completamente o comportamento&lt;/li&gt;
&lt;li&gt;Alterar uma regra de roteamento afeta endpoints não relacionados&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Esses sintomas indicam múltiplas “fontes de verdade” competindo ao mesmo tempo.&lt;/p&gt;
&lt;p&gt;Outro exemplo comum é o uso de endpoints internos como contrato público:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;https://example.com/api/filesystem/versão/public/my-project/files/123/banner/image.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Por trás disso, existe algo como:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# pseudocódigo
@app.route(&quot;/api/filesystem/versão/public/my-project/files/&amp;lt;id&amp;gt;/&amp;lt;path&amp;gt;/&amp;lt;filename&amp;gt;&quot;)
def serve_file():
    base = &quot;/mnt/efs/project/files&quot;
    return send_from_directory(f&quot;{base}/{id}/{path}&quot;, filename)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso parece simples, mas cria um acoplamento crítico, a URL pública depende diretamente da estrutura interna de armazenamento (&lt;code&gt;/mnt/efs/...&lt;/code&gt;). Qualquer mudança nesse layout quebra consumidores externos. Além disso, você transforma seu backend em um servidor de arquivos, algo que ele não foi projetado para ser.&lt;/p&gt;
&lt;p&gt;Outro anti-pattern frequente é usar o load balancer como origem de CDN para arquivos estáticos. A configuração costuma parecer com isso:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;CDN Behavior:
/static/* → ALB

ALB Rule:
Host: example.com AND Path: /static/* → ECS Service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esse encadeamento não adiciona valor. Ele apenas aumenta latência, custo e pontos de falha. O CDN poderia servir diretamente do storage, mas está preso a um caminho indireto.&lt;/p&gt;
&lt;p&gt;A consequência prática aparece em erros intermitentes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;HTTP/2 502
x-cache: Error from cloudfront
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ou&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;HTTP/2 503
server: awselb/2.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esses erros não são bugs isolados, são sintomas de uma arquitetura onde a entrega depende do estado de múltiplos recursos.&lt;/p&gt;
&lt;p&gt;Outro problema sério é quando múltiplas distribuições de CDN coexistem com responsabilidades sobrepostas. Por exemplo:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;CDN #1 → frontend
CDN #2 → assets
CDN #3 → fallback legado
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sem isolamento claro de rotas (&lt;code&gt;/static&lt;/code&gt;, &lt;code&gt;/api&lt;/code&gt;, &lt;code&gt;/*&lt;/code&gt;), a ordem de avaliação dos behaviors passa a definir o comportamento do sistema. Isso não é arquitetura, é efeito colateral de configuração.&lt;/p&gt;
&lt;p&gt;O ponto central aqui é que arquivos estáticos não deveriam depender de computação, visto que já possuímos inúmeras soluções modernas e otimizadas, não estamos mais aqui precisando upar arquivos em sFTP com o Dreamweaver. Computação escala com CPU e memória, assets escalam com throughput e cache. Misturar esses dois mundos gera ineficiência e fragilidade.&lt;/p&gt;
&lt;p&gt;A alternativa correta é simples conceitualmente, mas exige disciplina arquitetural. Separe responsabilidades de forma rígida.&lt;/p&gt;
&lt;p&gt;O fluxo ideal para frontend:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;[ Client ] ---&amp;gt; [ Frontend Hosting ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para assets:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;[ Client ] ---&amp;gt; [ CDN ] ---&amp;gt; [ Object Storage ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;E para backend:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;[ Client ] ---&amp;gt; [ API ] ---&amp;gt; [ Compute ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nesse modelo, cada camada faz exatamente uma coisa. A CDN entrega conteúdo estático. O storage armazena arquivos. O backend processa lógica. O frontend renderiza.&lt;/p&gt;
&lt;p&gt;Se existe dependência externa de caminhos como &lt;code&gt;/static&lt;/code&gt;, você não quebra isso, você abstrai. Um exemplo simples usando rewrite:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/static/:path*',
        destination: 'https://cdn.example.com/static/:path*',
      },
    ];
  },
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para o consumidor externo, nada muda:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;https://example.com/static/image.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mas internamente, você já desacoplou completamente a origem.&lt;/p&gt;
&lt;p&gt;Outro ponto crítico é a migração de arquivos que hoje estão em filesystem acoplado. Um script simples resolve isso:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;aws s3 sync /mnt/efs/my-project/files s3://example-assets-bucket/project/files
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depois, a URL pode ser padronizada:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;https://cdn.example.com/project/files/123/banner/image.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sem passar por backend, sem depender de mount, sem latência adicional.&lt;/p&gt;
&lt;p&gt;Se for necessário manter compatibilidade com endpoints antigos como &lt;code&gt;/api/filesystem&lt;/code&gt;, a abordagem não é manter o backend servindo arquivos, mas sim redirecionar:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;@app.route(&quot;/api/filesystem/1.0/public/my-project/files/&amp;lt;path:file&amp;gt;&quot;)
def redirect_to_cdn(file):
    return redirect(f&quot;https://cdn.example.com/project/files/{file}&quot;, code=301)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso remove a responsabilidade do backend gradualmente, sem quebrar contratos existentes.&lt;/p&gt;
&lt;p&gt;O erro mais comum em ambientes como esse é tentar corrigir o problema ajustando regras de CDN ou ALB. Isso não resolve. O problema não está na configuração isolada, está na ausência de fronteiras claras entre camadas.&lt;/p&gt;
&lt;p&gt;Enquanto arquivos estáticos dependerem de containers, enquanto CDNs dependerem de load balancers para assets, e enquanto URLs públicas refletirem detalhes internos de filesystem, qualquer mudança continuará sendo arriscada.&lt;/p&gt;
&lt;p&gt;A saída exige uma mudança de modelo mental: infraestrutura não é apenas “fazer funcionar”, é garantir previsibilidade. E previsibilidade só existe quando cada componente tem uma responsabilidade única, explícita e isolada.&lt;/p&gt;
&lt;p&gt;Esse é apenas um relato de experiência que passe em migração/modernização de sistema legado.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/21408/acoplamento-entre-cdn-alb-ecs-para-entrega-frontend-assets-uma-experiencia-que-nao-fazer</guid>
<pubDate>Fri, 26 Jun 2026 11:44:47 +0000</pubDate>
</item>
</channel>
</rss>