Pular para o conteúdo

Arquitetura de uma Aplicação Distribuída

Antes de mergulhar em cada componente separadamente, vale ter uma imagem mental de como eles se encaixam. A maioria dos sistemas web em escala, de um encurtador de URL a uma rede social inteira, segue uma variação do mesmo esqueleto:

flowchart LR
    Client[Cliente] --> DNS[DNS]
    DNS --> CDN[CDN]
    CDN --> LB[Load Balancer]
    LB --> GW[API Gateway]
    GW --> S1[Microsserviço A]
    GW --> S2[Microsserviço B]
    S1 --> Cache[(Redis Cache)]
    S2 --> Cache
    S1 --> Queue[Filas / Kafka]
    S2 --> Queue
    S1 --> DB[(Banco de Dados)]
    S2 --> DB
    Queue --> Worker[Workers assíncronos]
    Worker --> DB

Lendo esse diagrama da esquerda para a direita:

  • O cliente (navegador, app mobile) faz uma requisição
  • O DNS traduz o nome de domínio (api.exemplo.com) para o endereço IP de quem vai atender a requisição, geralmente o load balancer ou a borda da CDN. É o primeiro passo de qualquer requisição, e a resposta fica em cache no cliente e nos resolvedores intermediários por um tempo (o TTL do registro), então essa tradução não acontece toda vez
  • A CDN intercepta pedidos por conteúdo estático (imagens, JS, CSS) direto de um ponto próximo do usuário, sem nem chegar ao backend
  • O Load Balancer distribui as requisições que chegam entre várias instâncias do backend, para nenhuma máquina ficar sobrecarregada sozinha
  • O API Gateway centraliza autenticação, roteamento e outras preocupações transversais antes de encaminhar a requisição para o microsserviço certo
  • Os microsserviços contêm a lógica de negócio. Dentro de cada um, o código costuma ser dividido em camadas (recebe a requisição, aplica a regra de negócio, fala com o banco), assunto de Arquitetura em Camadas. Eles consultam o Redis para dados que precisam de leitura rápida e frequente, e o banco de dados para o estado persistente e confiável
  • Trabalho que não precisa ser feito na hora (enviar um e-mail, processar um pagamento em lote, gerar um relatório) é jogado em uma fila (Kafka, RabbitMQ, SQS), e processado depois por workers assíncronos

Nenhum sistema real usa todas essas peças ao mesmo tempo desde o primeiro dia. Um MVP pode rodar só com cliente, um servidor e um banco. Mas conforme a carga cresce, cada uma dessas peças entra no design para resolver um gargalo específico, e é exatamente essa ordem (o que adicionar e por quê) que as próximas notas desta seção e da seção de Escalabilidade e Infraestrutura cobrem em detalhe:

Vale notar que esse diagrama já é o resultado de várias decisões de trade-off (por que ter um API Gateway em vez de expor os microsserviços direto, por que ter cache antes do banco). O motivo de cada peça existir fica muito mais claro depois de estudar Capacity Planning e Latência, Throughput e Performance: é a carga esperada e o orçamento de latência que dizem quais dessas peças realmente valem a complexidade de adicionar.