sinka/docs/DECISIONS.md
2026-09-15 21:10:42 -03:00

43 lines
2.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.