2.1 KiB
Decisões técnicas
Multi-tenant: um banco MySQL por empresa
Cada empresa recebe o próprio database (sinka_t_<slug>). 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
- ViaCEP — cidade/UF a partir do CEP de origem e destino
- 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.