Ir para o conteúdo

Guia de Performance

Este documento descreve otimizações e boas práticas de performance para o Billings Ease.

Índice

  1. Backend
  2. Frontend Web
  3. Banco de Dados
  4. API

Backend

Otimizações de Queries

1. Eager Loading (Preload)

// ❌ N+1 queries
cycles := []Cycle{}
db.Find(&cycles)
for _, cycle := range cycles {
    db.Model(&cycle).Association("Observations").Find(&cycle.Observations)
}

// ✅ Uma query com joins
cycles := []Cycle{}
db.Preload("Observations").Find(&cycles)

2. Seleção de Campos

// ❌ Busca todos os campos
db.Find(&users)

// ✅ Busca apenas campos necessários
db.Select("id", "name", "email").Find(&users)

3. Índices no Banco

CREATE INDEX idx_cycles_user_id ON cycles(user_id);
CREATE INDEX idx_observations_cycle_id ON observations(cycle_id);
CREATE INDEX idx_observations_date ON observations(date);


Cache

Estratégias: - Cache de símbolos permitidos (5 minutos) - Cache de regras do método (configuração) - Cache de planos (público)

Implementação Futura: - Redis para cache distribuído - Cache de queries frequentes


Concorrência

Goroutines para operações independentes:

var wg sync.WaitGroup
wg.Add(2)

go func() {
    defer wg.Done()
    // Operação 1
}()

go func() {
    defer wg.Done()
    // Operação 2
}()

wg.Wait()


Frontend Web

React Query

Configuração:

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000, // 5 minutos
      cacheTime: 10 * 60 * 1000, // 10 minutos
    },
  },
})

Uso:

// Cache automático
const { data } = useQuery({
  queryKey: ['cycles'],
  queryFn: fetchCycles,
  staleTime: 5 * 60 * 1000,
})


Code Splitting

Lazy Loading de Rotas:

import { lazy, Suspense } from 'react'

const Cycles = lazy(() => import('./pages/cycles/Cycles'))

<Suspense fallback={<Loading />}>
  <Cycles />
</Suspense>


Memoização

React.memo para componentes:

export const CycleCard = React.memo(({ cycle }) => {
  return <div>{cycle.name}</div>
})

useMemo para cálculos:

const filteredCycles = useMemo(() => {
  return cycles.filter(c => c.is_active)
}, [cycles])


Imagens

Otimizações: - Lazy loading de imagens - WebP quando possível - Tamanhos responsivos


Banco de Dados

Índices

Índices essenciais:

-- Foreign keys
CREATE INDEX idx_cycles_user_id ON cycles(user_id);
CREATE INDEX idx_observations_cycle_id ON observations(cycle_id);

-- Queries frequentes
CREATE INDEX idx_observations_date ON observations(date);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_user_type ON users(user_type);


Queries Otimizadas

Evitar N+1:

// ✅ Preload
db.Preload("User").Preload("Observations").Find(&cycles)

Paginação:

db.Offset((page - 1) * limit).Limit(limit).Find(&items)


API

Rate Limiting

Implementado: - OAuth: 5 requisições / 5 minutos - Geral: 100 requisições / minuto

Configuração:

rateLimitMiddleware := ratelimit.NewRateLimitMiddleware(100, time.Minute)


Compressão

Gzip para respostas grandes:

e.Use(middleware.Gzip())


Headers de Cache

Para recursos estáticos:

e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
    Root:   "static",
    Browse: false,
    Index:  "index.html",
}))


Referências