Pular para o conteúdo principal
Luiz Pansarini
← Voltar aos projetos

E-commerce de máquinas pesadas

Principal Software Engineer · 2024

Next.jsReactTypeScriptTailwindShadcnReact NativeExpoBun
E-commerce de máquinas pesadas

Problema

Compradores de máquinas pesadas nos EUA não tinham nenhuma experiência de compra online que valesse a pena usar. Os players dominantes eram listagens de catálogo: um formulário de contato, um telefone e três semanas de vai-e-vem até um orçamento aterrissar. Specs viviam em PDFs escaneados; preços viviam exclusivamente na cabeça dos vendedores; financiamento era uma ligação separada para um broker.

Duas restrições do lado de negócio amplificaram o brief técnico. Primeira: toda transação precisava trafegar pelo Odoo, o ERP existente — contabilidade, estoque, fulfillment e repasses para parceiros já viviam ali, e substituí-lo não era uma opção. Segunda: os compradores estavam na estrada. Metade do tráfego analítico vinha de celular, frequentemente em LTE instável em canteiros de obra rurais. Uma SPA desktop-first reluzente teria entregado um LCP de 4 segundos ao único público que importava.

O brief parecia e-commerce, mas as características de runtime pareciam software de campo: conectividade baixa, carrinho durável, app com cara de nativo, hardening sem PII, e um checkout que sobrevivia ao comprador apertar voltar acidentalmente em uma carregadeira de US$ 180.000.

Solução

Liderei a arquitetura como Principal Engineer, depois construí pessoalmente as peças que exigiam mais investimento de liderança técnica — a fronteira do ERP, o contrato de estado do carrinho e o module bridge do React Native — enquanto fazia pareamento com o time na storefront.

O formato que aterrissou: Next.js App Router para a storefront (RSC para páginas de produto, ilhas client para carrinho + checkout), um gateway Fastify enxuto na frente do Odoo traduzindo REST/JSON para o dialeto XML-RPC do Odoo, e um app React Native + Expo que reutilizava o mesmo gateway. Bun rodava o gateway em Docker; deploy era Vercel para o tier web e EAS para o pipeline mobile.

O gateway Fastify foi a peça de maior alavancagem. O schema RPC do Odoo é generoso e pouco tipado; um campo escrito errado aterrissa em produção como uma linha de US$ 0 em uma invoice real. O padrão que funcionou foi uma fronteira validada por Zod em toda rota do gateway:

// gateway/src/routes/quotes.ts (sanitizado)
import { Type } from '@sinclair/typebox';
import type { FastifyPluginAsync } from 'fastify';
import { odooClient } from '../lib/odoo';
import { QuoteRequest, QuoteResponse } from '../schemas/quote';
 
export const quoteRoutes: FastifyPluginAsync = async (app) => {
  app.post('/quotes', {
    schema: {
      body: Type.Unsafe(QuoteRequest),
      response: { 200: Type.Unsafe(QuoteResponse) },
    },
  }, async (req, reply) => {
    const parsed = QuoteRequest.parse(req.body);
    const odooResult = await odooClient.execute(
      'sale.order',
      'create',
      [parsed.toOdooPayload()],
    );
    return QuoteResponse.parse({
      id: odooResult.id,
      total: odooResult.amount_total,
      status: odooResult.state,
    });
  });
};

O outro momento de liderança técnica foi o contrato de estado do carrinho entre web e mobile. Os dois clients persistiam o carrinho localmente, os dois podiam iniciar checkout, e os dois tinham que convergir no mesmo quote_id do gateway sem uma guerra de sync. Entregamos um padrão de fonte única da verdade: o gateway emitia quote_ids com TTL curto, o client persistia o id (não o carrinho), e um id obsoleto disparava um aviso elegante de "seu carrinho foi atualizado" em vez de um merge silencioso.

Impacto

A storefront foi lançada como a primeira experiência de compra transacional no segmento de máquinas pesadas dos EUA. O app mobile foi para a App Store e Play Store sob processos de review que dirigi pessoalmente fim-a-fim (a fila da Apple é praticamente um codebase próprio). LCP em um iPhone SE real em LTE aterrissou em ~1,7s nas páginas de produto, ~2,1s no carrinho — bem abaixo do limiar "bom" de 2,5s do Lighthouse.

0 → 1storefronts transacionais no segmento (web + nativo, lançamento simultâneo)

~1,7sLCP real-world em iPhone SE / LTE (páginas de produto)

A vitória downstream foi operacional, não apenas de produto. Vendas passaram a ter uma visão única e ao vivo de estoque, orçamentos e status de pedido — puxados do Odoo pelo mesmo gateway que a storefront usava. A fronteira do ERP virou também uma API interna.

Stack

  • Frontend (web): Next.js 15 App Router, React Server Components, Tailwind v4, primitivos Shadcn, TypeScript strict.
  • Frontend (mobile): React Native + Expo (managed workflow, EAS Build / EAS Submit), tipos TypeScript compartilhados via package de workspace.
  • Gateway: Bun + Fastify, schemas Zod em toda fronteira, OpenAPI auto-gerado dos mesmos schemas para consumo do time de vendas.
  • ERP: Odoo (existente), bridged via XML-RPC atrás da fronteira Fastify.
  • Hosting: Vercel (storefront), EAS (mobile), Odoo self-hosted (existente).