Diego
← Voltar para projetos

Trackrr

Gestão de despesas pessoais com orçamento mensal por categoria — API REST + front Next.js.

Back-end (API)

  • Express 5
  • TypeScript
  • Prisma 7
  • PostgreSQL
  • Zod
  • JWT
  • bcryptjs
  • Pino
  • Vitest
  • Supertest

Front-end

  • Next.js 16
  • React
  • TypeScript
  • TanStack Query
  • Zustand
  • Axios
  • Zod

Full-screen para maior qualidade.

Contexto

Trackrr é um sistema de gestão de despesas pessoais com controle de orçamento mensal por categoria. O usuário cadastra categorias, registra despesas e define um teto por categoria. O dashboard mostra gasto vs. orçamento em tempo real, sempre derivado a partir das despesas — nunca armazenado. Exportação em PDF (via @react-pdf/renderer no client) e CSV (sem lib, gerado em código direto).

O back-end segue arquitetura em camadas estrita com Express 5, Prisma 7 e PostgreSQL. Valores monetários usam Decimal(12,2) serializado como string no JSON. Suíte de testes executa 79 testes em ~1 segundo com Prisma mockado, sem dependência de banco.

O front-end usa Next.js 16 com TanStack Query para estado de servidor e Zustand exclusivamente para auth. Autenticação dual: JWT no header via interceptor Axios + cookie espelho para o middleware edge. Organização por feature, não por tipo de arquivo.

Decisões técnicas

API · Arquitetura em camadas estrita

A aplicação segue Routes → Controllers → Services → Repositories, com cada camada possuindo uma única responsabilidade. Controllers não acessam o Prisma, services não manipulam req/res, e repositories não contêm regras de negócio. Dependências fluem em uma única direção.

Repositories podem ser mockados para testar services em isolamento; o Prisma client pode ser mockado para testar rotas end-to-end. Regras de negócio ficam nos services, validações nos schemas, e mudanças de persistência não vazam para o restante do sistema.

API · Decimal para valores monetários

Todos os campos monetários usam Decimal(12,2) no Prisma e são serializados como string no JSON (“12.50” em vez de 12.5). Evita problemas de precisão do IEEE 754 — 0.1 + 0.2 === 0.30000000000000004 em JavaScript — que acumulam erros em operações sobre orçamento.

Serializar como string preserva a precisão até o consumidor, que pode optar pela representação adequada ao seu contexto (libs como decimal.js no front-end, BigInt para cálculos exatos, ou conversão para float quando precisão não é crítica).

API · Estratégia de testes com Prisma mockado

A suíte (Vitest + Supertest) executa 79 testes em ~1 segundo sem dependência de banco em execução. O Prisma client é mockado via vi.mock, e cada teste configura explicitamente o comportamento dos métodos consumidos. Cobertura inclui rotas, middlewares, services, mappers — toda a stack acima da camada de persistência.

Trade-off: bugs de SQL ou migrations quebradas não são detectados aqui. A camada complementar seria testes de repository contra Postgres real (testcontainers) no pipeline de CI.

Front · Zustand para auth, TanStack Query para o resto

Estado de UI e estado de servidor são tratados por ferramentas distintas. Server state usa TanStack Query — cache, invalidação, refetch e dedup de requests em flight.

Zustand entra só onde Query não cabe: o par { token, user } da auth, que precisa ser síncrono, persistir em localStorage e ser lido fora de componente (interceptor do Axios). Server state e client state ficam em camadas distintas.

Front · Arquitetura por feature, não por tipo

features/expenses/ agrupa tudo da feature: api.ts, hooks.ts, schemas.ts, types.ts, components/. A pasta app/ fica fina — cada página é composição desses hooks e components.

Regra: features não importam umas das outras. Qualquer coisa transversal vai para shared/. O blast radius de qualquer mudança fica contido na pasta da feature.

Front · PDF gerado no client, não no servidor e com lazy import

Caminho mais fácil seria adicionar Puppeteer/Playwright na API Express e renderizar PDF no back — pixel-perfect, qualquer CSS funciona, mas o custo seria muito alto.

@react-pdf/renderer no client: componentes JSX viram PDF binário direto no navegador, sem rasterizar DOM (que é o que html2canvas faz, e fica pixelado). API fica desacoplada do formato — se amanhã precisar exportar para Excel ou Markdown, é só adicionar outro módulo em features/export/, sem mexer no back.

Rodar localmente

Necessário: Docker e Git instalados.

mkdir trackrr-local && cd trackrr-local

git clone https://github.com/odgiedev/trackrr
git clone https://github.com/odgiedev/trackrr-api

cp trackrr/.env.example trackrr/.env
cp trackrr-api/.env.example trackrr-api/.env

# Terminal 1 — API
cd trackrr-api && docker compose up

# Terminal 2 — Front
cd trackrr && docker compose up