# Decisões técnicas ## Multi-tenant: um banco MySQL por empresa Cada empresa recebe o próprio database (`sinka_t_`). O catálogo de empresas e o administrador da plataforma ficam em `sinka_platform`. A API resolve o banco no login (slug da empresa) e reutiliza um `PrismaClient` por database. Dados de operação (usuários, clientes, simulações) nunca compartilham tabela com outra empresa. **Por quê:** o desafio pede isolamento entre empresas. Database-per-tenant é o isolamento mais forte no MySQL: backup, restore e exclusão por empresa; um vazamento de `WHERE` não cruza tenant. O custo extra (provisionar database + `db push` no onboarding) cabe no desafio e deixa a justificativa explícita. O `PLATFORM_ADMIN` não mora no banco da empresa. Ele só cria/suspende tenants e não lê operação. Papéis **dentro** da empresa: `ADMIN` (Administrador), `MANAGER`, `OPERATOR`. ## Auth JWT de acesso (cookie httpOnly, 15 min) + refresh (cookie httpOnly, 7 dias, hash no banco da empresa). Login com slug + e-mail + senha. Rate limit no Redis. Google, GitHub e TOTP ficam para a etapa seguinte; os campos já existem no usuário. Auditoria: login, provisionamento de empresa, mudança de papel e CRUD de usuários. ## Prisma + NestJS Dois schemas Prisma: `platform` (catálogo) e `tenant` (cópia idêntica em cada database de empresa). Módulos Nest por domínio. ## Redis Fila de importação (próxima etapa), rate limit de login, cache de CEP/geocoding. ## Integrações externas 1. **ViaCEP**: cidade/UF a partir do CEP de origem e destino 2. **OpenStreetMap Nominatim**: coordenadas; distância por haversine Se o Nominatim falhar, a simulação usa uma distância de fallback (mesmo município / mesmo estado / interestadual). ## Cálculo de frete Peso cobrado = máximo entre peso real e peso cubado `(L × A × C) / 6000`. Preço = taxa base + (R$/kg × peso cobrado) + (R$/km × distância) + (ad valorem × valor da carga). Cada transportadora ativa da empresa gera uma cotação; a simulação grava o ranking no histórico.