sinka/docs/DECISIONS.md
2026-09-16 04:57:09 +00:00

2.1 KiB
Raw Blame History

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

  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.