Projetos Práticos para Aprender Design de Sistemas

Projetos Práticos para Aprender Design de Sistemas






Projetos Práticos para aprender System Design


1. Definindo objetivos, requisitos não funcionais e métricas

Antes de desenhar qualquer solução, defino o objetivo central e as métricas de sucesso. Com System Design, aquilo que não está quantificado tende a receber menos atenção do time. Abaixo estão diretrizes que eu sigo.

  • Latência alvo por operação: leituras rápidas (< 100 ms) e escritas estáveis (< 200–500 ms) sob carga típica.
  • Throughput esperado (operações por segundo) e picos sazonais; planejo dimensionamento com buffers para picos.
  • Disponibilidade desejada (por exemplo, 99.9% ou 99.99%) e RTO/RPO aceitáveis.
  • Consistência de dados: decidir entre forte imediato ou eventual, com mecanismos de reconciliação quando necessário.
  • Custos operacionais, limites de banda, uso de cache e estratégias de retenção de dados.
  • Observabilidade: métricas, logs estruturados e tracing para diagnóstico rápido de incidentes.

Exercício rápido: descreva, em 1 página, os requisitos para um sistema de reservas de ingressos, considerando pico de demanda, controle de disponibilidade e recuperação de falhas.

2. Arquitetura de alto nível: componentes e fluxos

Eu normalmente começo definindo um conjunto básico de componentes que sustenta o domínio do sistema, mantendo limites claros entre responsabilidades:

  • API Gateway que gerencia autenticação, autorização e roteamento.
  • Serviços de domínio (ex.: Catálogo, Pedido, Usuário) com interfaces estáveis.
  • Banco de dados principal por serviço (isolamento de transações) com leituras otimizadas via camadas de leitura.
  • Cache distribuído (por exemplo, Redis) para dados quentes e padrões de acesso previsíveis.
  • Event Bus ou fila assíncrona para integração entre serviços e atualização de visões de leitura.
  • Visões de leitura separatadas (read models) para melhorar o desempenho de consultas complexas.
  • Observabilidade: métricas, logs estruturados e tracing para rastrear a jornada de uma solicitação.

Fluxo típico de uma operação de criação:

  • Cliente => API Gateway => Serviço de Autenticação (token) => Serviço de Domínio (cria pedido) => Evento no Event Bus => Serviço de Visão de Leitura atualiza read model => Cache invalidado/refrescado.

3. Dados, particionamento e consistência

Para suportar multi-tenancy e escalabilidade horizontal, o particionamento (sharding) é escolhido com base em padrões de acesso. A ideia é manter cada particionamento com carga balanceada, facilitar recuperação e reduzir contenção entre tenants.

  • Escolha de chave de particionamento: tenant_id ou user_id, dependendo do padrão de leitura/escrita.
  • Particionamento horizontal para escalar writes; leitura pode ser distribuída entre read models ou caches.
  • Consistência: use confirmação assíncrona via eventos para atualizações entre serviços, mantendo leitura rápida com visões atualizadas periodicamente.
  • Read models substituem queries complexas nos dados transacionais, melhorando a escalabilidade de leitura.
  • Estratégias de retenção, TTL e arquivamento para controle de custo e conformidade.

-- Exemplo de particionamento em PostgreSQL
CREATE TABLE events (
  tenant_id UUID NOT NULL,
  event_id UUID NOT NULL,
  created_at TIMESTAMPTZ NOT NULL,
  type TEXT,
  payload JSONB,
  PRIMARY KEY (tenant_id, created_at, event_id)
) PARTITION BY HASH (tenant_id);

CREATE TABLE events_p0 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE events_p1 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE events_p2 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE events_p3 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 3);

Observação: este é um esquema básico para demonstrar o conceito de particionamento. Ajustes são necessários conforme o padrão de acesso real, a frequência de eventos e o volume de dados.

4. Observabilidade, resiliência e trade-offs

Operar sistemas em produção exige visibilidade e controles para manter a confiabilidade. Abaixo estão práticas que eu considero essenciais.

  • Logs estruturados e consistentes, com correlação de traces entre serviços.
  • Métricas significativas: latência, throughput, taxa de erros, saturation de recursos.
  • Tracing distribuído para entender a jornada de uma requisição entre serviços.
  • Estratégias de resiliência: retries com backoff, circuit breakers, timeout adequado e bulkheads.
  • Planejamento de capacidade e DR: avaliações periódicas, failover testado e replicação entre regiões quando aplicável.
  • Escolha de trade-offs: equilíbrio entre consistência de dados, disponibilidade e custo (CAP), alinhado aos SLOs.

Exercício rápido: desenhe um conjunto de dashboards para monitorar um sistema de reservas com foco em disponibilidade e latência. Liste as métricas mais relevantes para cada domínio.

Curtiu o conteúdo técnico? Continue explorando mais posts para aprofundar sua compreensão de arquitetura de software.

Ver mais posts


Y

Yuri Sousa

Front-End Developer / Designer

Desenvolvedor apaixonado por criar experiências digitais acessíveis e visualmente perfeitas. Escrevo sobre desenvolvimento web, design e tecnologia.