DEVBLUEPRINTS

Blog

  • Sobre o blog
  • Arquivo
  • Newsletter
  • RSS

Legal

  • Termos e Privacidade
  • Contato
  • Sobre mim

Inscreva-se na Newsletter

Autorizo o envio de comunicações por e-mail ou qualquer outro meio e concordo com os Termos e Política de Privacidade

 

Blog
  • Sobre o blog
  • Arquivo
  • Newsletter
  • RSS
Legal
  • Termos e Privacidade
  • Contato
  • Sobre mim
© 2026 Todos os direitos reservados — Desenhado e construido comFooter Heartpor Ednaldo Luiz
Blog/System Design

System Design: o que é e como projetar sistemas na prática

Aprenda System Design com um sistema de agendamentos: requisitos, dados, capacidade, falhas e trade-offs para projetar sem complicar antes da hora.

system design
software architecture
scalability
software engineering
System Design: o que é e como projetar sistemas na prática
Ednaldo Luiz
Ednaldo Luiz
Nível: Intermediário
Nível:
Publicado: 22 de outubro de 2026
Última atualização: 22 de outubro de 2026
13 min de leitura
visualização: - visualização

Introdução

Uma tela de agendamento parece simples: mostrar os horários livres, deixar o usuário escolher um e salvar a reserva.

Agora duas pessoas escolhem o mesmo horário ao mesmo tempo. As duas recebem confirmação. Em outra tentativa, a reserva é gravada, mas a resposta se perde na rede. O usuário tenta novamente. Enquanto isso, o provedor de e-mail fica lento e a tela continua carregando.

É comum associar System Design a empresas gigantes, a entrevistas de big tech ou a sistemas com milhões de usuários. Faz sentido: é nesses lugares que o tema mais aparece.

Só que esse cenário fictício não precisa de nada disso para dar errado. Bastam concorrência, uma falha de comunicação e decisões que ficaram implícitas. Uma clínica com três profissionais já reúne tudo isso: dois pacientes escolhendo o mesmo horário, uma resposta que se perde na rede e ninguém que tenha decidido o que acontece quando o e-mail atrasa.

Organizar controllers, services e repositories ajuda a construir o código. Mas ainda precisamos decidir quem confirma a reserva, quais dados precisam ser protegidos e o que continua funcionando quando uma dependência falha.

É nesse espaço que entra System Design.

Vamos acompanhar esse sistema de agendamentos do problema ao primeiro desenho, sem começar por uma lista de tecnologias.

TL;DR

System Design é projetar como componentes, dados e comunicações trabalham juntos para atender a requisitos reais.

Comece pelo comportamento esperado, pelos limites e pelo que não pode dar errado. Depois desenhe uma solução inicial, confronte-a com carga e falhas e acrescente complexidade quando houver um motivo concreto.


O que é System Design

System Design é a atividade de definir responsabilidades, interfaces, dados e interações para que um sistema entregue o comportamento e a qualidade esperados. O desenho precisa considerar tanto as funcionalidades quanto requisitos de desempenho, segurança, disponibilidade e evolução.

Não é apenas escolher onde colocar um cache. É entender qual trabalho ele evitaria, quanto tempo o dado poderia ficar desatualizado e o que aconteceria se ele saísse do ar.

Uma distinção prática: System Design é o trabalho de conectar necessidade e solução. Arquitetura de software é o desenho estrutural e o conjunto de decisões que esse trabalho ajuda a produzir e revisar. Os termos se sobrepõem; essa distinção serve para orientar a conversa, não para criar uma fronteira rígida.

A pergunta inicial não é qual tecnologia usar. É qual compromisso o sistema precisa cumprir.


Comece pelos requisitos, não pela tecnologia

Requisitos definem o que a solução precisa entregar; restrições limitam os caminhos disponíveis. Sem esses dois elementos, comparar arquiteturas vira uma disputa de preferências. Tanto o método de projeto do quanto as orientações da Microsoft partem dessas informações para orientar decisões.

Para o nosso sistema, vamos assumir horários fixos de 30 minutos, previamente cadastrados, com uma vaga por profissional em cada horário. Usuários consultam e cancelam seus agendamentos; gestores acompanham as agendas dos profissionais das equipes sob sua responsabilidade.

Pagamentos, recorrência e integrações com calendários externos ficam fora desta primeira versão.

Funcionalidades, qualidade e restrições

Cada tipo de requisito cobra algo diferente do desenho. Para o agendamento, as primeiras definições seriam estas. Os números são hipóteses didáticas, não recomendações universais:

NaturezaPerguntaNo sistema de agendamentos
FuncionalidadesO que precisa acontecer?Consultar disponibilidade, reservar, cancelar e acompanhar agendas autorizadas.
QualidadeEm quais condições deve funcionar?Responder a 95% das consultas em até 300 ms na API, sob a carga-alvo de pico.
RestriçõesQue limites reduzem as escolhas?Quatro desenvolvedores, oito semanas e infraestrutura já utilizada pela empresa.

Cada linha já mexe no desenho:

  • Acompanhar agendas autorizadas parece só uma funcionalidade, mas traz uma regra de acesso que o backend vai ter de verificar. Ela volta logo abaixo.
  • 95% das consultas em até 300 ms dá para testar; “precisa ser rápido” não dá. A meta tem uma operação, um ponto de medição e um limite, e é isso que separa um requisito verificável de um pedido vago, tema do artigo sobre Engenharia de Requisitos. Falta a carga-alvo, que sai da estimativa de capacidade na próxima seção.
  • Quatro desenvolvedores e oito semanas tiram da mesa as opções que exigem operar muitas peças separadas. Essa linha vai pesar no primeiro desenho.

Invariantes: o que precisa continuar verdadeiro

Uma invariante é uma propriedade que o sistema precisa preservar durante suas operações. No nosso caso:

Um horário não pode ter duas reservas ativas.

Essa regra vale quando duas pessoas clicam juntas, quando uma requisição é repetida e quando existem várias instâncias, ou cópias em execução, da aplicação.

Outra regra do exemplo é que ser gestor não concede acesso a qualquer agenda. O acesso depende de qual equipe aquela pessoa pode acompanhar.

Antes de desenhar a solução, já encontramos duas responsabilidades: proteger a ocupação do horário e verificar o escopo de acesso. Elas não podem depender apenas da tela.


Estime a carga antes de pensar em escala

Uma estimativa de capacidade serve para orientar escolhas, não para prever o futuro com precisão. O que interessa primeiro é a ordem de grandeza, separando tráfego normal, picos e crescimento.

Suponha 10 mil usuários ativos por dia, cada um fazendo vinte consultas de disponibilidade:

Usuários por dia
Consultas por usuário
Segundos no dia
Média
10.000
×
20
÷
86.400
=
~2,3/s
Usuários por dia
10.000
×
Consultas por usuário
20
÷
Segundos no dia
86.400
=
Média
~2,3/s

Isso não quer dizer que três consultas por segundo bastem. A demanda não se espalha pelo dia: ela se concentra quando a agenda abre. Se mil dessas pessoas entrarem nos mesmos cinco minutos, a mesma conta muda de escala:

Pessoas na abertura
Consultas por pessoa
Segundos na janela
Pico
1.000
×
20
÷
300
=
~67/s
Pessoas na abertura
1.000
×
Consultas por pessoa
20
÷
Segundos na janela
300
=
Pico
~67/s

Com margem para o que a estimativa não vê, vamos estabelecer uma carga-alvo de teste de 100 consultas e 20 tentativas de reserva por segundo. As 20 tentativas cobrem um pedido de cada uma dessas mil pessoas no primeiro minuto. É uma hipótese de pico a validar, não uma medição.

O crescimento pede outra conta, a do armazenamento. Se tivermos cinco mil novas reservas por dia, com 2 KB lógicos cada, serão cerca de 10 MB por dia ou 3,65 GB por ano, usando unidades decimais. Histórico adicional, índices, réplicas e backups ainda não entraram, e o artigo sobre estimativa de storage mostra como somá-los.

Mesmo multiplicado algumas vezes, esse volume cabe com folga num único banco relacional. A conta não dimensiona o banco sozinha, mas impede que “precisamos de um banco distribuído” apareça sem justificativa.

Também importa onde a demanda se concentra. As 20 tentativas por segundo podem ser para vinte profissionais diferentes ou todas para o mesmo horário: são testes distintos, e o total de requisições não conta essa parte da história.


Do problema ao primeiro desenho de arquitetura

O primeiro desenho deve mostrar responsabilidades e caminhos de dados, não todos os detalhes da infraestrutura. O modelo organiza essas visões por níveis de aproximação; aqui, basta uma visão lógica pequena para discutir as escolhas principais.

Com quatro desenvolvedores e oito semanas, eu começaria com uma aplicação modular e um banco relacional. Agenda, Equipes e Autorização seriam módulos com responsabilidades separadas, não três microsserviços.

Um cuidaria das notificações. A reserva e a pendência de notificação seriam persistidas na mesma transação; o envio aconteceria depois.

O desenho separa o que o usuário precisa aguardar do trabalho que pode continuar em segundo plano:

AplicaçãoBanco relacionalUsuárioAutorizaçãoAgendaEquipesReservaPendência de notificaçãoWorkerProvedor de e-mailPOST /appointmentsdepois, fora da requisiçãona mesmatransação
Agenda, Equipes e Autorização são módulos da mesma aplicação. O worker processa tarefas persistidas; ele não decide quem conseguiu reservar.

Essa proposta evita introduzir comunicação de rede entre partes que o mesmo time modifica em conjunto. Microsserviços seriam uma alternativa a avaliar caso surgissem necessidades concretas de implantação, operação ou escala independentes. Não os adicionaria apenas para dividir o desenho.

O banco relacional é um candidato porque o exemplo precisa relacionar usuários, equipes e reservas e proteger alterações transacionais. Não porque “SQL é sempre melhor”. A escolha específica ainda depende do que a empresa opera e do comportamento medido.

As interfaces também precisam ser explícitas. Por exemplo:

POST/appointments

Nesse contrato, uma resposta de sucesso significará reserva confirmada, não apenas pedido recebido para processamento futuro:

201Createdapplication/json
{
	"id": "apt_8f21",
	"slot": "H-42",
	"status": "CONFIRMED"
}
201Createdapplication/json
{
	"id": "apt_8f21",
	"slot": "H-42",
	"status": "CONFIRMED"
}

Um 202 Accepted prometeria só que o pedido entrou na fila. Essa distinção orienta o que precisa acontecer antes de responder.


Dados, consistência e concorrência

Escolher um banco não resolve, por si só, quem pode alterar os dados nem como preservar as regras sob concorrência. No nosso desenho, o módulo de Agenda controla reservas; Equipes mantém os vínculos organizacionais usados pela autorização. Compartilhar um banco não significa permitir que qualquer módulo reescreva qualquer estado.

Consultar disponibilidade não é reservar

Imagine Ana e Bruno consultando o horário H-42. Os dois recebem “disponível”. Ana confirma primeiro, mas Bruno ainda está olhando a resposta anterior.

Se a aplicação apenas consulta e depois grava, sem uma proteção concorrente, existe espaço para ambas as operações passarem. Estar dentro de uma transação não torna automaticamente seguro qualquer fluxo de consulta seguida de escrita; o e as restrições usados importam.

Sem essa proteção, a disputa pelo H-42 termina assim:

Ana consulta:H-42 disponívelBruno consulta:H-42 disponívelAna gravaa reservaBruno gravaa reservaH-42 com duasreservas ativas
Os dois leram o horário livre antes de qualquer gravação. Cada gravação, sozinha, era válida; juntas, quebram a invariante.

Para os horários fixos deste exemplo, uma solução seria manter uma ocupação vigente por horário, protegida por unicidade no banco. A transação agrupa as alterações: a reserva e sua ocupação são confirmadas juntas ou desfeitas. Restrições de unicidade existem justamente para impedir valores duplicados nas colunas protegidas.

As duas solicitações passam pelo mesmo ponto de proteção:

Ana grava aocupação do H-42Bruno grava aocupação do H-42Unicidade: umaocupação por horárioAna: reservaconfirmadaBruno: 409
Exemplo em que Ana consegue reservar. A ordem de chegada não garante prioridade; o importante é que somente uma ocupação seja confirmada.

Quem perde a disputa recebe um conflito, não uma confirmação que será desfeita silenciosamente:

409Conflictapplication/json
{
	"code": "SLOT_UNAVAILABLE",
	"message": "Este horário acabou de ser reservado. Escolha outro."
}
409Conflictapplication/json
{
	"code": "SLOT_UNAVAILABLE",
	"message": "Este horário acabou de ser reservado. Escolha outro."
}

Ao cancelar, o sistema libera a ocupação e preserva o histórico da reserva. Isso também faz parte do modelo.

Só vale para horários fixos

A unicidade por horário funciona porque cada horário é uma vaga fixa. Com intervalos livres, duas reservas podem se sobrepor sem começar no mesmo instante, e impedir inícios iguais não basta.

Nem todo dado exige a mesma atualização

A confirmação precisa respeitar a ocupação atual. Já um resumo gerencial poderia, se o produto aceitar, ser atualizado com alguns segundos de atraso. São compromissos diferentes dentro do mesmo sistema.

Um cache de disponibilidade poderia acelerar consultas, mas não substituiria a verificação protegida ao reservar: a cópia pode estar desatualizada.

Permissões exigem outro cuidado. A API deve verificar o usuário, a ação e o recurso acessado. Ocultar uma agenda no frontend não protege a chamada direta ao backend. A recomenda validar autorização em cada requisição.

Perceba como a regra “gestor acompanha sua equipe” virou uma decisão sobre fonte de dados, consulta e fronteira de acesso, não apenas sobre qual botão aparece na tela.


Performance e escalabilidade são problemas diferentes

Latência é quanto uma operação demora. Throughput é quanto trabalho o sistema conclui por unidade de tempo. Escalabilidade é sua capacidade de lidar com mais carga ao ampliar recursos, preservando os objetivos definidos. Um sistema pode responder rápido com dez usuários e degradar quando a concorrência aumenta.

Escalar verticalmente é dar mais recursos a uma instância. Escalar horizontalmente é adicionar instâncias. Um balanceador distribui requisições entre elas, mas isso não elimina um gargalo compartilhado no banco. Também precisamos evitar que uma requisição dependa de estado guardado somente na memória de uma instância específica.

Outras peças entram conforme o problema observado:

Pressão observadaOpção a avaliarO preço da escolha
Muitas leituras repetidasCacheReduz acesso à origem, mas exige lidar com dados desatualizados.
Consultas sobrecarregam o banco principalRéplica de leituraDistribui leituras, mas a replicação assíncrona pode apresentar atraso.
Picos de trabalho que pode esperarFila e workersAmortecem picos, mas acrescentam espera, reprocessamento e monitoramento.
Dados ou escrita atingem limites de um nóParticionamento entre nósDistribui carga, mas complica consultas e operações entre partições.

Esses mecanismos resolvem problemas distintos, e cada um cobra o seu preço.

No nosso exemplo, eu verificaria consultas, índices e quantidade de dados retornados antes de adicionar outra camada. Um histórico inteiro devolvido a cada acesso pede uma resposta limitada, possivelmente paginada, não necessariamente mais servidores.

E uma fila não cria capacidade infinita. Se o trabalho chega continuamente mais rápido do que os consumidores processam, o atraso cresce.

Suponha que, no pico, as reservas confirmadas gerem 20 e-mails por segundo e que o provedor aceite 10:

E-mails que chegam
E-mails enviados
Crescimento da fila
20/s
−
10/s
=
10/s
E-mails que chegam
20/s
−
E-mails enviados
10/s
=
Crescimento da fila
10/s

Em dez minutos de pico, são 6.000 e-mails esperando. E mais workers não resolvem: o limite está no provedor, não em quem consome a fila.


Projete também para falhas e operação

Um desenho precisa explicar o que acontece quando suas partes deixam de responder. Falhas transitórias, timeouts e sobrecarga fazem parte do problema, e novas tentativas mal controladas podem agravá-lo.

No sistema de agendamentos, a consequência depende de qual parte falhou.

Se o banco estiver indisponível, não prometemos uma nova reserva. Sem registrar a ocupação com segurança, responder “confirmado” quebraria o contrato do endpoint.

Se o provedor de e-mail falhar, a reserva já confirmada permanece válida. A pendência persistida permite tentar o envio depois. O worker precisa registrar sucesso ou falha, limitar tentativas e tornar pendências antigas visíveis para a operação. Neste desenho inicial, uma tabela de tarefas pode cumprir esse papel sem um separado.

Se a resposta se perder depois da confirmação, o resultado fica incerto para o cliente. Um timeout não significa que nada aconteceu. Para permitir repetições seguras, podemos identificar a solicitação e reconhecer novas tentativas da mesma operação, sem produzir outra reserva. Essa propriedade é chamada idempotência.

Com uma chave enviada pelo cliente, a repetição deixa de ser um novo pedido:

ClienteServidorEnvia a reservacom a chave 7f3cGrava a reservae a chave 7f3cTimeout:sem respostaRepete com amesma chave 7f3c7f3c já existe:devolve a reservaRecebe a mesmareservaa respostase perde
O servidor guarda a chave junto com a reserva. A segunda tentativa encontra a chave e devolve o resultado da primeira.

A unicidade protege o horário contra duas reservas; a idempotência ajuda a reconhecer a repetição de um mesmo pedido. Uma não substitui a outra.

Como saberemos que o sistema está saudável?

O Google SRE recomenda acompanhar latência, tráfego, erros e . Para a agenda, eu acrescentaria conflitos de reserva, idade das notificações pendentes e tempo esperando conexão com o banco.

Um aumento de latência pede investigação: processamento lento, dependência externa ou espera por um recurso ocupado? Métricas mostram o comportamento agregado; o rastreamento de uma requisição ajuda a localizar onde ela gastou tempo.

O artigo de P50, P95 e P99 aprofunda essa leitura. Aqui, basta guardar que “a média está boa” não encerra a avaliação.

Também precisamos combinar quanto tempo a operação pode ficar indisponível e quanta perda de dados é tolerável em uma recuperação. Essas metas orientam redundância, backups e testes de restauração; um serviço gerenciado não elimina a necessidade de verificar o resultado.


Como avaliar e evoluir o desenho

Uma arquitetura é uma proposta a validar, não uma prova de capacidade. Para o nosso exemplo, os testes precisam confrontar o desenho com os compromissos assumidos:

  1. Disputar o mesmo horário. Duas solicitações concorrentes não podem produzir duas reservas ativas.
  2. Exercer a carga-alvo. As consultas precisam atender à meta de latência com 100 leituras e 20 tentativas de reserva por segundo, usando dados representativos.
  3. Interromper dependências. A indisponibilidade do e-mail não deve bloquear reservas; a queda do banco não pode produzir falsas confirmações.
  4. Testar fronteiras de acesso. Um gestor não pode consultar outra equipe apenas alterando um identificador na requisição.

Depois entram os custos que nenhum diagrama mostra: quem recebe o alerta, restaura o banco, mantém os contratos e atualiza as dependências. A avaliação de arquitetura precisa considerar esse trabalho, além da conta de infraestrutura.

E o desenho de hoje não tem um próximo passo fixo. Cada sinal pede uma mudança diferente:

Aplicação modulare banco relacionalRelatórioscompetem comas reservasNotificaçõesacumulamatrasoEquipes pedemimplantaçãoprópriaIsolar osrelatóriosRever o fluxode notificaçõesRever asfronteiras
Cada sinal abre uma investigação, não uma mudança automática. Sem nenhum deles, o desenho de hoje continua servindo.

A evolução responde a limites e necessidades concretas, não a um roteiro obrigatório de tecnologias.

Eu registraria a proposta em um Design Doc e preservaria as escolhas arquiteturais relevantes em ADRs. Por exemplo: por que a reserva precisa de confirmação imediata, mas a notificação pode acontecer depois?

Essa justificativa importa quando alguém propuser colocar toda a operação em uma fila para “ganhar escala”. A mudança pode ser válida, mas altera a promessa feita ao usuário e precisa ser discutida como tal.


Antes de fechar, cinco perguntas sobre requisitos, carga, concorrência, falhas e a promessa feita ao usuário:

Questão1/5

Todas as metas abaixo falam das consultas de disponibilidade. Qual delas permite avaliar se o desenho atende?


Conclusão

O sistema de agendamentos começou com uma tela, um horário e um botão. O desenho ganhou forma quando definimos o que esse botão precisava garantir.

Não foi necessário começar com microsserviços, cache distribuído e várias filas. Foi necessário explicitar responsabilidades, proteger a reserva e separar a confirmação do trabalho que poderia esperar.

Aprender System Design é desenvolver esse raciocínio: ligar cada peça a um requisito, reconhecer o custo da escolha e saber qual evidência faria o desenho mudar.

Na próxima funcionalidade do seu projeto, escolha uma operação importante e siga seu caminho. Quem pode executá-la? Que estado ela altera? O que acontece se chegar duas vezes ou se a resposta não voltar?

Antes de adicionar outra caixa ao diagrama, responda a essas perguntas. Você já estará fazendo System Design.

Nesta página

Compartilhe

Recomendados

Os padrões, os pilares e a arquitetura por trás do desenho deste artigo.

Ednaldo Luiz
GitHubLinkedInPortfólio

Ednaldo Luiz

Arquiteto e Engenheiro de Software | Java & IA

Engenheiro de Software focado em arquitetura e performance. Trabalho com Java/Spring Boot, SQL bem estruturado, serviços escaláveis na AWS e soluções GenAI com RAG (LangChain + bases vetoriais). Valorizo código legível e decisões bem justificadas.