Design clássico · Sênior
Aplicativo de corridas (Uber)
Projeto de entrevista clássico. Pontos centrais: indexação geoespacial, WebSocket e presença e locks distribuídos.
Resposta curta
Projeto de entrevista clássico. Pontos centrais: indexação geoespacial, WebSocket e presença e locks distribuídos. Num aplicativo de corridas, o passageiro vê a tarifa estimada e pede a corrida; o sistema precisa casar esse pedido com um motorista próximo e disponível, e o motorista aceita ou recusa e segue para o embarque.
01 · CONTEXTO E REQUISITOS
O que o despacho precisa garantir
Num aplicativo de corridas, o passageiro vê a tarifa estimada e pede a corrida; o sistema precisa casar esse pedido com um motorista próximo e disponível, e o motorista aceita ou recusa e segue para o embarque. O coração do problema é o despacho (quem recebe qual pedido) e o estado da corrida ao longo desse caminho.
Três requisitos não funcionais mandam no desenho: matching de baixa latência, com resposta (match ou falha) em menos de um minuto; consistência forte para que nenhum motorista receba várias corridas ao mesmo tempo; e alto throughput em picos e eventos, com cem mil pedidos vindos do mesmo lugar.
02 · FLUXO BÁSICO
Do pedido ao aceite
O serviço de corridas cria a corrida com estado inicial de pedido feito e dispara o matching. A entidade da corrida acompanha o pedido até a conclusão e guarda, entre outros campos, o estado, e é esse estado que vai mudando a cada decisão.
O passageiro confirma a tarifa e pede a corrida; o gateway autentica e repassa ao serviço de corridas.
O serviço de corridas grava a corrida no banco com o estado inicial de pedido feito.
Em seguida ele aciona o serviço de matching para achar um motorista.
Em paralelo, o tempo todo, os motoristas enviam a posição e o armazém mantém só a mais recente de cada um.
O matching consulta os motoristas próximos e disponíveis, ranqueia e manda o serviço de notificações avisar o primeiro da lista.
O motorista aceita pelo app; o pedido volta pelo gateway e o serviço de corridas atualiza o estado e atribui o motorista.
O serviço de matching escolhe o motorista pesando proximidade, disponibilidade, avaliação e outros fatores. O serviço de notificações avisa o motorista quando um pedido é casado com ele, e o serviço de localização cuida das posições em tempo real.
O matching ranqueia os motoristas elegíveis e notifica o primeiro da lista por APNs ou FCM. Se esse motorista recusa, o sistema notifica o próximo da lista; ao aceitar, o serviço de corridas atualiza o estado e atribui o motorista.
03 · POSIÇÕES DOS MOTORISTAS
O que alimenta o matching
Com milhões de motoristas enviando a posição a cada poucos segundos, a taxa de escrita chega a milhões de atualizações por segundo. Sem otimização, achar motoristas por latitude e longitude exigiria varrer a tabela inteira.
Uma saída é um armazém geoespacial em memória, como o Redis: ele atende escritas e buscas por proximidade com alto throughput e baixa latência e ainda expira dados sozinho. Para motoristas que ficam offline, um processo periódico remove quem passou de um limite de tempo sem atualizar.
O principal desafio é a durabilidade, mas, como as posições se renovam em poucos segundos, o impacto de perder dados seria pequeno. Do lado do app, intervalos adaptativos reduzem o volume: a frequência de envio muda conforme velocidade, direção, proximidade de pedidos pendentes e status do motorista.
04 · UM MOTORISTA POR VEZ
Evitando pedidos simultâneos ao mesmo motorista
Consistência no matching significa pedir a um motorista por vez para cada corrida e entregar a cada motorista um pedido por vez. O motorista tem uma janela curta, de dez segundos, para aceitar ou recusar antes de o sistema passar ao próximo.
| Critério | Trava na aplicação | Status no banco | Trava com expiração no Redis |
|---|---|---|---|
| Coordenação entre instâncias | Não há coordenação central e surgem condições de corrida | O banco coordena quem trava | Todas as instâncias disputam a mesma trava |
| Se o dono da trava cai | Ela pode ficar presa | O temporizador em memória se perde e a trava fica presa | A trava expira sozinha |
| Custo principal | Falta de coordenação | Cron ou rotina extra para liberar | Depende do armazém em memória |
O pedido de corrida é publicado na fila em vez de ser tratado direto pelo serviço de matching.
Um worker pega o pedido; ele só será dado como concluído depois que o matching der certo.
Antes de contatar o motorista, o worker tenta adquirir a trava daquele motorista, com tempo de expiração igual à janela de aceite.
Se outro worker tenta o mesmo motorista agora, a trava já existe e o pedido dele não segue para esse motorista.
Com a trava, o worker manda o pedido ao motorista por push.
Se o motorista aceita, o estado vai para o banco e a trava é liberada; se ignora, a trava expira sozinha e ele volta a ser candidato.
A trava dura pouco e é efêmera, então perdê-la é mais fácil de recuperar do que perder um dado durável; mesmo assim, o armazém precisa de monitoramento e failover porque o matching depende dele.
05 · PICOS E FALHAS
Pedidos que não podem se perder
Sem fila, se uma instância do matching cai, os pedidos que ela processava se perdem. Com uma fila, cada pedido entra na fila e o matching consome em ordem de chegada, escalando horizontalmente se a fila cresce.
Confirmando o offset só depois de achar o match, o pedido continua na fila se a instância cair e outra instância o retoma.
E se o motorista não responde? Uma delay queue agenda uma mensagem para a expiração da janela e, se a corrida segue sem atribuição, passa ao próximo da lista. O custo é gerenciar várias mensagens atrasadas e cancelá-las corretamente quando o motorista aceita.
Uma execução durável resolve isso de outro jeito: oferece timeouts, retentativas e estado que sobrevivem a quedas e reinícios do serviço. O workflow guarda o estado de forma persistente e, se o serviço cair, retoma de onde parou. A Uber é a autora original do projeto Cadence, que deu origem ao Temporal.
O serviço de corridas inicia um fluxo de matching para a nova corrida.
O fluxo envia o pedido ao primeiro motorista da lista e passa a esperar a resposta com um limite de tempo.
O motorista não responde dentro do limite; o fluxo percebe a expiração e segue para o próximo da lista.
A cada passo o progresso fica gravado no armazém de estado do fluxo.
Se a instância do fluxo cai no meio da espera, o estado salvo continua disponível.
Outra instância retoma o fluxo do ponto em que parou; quando um motorista aceita, o banco recebe o estado final.
06 · O QUE LEVAR
Cada falha, um mecanismo
| Problema | Mecanismo | Observação |
|---|---|---|
| Dois matchers disputam o mesmo motorista | Trava com expiração no armazém em memória | A trava some sozinha se ninguém aceita |
| Instância do matching cai | Fila com offset confirmado após o match | O pedido volta para outra instância |
| Motorista não responde | Delay queue ou temporizador do workflow | Passa ao próximo da lista |
Perguntas frequentes
O que é Aplicativo de corridas (Uber)?
Projeto de entrevista clássico. Pontos centrais: indexação geoespacial, WebSocket e presença e locks distribuídos.
O que o design de Aplicativo de corridas (Uber) cobre?
Ele está organizado nestas seções: O que o despacho precisa garantir, Do pedido ao aceite, O que alimenta o matching, Evitando pedidos simultâneos ao mesmo motorista, Pedidos que não podem se perder e Cada falha, um mecanismo.
Quanto tempo leva Aplicativo de corridas (Uber) e qual é o nível?
Leva cerca de 16 minutos para ler e tem nível Sênior.
Agora projete você mesmo
Rode este caso como uma entrevista na escola de System Design: você toma as decisões, o tutor as questiona e você vê a nota do seu desenho.