AAMLabs (atual Forge Labs) · Engenheiro full-stack

Pedeja Delivery

SaaS de delivery com sete microsserviços NestJS orientados a eventos, atrás de um gateway, atendendo dois clientes: um backoffice web e um app mobile.

Cliente
AAMLabs (atual Forge Labs)
Papel
Engenheiro full-stack
Período
2024 ao presente

O problema

Um SaaS de delivery precisa atender três públicos com ritmos muito diferentes: o restaurante, o entregador e o cliente final. Tratar isso como um monolito significa acoplar o pico de pedidos do almoço, o tracking constante da entrega e a explosão de pagamento no fechamento na mesma base e no mesmo deploy. A meta foi separar os domínios de forma limpa, para escalar e evoluir cada parte sem que uma pise no calo da outra.

Arquitetura: sete serviços atrás de um gateway

O backend são sete serviços NestJS, cada um dono do próprio banco e do próprio contrato, atrás de um API gateway que é a porta única para os clientes. Duas pontas consomem essa porta: o backoffice web e o app mobile.

   backoffice web (React)        app mobile (Expo / React Native)
            └───────────────┬────────────────┘
                       API gateway

   ┌──────── 7 serviços NestJS, cada um com seu banco ────────┐
   auth          sessões, refresh, rotação de token
   user          perfis, endereços, papéis
   catalog       restaurantes, cardápios, itens (cache em Redis)
   order         máquina de estado do pedido (Postgres + MongoDB)
   payment       provedores, liquidação, estornos
   notification  e-mail, push, in-app
   delivery      atribuição de entregador, ETA, comprovante

   eventos via Kafka · status do pedido em tempo real via WebSocket

A comunicação assíncrona entre serviços é orientada a eventos, com Apache Kafka. Quando um pedido muda de estado ou um pagamento é processado, o serviço publica um evento em um tópico (order.status.changed, payment.processed e afins), e quem se importa consome. Isso desacopla os serviços no tempo: o pagamento não precisa saber quem reage a ele.

O serviço de pedido é o mais interessante. O estado do pedido vive no Postgres como fonte da verdade (PENDING → CONFIRMED → PREPARING → READY → PICKED_UP → IN_DELIVERY → DELIVERED, com CANCELLED e REFUNDED), e o histórico completo de transições vai para o MongoDB, melhor para uma linha do tempo append-only de auditoria. Em cima disso, um gateway WebSocket com Socket.io escuta os eventos do Kafka e empurra a mudança de status em tempo real para quem está acompanhando o pedido. Cada serviço expõe REST documentada via Swagger.

O aprendizado é sobre fronteira: o evento é o que mantém os serviços de fato independentes. No momento em que um serviço precisa chamar o outro de forma síncrona para um efeito colateral, o acoplamento volta pela porta dos fundos.

O backoffice web

O painel administrativo é um produto por si só, e foi onde passei boa parte do tempo. É uma SPA em React 19 com Vite e TanStack Router, com roteamento file-based e divisão de código automática. A organização é fatiada por domínio: cada feature (pedidos, restaurantes, entregadores, pagamentos, promoções, usuários, suporte, ajustes e mais) é autocontida, com suas páginas, componentes, chamadas de API, schemas e store. São doze módulos cobrindo a operação inteira.

O dashboard usa Recharts para receita, pedidos, desempenho de entregador e métodos de pagamento. Os primitivos de interface são shadcn/ui sobre Radix, com tokens Tailwind v4 e tema claro e escuro. A rota só carrega depois de um guard de autenticação (beforeLoad), e o Axios injeta o JWT em cada requisição e desloga no 401.

A decisão que mais rendeu no dia a dia foi montar uma camada de mocks com MSW. O backoffice inteiro sobe e exercita toda a interface sem nenhum backend rodando, o que deixou o front evoluir no próprio ritmo, desacoplado do andamento dos serviços. O aprendizado: um mock fiel ao contrato não é só conveniência de teste, é o que permite as duas pontas avançarem em paralelo.

O app do cliente

A terceira via é o app do cliente final, em Expo com React Native e expo-router, o mesmo roteamento file-based do backoffice, agora no mobile. A interface usa NativeWind, que é o Tailwind no React Native, então o vocabulário de estilo é o mesmo das outras pontas. O fluxo cobre o caminho do pedido: autenticação, navegação por categorias e busca com filtros, escolha de endereço no mapa com react-native-maps e expo-location, e o acompanhamento dos pedidos.

O estado é mais enxuto que o do painel: em vez de TanStack Query e stores, o app se apoia em contexto de React para autenticação e preferências, falando com o backend por uma única base de URL. Essa base única é o ponto da arquitetura: tanto o app quanto o backoffice enxergam um só endereço, o gateway, e não os sete serviços por trás. Mudar o roteamento interno do backend não mexe em nenhum cliente.

Onde está

O projeto está em construção, com as três vias erguidas. Os sete serviços rodam localmente via Docker Compose com Postgres, MongoDB e Kafka, com eventos e WebSocket funcionando; o backoffice web está completo sobre os mocks; e o app mobile cobre o fluxo principal do cliente. A peça que costura tudo para produção é o API gateway, a porta única que centraliza autenticação e roteamento. A forma, porém, já está decidida: cada serviço evolui e implanta no próprio ritmo, e cada cliente fala com um só endereço.

Números

  • 7

    Microsserviços

  • 12

    Módulos do painel

Stack

  • NestJS 11
  • Prisma 6
  • PostgreSQL
  • MongoDB
  • Apache Kafka
  • Socket.io
  • Redis
  • React 19
  • Vite 7
  • TanStack Router
  • TanStack Query
  • Tailwind 4
  • shadcn/ui
  • Recharts
  • MSW
  • Expo
  • React Native
  • expo-router
  • NativeWind
  • Zod