Analytics e BI para apps: do Google Analytics ao dashboard executivo com Mixpanel + Metabase

Stack completo de analytics: eventos, funnels, cohorts e dashboards. De R$ 0 (MVP) a R$ 2K (scale) com ROI 420% em decisões data-driven.

Decisões sem dados = achismo. Achismo = desperdício de R$ 50-200K em features erradas.

Com analytics estruturado:

  • Identifica gargalos (conversão cai 40% no step 3 do onboarding)
  • Valida hipóteses (feature X aumentou retenção em 25%)
  • Prioriza roadmap (70% dos usuários pedem feature Y)

Este artigo mostra stack completo de analytics, do básico ao avançado, com código e custos.

As 4 camadas de analytics

Camada 1: Web analytics (tráfego e pageviews)

Ferramenta: Google Analytics 4 (GA4) - Grátis.

O que mede:

  • Visitantes únicos
  • Pageviews
  • Taxa de rejeição
  • Origem do tráfego (Google, direct, social)
  • Dispositivos (desktop 65%, mobile 35%)

Setup (Next.js):

// app/layout.tsx
import Script from 'next/script'

export default function RootLayout({ children }: Props) {
  const GA_ID = process.env.NEXT_PUBLIC_GA_ID

  return (
    <html>
      <head>
        <Script
          src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`}
          strategy="afterInteractive"
        />
        <Script id="google-analytics" strategy="afterInteractive">
          {`
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', '${GA_ID}', {
              page_path: window.location.pathname,
            });
          `}
        </Script>
      </head>
      <body>{children}</body>
    </html>
  )
}

Eventos customizados:

// lib/analytics/ga.ts
export function trackEvent(
  eventName: string,
  params?: Record<string, any>
) {
  if (typeof window !== 'undefined' && window.gtag) {
    window.gtag('event', eventName, params)
  }
}

// Uso
trackEvent('purchase', {
  transaction_id: 'T12345',
  value: 99.90,
  currency: 'BRL'
})

trackEvent('sign_up', {
  method: 'Google'
})

Limitações do GA4:

  • ❌ Não rastreia usuários logados (apenas sessões)
  • ❌ Funnels básicos
  • ❌ Sem cohort analysis
  • ❌ Dados agregados (não permite queries customizadas)

Quando usar: Tráfego de marketing, SEO, conversão de landing pages.

Camada 2: Product analytics (comportamento do usuário)

Ferramenta: Mixpanel ou Amplitude.

O que mede:

  • Ações específicas (clicou botão X, completou onboarding)
  • Funnels (quantos % passam de step A → B → C)
  • Retention (quantos % voltam após 7 dias?)
  • Cohorts (usuários que se cadastraram em janeiro vs fevereiro)

Setup (Mixpanel + Next.js):

npm install mixpanel-browser
// lib/analytics/mixpanel.ts
import mixpanel from 'mixpanel-browser'

const MIXPANEL_TOKEN = process.env.NEXT_PUBLIC_MIXPANEL_TOKEN!

export function initMixpanel() {
  mixpanel.init(MIXPANEL_TOKEN, {
    debug: process.env.NODE_ENV === 'development',
    track_pageview: true,
    persistence: 'localStorage'
  })
}

// Identifica usuário (após login)
export function identifyUser(userId: string, properties?: Record<string, any>) {
  mixpanel.identify(userId)

  if (properties) {
    mixpanel.people.set(properties)
  }
}

// Rastreia evento
export function trackEvent(eventName: string, properties?: Record<string, any>) {
  mixpanel.track(eventName, properties)
}

// Incrementa contador (ex: "bookings_completed")
export function incrementMetric(metric: string, value: number = 1) {
  mixpanel.people.increment(metric, value)
}

Eventos essenciais (marketplace):

// Após cadastro
trackEvent('Sign Up', {
  method: 'Email', // ou 'Google', 'Facebook'
  role: 'Teacher' // ou 'Student'
})
identifyUser(user.id, {
  $email: user.email,
  $name: user.name,
  plan: 'Free',
  createdAt: user.createdAt
})

// Onboarding completado
trackEvent('Onboarding Completed', {
  steps_completed: 3,
  time_spent_seconds: 127
})

// Primeira transação
trackEvent('First Booking', {
  value: 120,
  category: 'Math',
  payment_method: 'Credit Card'
})
incrementMetric('bookings_completed', 1)

// Feature usage
trackEvent('Feature Used', {
  feature: 'Calendar Sync',
  context: 'Settings'
})

Funnel de conversão (visualização no Mixpanel):

Sign Up → 1.000 usuários (100%)
  ↓ -40%
Onboarding Completed → 600 (60%)
  ↓ -50%
First Booking → 300 (30% total, 50% dos que completaram onboarding)
  ↓ -20%
Second Booking → 240 (24% total)

Cohort retention (quanto % volta após N dias):

CohortD0D1D7D30
Jan 2026100%42%28%18%
Fev 2026100%48%35%25%
Mar 2026100%52%40%30%

Análise: Retenção melhorando mês a mês (onboarding aprimorado funcionou).

Custo:

  • Mixpanel: R$ 0 (até 100K eventos/mês) → R$ 600-1.200 (escala)
  • Amplitude: R$ 0 (até 10M eventos/mês) → R$ 800-2K (recursos avançados)

Camada 3: Business intelligence (dashboards executivos)

Ferramenta: Metabase (self-hosted) ou Looker.

O que mede:

  • MRR (Monthly Recurring Revenue)
  • Churn rate
  • LTV/CAC
  • ARR growth
  • Burn rate

Setup (Metabase self-hosted):

# Docker Compose
docker run -d -p 3000:3000 \
  -e "MB_DB_TYPE=postgres" \
  -e "MB_DB_DBNAME=metabase" \
  -e "MB_DB_PORT=5432" \
  -e "MB_DB_USER=metabase" \
  -e "MB_DB_PASS=secret" \
  -e "MB_DB_HOST=db" \
  --name metabase \
  metabase/metabase

Queries SQL customizadas:

-- MRR (Monthly Recurring Revenue)
SELECT
  DATE_TRUNC('month', created_at) AS month,
  COUNT(DISTINCT user_id) AS active_subscribers,
  SUM(amount) AS mrr
FROM subscriptions
WHERE status = 'active'
GROUP BY month
ORDER BY month DESC;

-- Churn rate mensal
WITH monthly_users AS (
  SELECT
    DATE_TRUNC('month', created_at) AS month,
    user_id
  FROM subscriptions
  WHERE status = 'active'
)
SELECT
  month,
  COUNT(DISTINCT user_id) AS users_start,
  COUNT(DISTINCT CASE WHEN canceled_at IS NOT NULL THEN user_id END) AS churned,
  ROUND(
    100.0 * COUNT(DISTINCT CASE WHEN canceled_at IS NOT NULL THEN user_id END) /
    COUNT(DISTINCT user_id),
    2
  ) AS churn_rate
FROM monthly_users
GROUP BY month
ORDER BY month DESC;

-- LTV/CAC
SELECT
  AVG(lifetime_value) AS avg_ltv,
  AVG(acquisition_cost) AS avg_cac,
  ROUND(AVG(lifetime_value) / NULLIF(AVG(acquisition_cost), 0), 2) AS ltv_cac_ratio
FROM (
  SELECT
    user_id,
    SUM(amount) AS lifetime_value,
    (SELECT spend / new_users FROM marketing_monthly WHERE month = DATE_TRUNC('month', u.created_at)) AS acquisition_cost
  FROM payments p
  JOIN users u ON p.user_id = u.id
  GROUP BY user_id, u.created_at
) AS ltv_calc;

Dashboard exemplo (métricas-chave):

// Componente custom para embed Metabase
export function MetabaseDashboard({ dashboardId }: Props) {
  const iframeUrl = `https://metabase.suaempresa.com/embed/dashboard/${dashboardId}?token=${embedToken}`

  return (
    <div className="dashboard-container">
      <iframe
        src={iframeUrl}
        width="100%"
        height="800"
        frameBorder="0"
        allowTransparency
      />
    </div>
  )
}

Métricas no dashboard executivo:

  • 💰 MRR: R$ 87,4K (+18% vs mês anterior)
  • 📉 Churn: 4,2% (meta: menor que 5%)
  • 📈 LTV/CAC: 5,8x (meta: maior que 3x)
  • 👥 Usuários ativos: 1.247 (+12%)
  • 🔄 NPS: 42 (promotores 68%, detratores 15%)

Custo:

  • Metabase self-hosted: R$ 60/mês (servidor)
  • Metabase cloud: R$ 500-2K/mês
  • Looker: R$ 2K-8K/mês (enterprise)

Camada 4: Alertas e anomalias

Ferramenta: Better Stack Monitors ou Datadog.

O que mede:

  • Quedas abruptas (conversão caiu 30% hoje)
  • Picos anormais (churn subiu 2x)
  • Metas não atingidas (MRR menor que R$ 80K)

Setup (Better Stack):

// lib/monitoring/alerts.ts
export async function checkMetrics() {
  const today = new Date()
  const yesterday = subDays(today, 1)

  // Verifica conversão do onboarding
  const conversionToday = await getOnboardingConversion(today)
  const conversionYesterday = await getOnboardingConversion(yesterday)

  if (conversionToday < conversionYesterday * 0.7) {
    // Queda de 30%+
    await sendAlert({
      severity: 'HIGH',
      title: 'Onboarding conversion dropped 30%',
      message: `Today: ${conversionToday}%, Yesterday: ${conversionYesterday}%`,
      channel: 'slack'
    })
  }

  // Verifica churn
  const churnRate = await getMonthlyChurn()
  if (churnRate > 0.05) {
    // maior que 5%
    await sendAlert({
      severity: 'MEDIUM',
      title: 'Churn rate above target',
      message: `Current: ${churnRate * 100}%, Target: 5%`,
      channel: 'email'
    })
  }
}

// Roda diariamente (cron)
cron.schedule('0 9 * * *', checkMetrics) // 9h todos os dias

Alertas Slack:

// lib/monitoring/slack.ts
import { WebClient } from '@slack/web-api'

const slack = new WebClient(process.env.SLACK_TOKEN)

export async function sendSlackAlert(message: string) {
  await slack.chat.postMessage({
    channel: '#alerts',
    text: message,
    blocks: [
      {
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: `🚨 *Alert*\n${message}`
        }
      },
      {
        type: 'actions',
        elements: [
          {
            type: 'button',
            text: { type: 'plain_text', text: 'Ver Dashboard' },
            url: 'https://metabase.suaempresa.com/dashboard/1'
          }
        ]
      }
    ]
  })
}

Stack recomendado por estágio

MVP (0-100 usuários, R$ 0/mês)

  • Web: Google Analytics 4 (grátis)
  • Product: Mixpanel free tier (grátis)
  • BI: SQL queries direto no banco (grátis)
  • Alertas: Script custom + email (grátis)

Total: R$ 0

Growth (100-1K usuários, R$ 600-1.2K/mês)

  • Web: GA4 (grátis)
  • Product: Mixpanel Startup (R$ 600/mês)
  • BI: Metabase self-hosted (R$ 60/mês servidor)
  • Alertas: Better Stack (R$ 180/mês)

Total: R$ 840/mês

Scale (1K-10K usuários, R$ 1.8-3K/mês)

  • Web: GA4 (grátis)
  • Product: Amplitude Growth (R$ 1.200/mês)
  • BI: Metabase Cloud (R$ 800/mês)
  • Alertas: Datadog (R$ 1.000/mês)

Total: R$ 3K/mês

Eventos essenciais por tipo de produto

SaaS B2B

// Lifecycle events
'User Signed Up'
'Trial Started'
'Onboarding Completed'
'First Value Achieved' // Ex: "First report generated"
'Trial Converted'
'Subscription Upgraded'
'Subscription Downgraded'
'Subscription Canceled'

// Feature usage
'Feature Used' { feature: 'Advanced Reports', context: 'Dashboard' }
'Export Completed' { format: 'PDF', rows: 1247 }
'Integration Connected' { integration: 'Slack' }

// Engagement
'Daily Active' // Apenas trackeia 1x/dia
'Weekly Report Viewed'

Marketplace

// Supply side (professores, prestadores)
'Provider Signed Up'
'Profile Completed'
'First Service Listed'
'Booking Accepted'
'Booking Completed'
'Rating Received'

// Demand side (clientes)
'Customer Signed Up'
'Search Performed' { query: 'math tutor' }
'Provider Viewed'
'Booking Requested'
'Payment Completed'

// Platform
'GMV Generated' { amount: 120, commission: 24 }
'Dispute Opened'

E-commerce

'Product Viewed' { product_id, category, price }
'Added to Cart' { product_id, quantity }
'Checkout Started'
'Payment Info Added'
'Order Completed' { order_id, total, items }
'Product Returned'

Análises avançadas

A/B testing (feature flags)

// lib/ab-testing/split.ts
export function getUserVariant(userId: string, experiment: string): 'A' | 'B' {
  const hash = createHash('md5').update(userId + experiment).digest('hex')
  const num = parseInt(hash.substring(0, 8), 16)

  return num % 2 === 0 ? 'A' : 'B'
}

// Uso
const variant = getUserVariant(user.id, 'new-onboarding')

if (variant === 'A') {
  return <OnboardingOld />
} else {
  return <OnboardingNew />
}

// Trackeia
trackEvent('Experiment Viewed', {
  experiment: 'new-onboarding',
  variant
})

Análise de resultados:

-- Comparação conversão A vs B
SELECT
  properties->>'variant' AS variant,
  COUNT(DISTINCT user_id) AS users,
  COUNT(DISTINCT CASE WHEN event_name = 'Onboarding Completed' THEN user_id END) AS converted,
  ROUND(
    100.0 * COUNT(DISTINCT CASE WHEN event_name = 'Onboarding Completed' THEN user_id END) /
    COUNT(DISTINCT user_id),
    2
  ) AS conversion_rate
FROM events
WHERE properties->>'experiment' = 'new-onboarding'
GROUP BY variant;

-- Resultado:
-- Variant A: 42% conversão (600 usuários)
-- Variant B: 58% conversão (600 usuários)
-- Winner: B (+38% vs A)

Cohort analysis (retenção por coorte)

-- Retenção por coorte de cadastro
WITH cohorts AS (
  SELECT
    user_id,
    DATE_TRUNC('month', created_at) AS cohort_month
  FROM users
),
activity AS (
  SELECT
    user_id,
    DATE_TRUNC('month', event_time) AS activity_month
  FROM events
  WHERE event_name = 'Daily Active'
)
SELECT
  cohorts.cohort_month,
  COUNT(DISTINCT cohorts.user_id) AS cohort_size,
  COUNT(DISTINCT CASE WHEN activity_month = cohort_month THEN activity.user_id END) AS month_0,
  COUNT(DISTINCT CASE WHEN activity_month = cohort_month + INTERVAL '1 month' THEN activity.user_id END) AS month_1,
  COUNT(DISTINCT CASE WHEN activity_month = cohort_month + INTERVAL '2 months' THEN activity.user_id END) AS month_2
FROM cohorts
LEFT JOIN activity ON cohorts.user_id = activity.user_id
GROUP BY cohorts.cohort_month
ORDER BY cohorts.cohort_month DESC;

ROI de analytics

Case real: SaaS de gestão para restaurantes.

Antes de analytics estruturado:

  • Decisões por “achismo”
  • Feature prioritization por “quem grita mais alto”
  • Não sabia onde usuários abandonavam
  • Churn: 8,5%/mês (não sabia por quê)

Após 3 meses com analytics:

Descoberta 1: 62% abandonavam no step 2 do onboarding (conectar sistema de pagamento).

  • Ação: Simplificou onboarding (conexão opcional)
  • Resultado: Conversão 42% → 68% (+62%)

Descoberta 2: Usuários que usavam feature “Relatórios” tinham churn 70% menor.

  • Ação: Adicionou tutorial in-app para relatórios
  • Resultado: Uso da feature 18% → 45%

Descoberta 3: 80% dos churn aconteciam após 2º mês (não viam valor).

  • Ação: Email sequence no mês 2 mostrando ROI (“você economizou R$ X”)
  • Resultado: Churn 8,5% → 4,2% (-51%)

ROI total:

  • Investimento analytics: R$ 12K (setup) + R$ 840/mês
  • Ganho anual (redução churn): R$ 180K
  • ROI: 420% (ano 1)

Checklist de analytics

Setup inicial:

  • GA4 instalado (tráfego web)
  • Mixpanel/Amplitude instalado (product analytics)
  • Eventos-chave mapeados (sign up, onboarding, conversão)
  • Identify de usuários (após login)
  • Funnels configurados (sign up → ativação → conversão)

Análises mensais:

  • Revisar funnels (onde há drop-off?)
  • Analisar retention (cohorts melhorando ou piorando?)
  • Feature usage (quais features correlacionam com retenção?)
  • Churn analysis (por que usuários cancelam?)

Cultura data-driven:

  • Decisões baseadas em dados (não opinião)
  • A/B testa features antes de lançar
  • Dashboard executivo revisado semanalmente
  • Alertas configurados para anomalias

Próximos passos

  1. Instalar GA4 e Mixpanel
  2. Mapear 10-15 eventos essenciais
  3. Implementar tracking no código
  4. Criar 3 funnels principais
  5. Configurar cohort analysis
  6. Setup Metabase (dashboards)
  7. Revisar dados toda semana

Lembre-se: Analytics não é “nice to have”. É diferença entre crescer rápido e morrer lento.

Pronto para sair do manual?

Agende o diagnóstico gratuito. Vamos mapear o gargalo, estimar o impacto e definir o primeiro resultado mensurável.

Você sai com clareza — não com um pitch de vendas.