Files
aletheia/docs/ARQUITETURA-TECNICA.md
bigtux 20a26affaa 📚 Documentação inicial do ALETHEIA
- MANUAL-PRODUTO.md: Manual do usuário final
- MANUAL-VENDAS.md: Estratégia comercial e vendas
- MANUAL-TECNICO.md: Infraestrutura e deploy
- README.md: Visão geral do projeto
2026-02-10 15:08:15 -03:00

44 KiB
Raw Blame History

🏛️ ALETHEIA — Arquitetura Técnica

Aletheia (ἀλήθεια) — "verdade" em grego. Scanner de rótulos alimentares com IA que revela a verdade sobre o que você come.


📋 Índice

  1. Visão Geral
  2. Stack Tecnológica
  3. Arquitetura de Sistema
  4. Módulos
  5. Modelo de Dados
  6. Fluxo Principal
  7. APIs Externas
  8. PWA & Câmera
  9. Monetização Técnica
  10. Performance
  11. Design & UX
  12. Roadmap
  13. Estrutura de Diretórios

Visão Geral

O usuário aponta a câmera do celular para o código de barras de um alimento. Em menos de 5 segundos, recebe:

  • Explicação simples de cada ingrediente ("como se tivesse 10 anos")
  • Score de saúde de 0 a 100 (com breakdown visual)
  • Alertas sobre ingredientes problemáticos (corantes, conservantes, excesso de sódio)
  • Alternativas mais saudáveis disponíveis na mesma categoria
  • Compatibilidade com seu perfil alimentar (alergias, dietas, restrições)

Stack Tecnológica

Backend

Componente Tecnologia Justificativa
Framework FastAPI (Python 3.12) Async nativo, tipagem forte, docs automáticas
ORM SQLAlchemy 2.0 + Alembic Migrations versionadas, async support
Banco principal PostgreSQL 16 JSONB para dados flexíveis, full-text search
Cache Redis 7 Cache de produtos, rate limiting, sessões
Task queue Celery + Redis broker Análises IA em background
Auth JWT (access + refresh tokens) Stateless, rotação de tokens
Servidor Uvicorn + Gunicorn Workers async para alta concorrência

Frontend

Componente Tecnologia Justificativa
Framework Next.js 14 (App Router) SSR, RSC, PWA-ready
UI Tailwind CSS + Radix UI Design system consistente, acessível
Estado Zustand Leve, simples, sem boilerplate
Câmera/Barcode navigator.mediaDevices + ZXing-js Scan direto no browser, sem SDK nativo
HTTP Axios + React Query (TanStack) Cache client-side, retry, optimistic updates
PWA next-pwa (Workbox) Offline support, install prompt
Animações Framer Motion Score gauge, transições suaves

Infra

Componente Tecnologia
Deploy backend Railway / Fly.io (ou VPS com Docker)
Deploy frontend Vercel
CI/CD GitHub Actions
Monitoramento Sentry (erros) + Uptime Kuma (status)
Logs Estruturados com structlog → stdout
Storage S3-compatible (imagens de produtos)

Arquitetura de Sistema

┌─────────────────────────────────────────────────────┐
│                    CLIENTE (PWA)                     │
│  Next.js 14 · Tailwind · ZXing-js · Service Worker  │
└──────────────────────┬──────────────────────────────┘
                       │ HTTPS
                       ▼
┌─────────────────────────────────────────────────────┐
│                   API GATEWAY                        │
│              FastAPI · Uvicorn · JWT                  │
│                                                      │
│  ┌──────────┐ ┌──────────┐ ┌───────────┐            │
│  │   Auth   │ │ Scanner  │ │ Produtos  │            │
│  │  Module  │ │  Module  │ │  Module   │            │
│  └──────────┘ └──────────┘ └───────────┘            │
│  ┌──────────┐ ┌──────────┐ ┌───────────┐            │
│  │ Análise  │ │  Score   │ │Alternativ.│            │
│  │   IA     │ │  Saúde   │ │  Module   │            │
│  └──────────┘ └──────────┘ └───────────┘            │
│  ┌──────────┐ ┌──────────┐ ┌───────────┐            │
│  │Histórico │ │  Perfil  │ │Gamificação│            │
│  │ Module   │ │Alimentar │ │  Module   │            │
│  └──────────┘ └──────────┘ └───────────┘            │
└───────┬──────────┬──────────────┬───────────────────┘
        │          │              │
   ┌────▼───┐ ┌───▼────┐  ┌─────▼──────┐
   │Postgres│ │ Redis  │  │  Celery    │
   │  16    │ │   7    │  │  Workers   │
   └────────┘ └────────┘  └─────┬──────┘
                                │
                    ┌───────────▼───────────┐
                    │   APIs Externas       │
                    │ • Open Food Facts     │
                    │ • OpenAI GPT-4o-mini  │
                    │ • Stripe              │
                    └───────────────────────┘

Módulos

1. Auth Module

Responsabilidade: Registro, login, gerenciamento de sessão.

POST /auth/register        → Cria conta (email + senha ou OAuth)
POST /auth/login           → Retorna access_token + refresh_token
POST /auth/refresh         → Renova access_token
POST /auth/forgot-password → Envia email de reset
POST /auth/reset-password  → Aplica nova senha
GET  /auth/me              → Retorna perfil do usuário logado
DELETE /auth/me            → Deleta conta (LGPD)
  • Senhas: bcrypt (cost factor 12)
  • Tokens: JWT RS256, access_token (15min), refresh_token (30 dias)
  • OAuth: Google e Apple Sign-In (futuro)
  • Rate limit: 5 tentativas de login por minuto por IP

2. Scanner Module

Responsabilidade: Decodificação de barcode e orquestração do fluxo de scan.

POST /scan                 → Recebe barcode, orquestra busca + análise
GET  /scan/{scan_id}       → Retorna resultado completo de um scan

Fluxo interno:

  1. Recebe barcode (EAN-13/UPC-A) do frontend
  2. Verifica rate limit do usuário (créditos)
  3. Busca produto no cache Redis → DB local → Open Food Facts
  4. Se produto novo, persiste no DB
  5. Dispara análise IA (sync se cache hit, async se primeira vez)
  6. Retorna resultado consolidado

Barcode no frontend:

  • ZXing-js para decodificação client-side
  • Fallback: envio de imagem para Google Cloud Vision API (barcode detection)
  • Suporte: EAN-13, EAN-8, UPC-A, UPC-E

3. Produtos Module

Responsabilidade: CRUD de produtos, cache e sincronização com Open Food Facts.

GET  /products/{barcode}           → Busca produto por barcode
GET  /products/{barcode}/nutrition → Dados nutricionais detalhados
POST /products/report              → Usuário reporta dados incorretos

Estratégia de cache (3 camadas):

Camada TTL Detalhes
Redis 24h Hot cache, produtos escaneados recentemente
PostgreSQL 30 dias Cache persistente, atualizado via cron
Open Food Facts Sob demanda Source of truth, fallback

Dados armazenados do produto:

  • Nome, marca, categoria
  • Lista de ingredientes (texto original)
  • Tabela nutricional (por 100g e por porção)
  • Nutri-Score (A-E) quando disponível
  • NOVA group (1-4, grau de processamento)
  • Imagens (frente, ingredientes, nutricional)
  • Alérgenos declarados

4. Análise IA Module

Responsabilidade: Análise inteligente de ingredientes via GPT-4o-mini.

POST /analysis/ingredients   → Analisa lista de ingredientes
GET  /analysis/{analysis_id} → Retorna análise completa

Prompt Engineering:

SYSTEM_PROMPT = """
Você é um nutricionista especialista que explica ingredientes 
alimentares de forma simples, como se falasse com alguém sem 
formação técnica.

Para cada ingrediente, forneça:
1. Nome popular (se diferente do técnico)
2. O que é e para que serve no produto (1-2 frases)
3. Classificação: ✅ Natural | ⚠️ Atenção | 🚫 Evitar
4. Motivo da classificação (1 frase)

Ao final, gere:
- Score de saúde (0-100) com justificativa
- Top 3 ingredientes problemáticos (se houver)
- Resumo em 1 parágrafo para leigo
"""

USER_PROMPT_TEMPLATE = """
Produto: {product_name}
Marca: {brand}
Categoria: {category}
Ingredientes: {ingredients_text}
Tabela nutricional (por 100g): {nutrition_table}
Perfil do usuário: {user_profile}  # alergias, restrições

Analise este produto.
"""

Otimizações:

  • Cache de análises por hash(ingredientes + perfil_usuario)
  • GPT-4o-mini para custo baixo (~$0.15/1M input tokens)
  • Structured output (JSON mode) para parsing confiável
  • Timeout: 10s, retry com exponential backoff
  • Fallback: análise baseada em regras se IA falhar

Response format (JSON):

{
  "ingredients": [
    {
      "name": "Açúcar invertido",
      "popular_name": "Açúcar líquido",
      "explanation": "É açúcar comum dissolvido e processado para ficar líquido. Usado para adoçar e dar textura.",
      "classification": "warning",
      "reason": "Alto índice glicêmico, contribui para picos de açúcar no sangue."
    }
  ],
  "health_score": 35,
  "score_breakdown": {
    "naturalness": 20,
    "nutrition": 40,
    "processing": 30,
    "additives": 50
  },
  "problematic_top3": ["Açúcar invertido", "Gordura vegetal hidrogenada", "Corante caramelo IV"],
  "summary": "Este produto é ultraprocessado com alto teor de açúcar...",
  "alerts": [
    {"type": "allergen", "message": "Contém glúten (incompatível com seu perfil)"}
  ]
}

5. Score de Saúde Module

Responsabilidade: Cálculo do score 0-100, combinando dados nutricionais + análise IA.

Algoritmo de Score:

def calculate_health_score(product, ai_analysis):
    score = 100
    
    # 1. Nutri-Score (peso: 25%)
    nutri_penalty = {"a": 0, "b": 5, "c": 15, "d": 25, "e": 35}
    score -= nutri_penalty.get(product.nutri_score, 20) * 0.25
    
    # 2. NOVA Group - Processamento (peso: 25%)
    nova_penalty = {1: 0, 2: 10, 3: 25, 4: 40}
    score -= nova_penalty.get(product.nova_group, 30) * 0.25
    
    # 3. Ingredientes problemáticos (peso: 25%)
    problematic_count = len(ai_analysis.problematic_ingredients)
    score -= min(problematic_count * 8, 40) * 0.25
    
    # 4. Perfil nutricional (peso: 25%)
    # Penaliza excesso de: sódio, açúcar, gordura saturada, gordura trans
    # Bonifica presença de: fibra, proteína, vitaminas
    nutrition_score = calculate_nutrition_subscore(product.nutrition)
    score -= (100 - nutrition_score) * 0.25
    
    return max(0, min(100, round(score)))

Visualização:

  • Gauge circular animado (0-100)
  • Cores: 🔴 0-30 | 🟠 31-50 | 🟡 51-70 | 🟢 71-100
  • Breakdown em 4 categorias com barras horizontais

6. Histórico Module

Responsabilidade: Timeline de scans do usuário, estatísticas e tendências.

GET  /history                → Lista scans do usuário (paginado)
GET  /history/stats          → Estatísticas gerais (score médio, total scans)
GET  /history/trends         → Evolução do score ao longo do tempo
DELETE /history/{scan_id}    → Remove scan do histórico

Funcionalidades:

  • Filtro por período, score range, categoria
  • Score médio dos últimos 7/30/90 dias
  • Gráfico de tendência (melhoria ao longo do tempo)
  • "Seus piores hábitos" (categorias com menor score médio)
  • Export CSV/PDF (premium)

7. Alternativas Module

Responsabilidade: Sugerir produtos mais saudáveis na mesma categoria.

GET /alternatives/{barcode}  → Alternativas para um produto

Lógica:

  1. Identifica categoria do produto (ex: "biscoito recheado")
  2. Busca produtos da mesma categoria no DB com score > produto atual
  3. Ordena por: score DESC, popularidade (nº de scans) DESC
  4. Retorna top 5 alternativas com comparativo

Response:

{
  "current_product": {"name": "Biscoito X", "score": 25},
  "alternatives": [
    {
      "name": "Biscoito Integral Y",
      "brand": "Marca Y",
      "score": 72,
      "score_diff": "+47",
      "highlights": ["Sem gordura trans", "Rico em fibras", "Menos açúcar"],
      "barcode": "7891234567890"
    }
  ]
}

8. Perfil Alimentar Module

Responsabilidade: Preferências, alergias e restrições do usuário.

GET  /profile/dietary        → Retorna perfil alimentar
PUT  /profile/dietary        → Atualiza perfil
GET  /profile/dietary/check/{barcode} → Verifica compatibilidade

Dados do perfil:

{
  "allergies": ["glúten", "lactose", "amendoim"],
  "intolerances": ["frutose"],
  "diet": "vegetariano",  // null, vegetariano, vegano, low-carb, keto, etc
  "avoid": ["corantes artificiais", "glutamato monossódico"],
  "goals": ["reduzir açúcar", "mais fibra"],
  "conditions": ["diabetes tipo 2", "hipertensão"]  // premium
}

Impacto no fluxo:

  • Análise IA recebe perfil como contexto
  • Alertas personalizados ("⚠️ Contém glúten — você é celíaco")
  • Score ajustado por relevância pessoal
  • Alternativas filtradas por compatibilidade

9. Gamificação Module

Responsabilidade: Engajamento via conquistas, streaks e níveis.

GET  /gamification/profile      → XP, nível, conquistas
GET  /gamification/achievements → Lista todas as conquistas
GET  /gamification/leaderboard  → Ranking semanal (premium)

Sistema de XP:

Ação XP
Scan de produto +10
Primeiro scan do dia +20 (bônus streak)
Escolher alternativa saudável +30
Completar perfil alimentar +50
Streak de 7 dias +100
Compartilhar resultado +15

Conquistas (badges):

  • 🌱 Primeiro Scan
  • 🔍 Detetive (10 scans)
  • 🏆 Expert (100 scans)
  • 🥗 Escolha Saudável (5 alternativas escolhidas)
  • 🔥 Streak Master (30 dias seguidos)
  • 📊 Analista (score médio > 70 no mês)

Modelo de Dados

Diagrama ER Simplificado

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│    users     │     │    plans     │     │dietary_profiles│
│──────────────│     │──────────────│     │──────────────│
│ id (PK)      │────▶│ id (PK)      │     │ id (PK)      │
│ email        │     │ name         │     │ user_id (FK)  │
│ password_hash│     │ price        │     │ allergies[]   │
│ name         │     │ scan_limit   │     │ diet          │
│ plan_id (FK) │     │ features{}   │     │ avoid[]       │
│ xp           │     └──────────────┘     │ goals[]       │
│ level        │                          │ conditions[]  │
│ streak_days  │                          └──────────────┘
│ created_at   │
└──────┬───────┘
       │ 1:N
       ▼
┌──────────────┐     ┌──────────────┐
│    scans     │────▶│  products    │
│──────────────│     │──────────────│
│ id (PK)      │     │ id (PK)      │
│ user_id (FK) │     │ barcode (UQ) │
│ product_id   │     │ name         │
│ analysis_id  │     │ brand        │
│ score        │     │ category     │
│ scanned_at   │     │ ingredients  │
│ source       │     │ nutrition {} │
└──────────────┘     │ nutri_score  │
                     │ nova_group   │
                     │ images {}    │
                     │ allergens[]  │
                     │ off_data {}  │
                     │ updated_at   │
                     └──────┬───────┘
                            │ 1:N
                            ▼
                     ┌──────────────┐     ┌──────────────┐
                     │ ingredients  │     │  analyses    │
                     │──────────────│     │──────────────│
                     │ id (PK)      │     │ id (PK)      │
                     │ product_id   │     │ product_id   │
                     │ name         │     │ profile_hash │
                     │ popular_name │     │ ingredients[]│
                     │ classification│    │ score        │
                     │ explanation  │     │ breakdown {} │
                     │ risk_level   │     │ summary      │
                     └──────────────┘     │ alerts[]     │
                                          │ model_version│
                                          │ created_at   │
                                          └──────────────┘

┌──────────────┐     ┌──────────────┐
│ alternatives │     │ achievements │
│──────────────│     │──────────────│
│ id (PK)      │     │ id (PK)      │
│ product_id   │     │ user_id (FK) │
│ alt_product_id│    │ badge_type   │
│ score_diff   │     │ unlocked_at  │
│ highlights[] │     └──────────────┘
└──────────────┘

┌──────────────┐
│ scan_credits │
│──────────────│
│ id (PK)      │
│ user_id (FK) │
│ date         │
│ used         │
│ limit        │
└──────────────┘

DDL Principais Tabelas

-- Usuários
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    name VARCHAR(100) NOT NULL,
    plan_id INTEGER REFERENCES plans(id) DEFAULT 1,
    xp INTEGER DEFAULT 0,
    level INTEGER DEFAULT 1,
    streak_days INTEGER DEFAULT 0,
    last_scan_date DATE,
    stripe_customer_id VARCHAR(255),
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Planos
CREATE TABLE plans (
    id SERIAL PRIMARY KEY,
    name VARCHAR(50) NOT NULL,          -- 'free', 'premium'
    price_cents INTEGER NOT NULL,        -- 0, 1490 (R$14.90)
    scan_limit_daily INTEGER NOT NULL,   -- 3, -1 (unlimited)
    features JSONB DEFAULT '{}'          -- {"export": true, "history_full": true}
);

-- Produtos
CREATE TABLE products (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    barcode VARCHAR(20) UNIQUE NOT NULL,
    name VARCHAR(500),
    brand VARCHAR(200),
    category VARCHAR(200),
    ingredients_text TEXT,
    nutrition JSONB,            -- {"energy_kcal": 250, "fat": 12, ...}
    nutri_score CHAR(1),        -- A-E
    nova_group SMALLINT,        -- 1-4
    images JSONB,               -- {"front": "url", "ingredients": "url"}
    allergens TEXT[],
    off_data JSONB,             -- Raw Open Food Facts response
    scan_count INTEGER DEFAULT 0,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_products_barcode ON products(barcode);
CREATE INDEX idx_products_category ON products(category);

-- Scans
CREATE TABLE scans (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    product_id UUID REFERENCES products(id),
    analysis_id UUID REFERENCES analyses(id),
    health_score SMALLINT,
    scanned_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_scans_user_date ON scans(user_id, scanned_at DESC);

-- Análises IA
CREATE TABLE analyses (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    product_id UUID REFERENCES products(id),
    profile_hash VARCHAR(64),    -- SHA-256 do perfil alimentar (para cache)
    ingredients_analysis JSONB,  -- Array de análises por ingrediente
    health_score SMALLINT,
    score_breakdown JSONB,       -- {"naturalness": 20, "nutrition": 40, ...}
    problematic_top3 TEXT[],
    summary TEXT,
    alerts JSONB,
    model_version VARCHAR(50),   -- "gpt-4o-mini-2024-07-18"
    tokens_used INTEGER,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_analyses_product_profile ON analyses(product_id, profile_hash);

-- Perfis Alimentares
CREATE TABLE dietary_profiles (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID UNIQUE REFERENCES users(id) ON DELETE CASCADE,
    allergies TEXT[] DEFAULT '{}',
    intolerances TEXT[] DEFAULT '{}',
    diet VARCHAR(50),
    avoid TEXT[] DEFAULT '{}',
    goals TEXT[] DEFAULT '{}',
    conditions TEXT[] DEFAULT '{}',
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

Fluxo Principal

Usuário abre app (PWA)
        │
        ▼
┌─ Tela de Scan ──────────────────────────────────────────┐
│  navigator.mediaDevices.getUserMedia({video: {          │
│    facingMode: "environment"                             │
│  }})                                                    │
│  ZXing-js detecta barcode em tempo real                 │
└────────────────────┬────────────────────────────────────┘
                     │ barcode detectado (ex: "7891000100103")
                     ▼
          POST /api/v1/scan
          { "barcode": "7891000100103" }
                     │
                     ▼
┌─ Backend ──────────────────────────────────────────────┐
│                                                         │
│  1. RATE LIMIT CHECK                                    │
│     Redis: INCR user:{id}:scans:{date}                 │
│     Se >= limite → 402 (upgrade para premium)           │
│                                                         │
│  2. BUSCA PRODUTO (3 camadas)                          │
│     a) Redis GET product:{barcode}                     │
│        → HIT? Retorna cached (< 5ms)                   │
│     b) PostgreSQL SELECT * FROM products WHERE barcode= │
│        → HIT? Retorna + atualiza Redis                  │
│     c) Open Food Facts API GET /api/v2/product/{barcode}│
│        → HIT? Persiste no DB + Redis                    │
│        → MISS? Retorna "Produto não encontrado"         │
│                                                         │
│  3. BUSCA ANÁLISE (cache por produto + perfil)         │
│     profile_hash = SHA256(user.dietary_profile)         │
│     SELECT * FROM analyses                              │
│       WHERE product_id = X AND profile_hash = Y         │
│     → HIT? Retorna cached                               │
│     → MISS? Chama GPT-4o-mini                          │
│                                                         │
│  4. ANÁLISE IA (se cache miss)                         │
│     OpenAI Chat Completion:                             │
│       model: "gpt-4o-mini"                              │
│       response_format: { type: "json_object" }          │
│       messages: [system_prompt, user_prompt]             │
│     Parse JSON → Persiste em analyses                   │
│                                                         │
│  5. CALCULA SCORE (se não veio da análise)             │
│     Combina: Nutri-Score + NOVA + IA + Nutrição        │
│                                                         │
│  6. BUSCA ALTERNATIVAS                                  │
│     SELECT FROM products WHERE category = X             │
│       AND health_score > current_score                  │
│     ORDER BY health_score DESC, scan_count DESC         │
│     LIMIT 5                                             │
│                                                         │
│  7. REGISTRA SCAN                                       │
│     INSERT INTO scans (...)                             │
│     UPDATE users SET xp = xp + 10                      │
│     Verifica achievements                               │
│                                                         │
│  8. RETORNA RESPONSE                                    │
└─────────────────────┬──────────────────────────────────┘
                      │
                      ▼
┌─ Frontend ─────────────────────────────────────────────┐
│                                                         │
│  ┌─ Resultado ────────────────────────────────────┐    │
│  │                                                 │    │
│  │  🍫 Chocolate ao Leite XYZ                     │    │
│  │  Marca ABC                                      │    │
│  │                                                 │    │
│  │  ┌────────────────┐                            │    │
│  │  │    SCORE: 32   │  🔴 Evitar                 │    │
│  │  │   ████░░░░░░   │                            │    │
│  │  └────────────────┘                            │    │
│  │                                                 │    │
│  │  ⚠️ ALERTAS                                    │    │
│  │  • Contém GLÚTEN (incompatível com seu perfil) │    │
│  │  • Alto teor de açúcar (52g/100g)              │    │
│  │                                                 │    │
│  │  📋 INGREDIENTES                               │    │
│  │  • Açúcar ← 🚫 Primeiro ingrediente = base    │    │
│  │  • Gordura vegetal hidrogenada ← 🚫 Trans      │    │
│  │  • Cacau ← ✅ Natural                          │    │
│  │  • Lecitina de soja ← ⚠️ Emulsificante        │    │
│  │  [ver todos →]                                  │    │
│  │                                                 │    │
│  │  🔄 ALTERNATIVAS MAIS SAUDÁVEIS                │    │
│  │  • Chocolate 70% Marca Y — Score: 68 (+36)     │    │
│  │  • Chocolate Orgânico Z — Score: 74 (+42)      │    │
│  │                                                 │    │
│  └─────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────┘

Sequência temporal (target < 5s)

Etapa Tempo (cache hit) Tempo (cache miss)
Decodificação barcode (client) ~200ms ~200ms
Request → Backend ~100ms ~100ms
Rate limit check (Redis) ~5ms ~5ms
Busca produto ~5ms (Redis) ~800ms (OFF API)
Busca/gera análise IA ~5ms (cache) ~2500ms (GPT)
Calcula score ~10ms ~10ms
Busca alternativas ~50ms ~50ms
Registra scan + XP ~20ms ~20ms
Response → Frontend ~100ms ~100ms
Render resultado ~200ms ~200ms
TOTAL ~700ms ~4000ms

APIs Externas

Open Food Facts

  • URL: https://world.openfoodfacts.org/api/v2/product/{barcode}.json
  • Custo: Gratuita, open source
  • Rate limit: 100 req/min (ser gentil)
  • Cobertura: ~3M produtos, boa cobertura Brasil
  • User-Agent obrigatório: Aletheia/1.0 (contato@aletheia.app)
  • Fallback: Se produto não encontrado, permitir cadastro manual (v2.0)

OpenAI GPT-4o-mini

  • Endpoint: POST https://api.openai.com/v1/chat/completions
  • Modelo: gpt-4o-mini
  • Custo estimado:
    • Input: ~500 tokens/scan × $0.15/1M = $0.000075/scan
    • Output: ~800 tokens/scan × $0.60/1M = $0.00048/scan
    • Total: ~$0.00055/scan ≈ R$0.003/scan
    • 10K scans/dia = ~R$30/dia
  • Timeout: 10 segundos
  • Retry: 3x com exponential backoff (1s, 2s, 4s)
  • Fallback: Análise baseada em regras (lista de ingredientes conhecidos)

Stripe

  • Uso: Assinaturas (Checkout + Customer Portal)
  • Webhooks: invoice.paid, customer.subscription.updated, customer.subscription.deleted
  • Planos:
    • Free: R$0 (3 scans/dia)
    • Premium: R$14,90/mês (ilimitado + features extras)

Google Cloud Vision (fallback barcode)

  • Uso: Apenas quando ZXing-js falha na decodificação client-side
  • Endpoint: POST https://vision.googleapis.com/v1/images:annotate
  • Custo: $1.50/1K imagens (primeiras 1K/mês grátis)
  • Alternativa free: QuaggaJS como segundo decoder client-side

PWA & Câmera

Service Worker (next-pwa)

// next.config.js
const withPWA = require('next-pwa')({
  dest: 'public',
  register: true,
  skipWaiting: true,
  runtimeCaching: [
    {
      urlPattern: /^https:\/\/api\.aletheia\.app\/api\/v1\/products\/.*/,
      handler: 'StaleWhileRevalidate',
      options: {
        cacheName: 'product-cache',
        expiration: { maxEntries: 200, maxAgeSeconds: 86400 }
      }
    }
  ]
});

Câmera & Barcode Scanner

// hooks/useBarcodeScan.ts
import { BrowserMultiFormatReader } from '@zxing/library';

export function useBarcodeScan() {
  const videoRef = useRef<HTMLVideoElement>(null);
  const readerRef = useRef(new BrowserMultiFormatReader());

  const startScan = async () => {
    const stream = await navigator.mediaDevices.getUserMedia({
      video: {
        facingMode: 'environment',  // câmera traseira
        width: { ideal: 1280 },
        height: { ideal: 720 },
      }
    });
    
    videoRef.current!.srcObject = stream;
    
    readerRef.current.decodeFromVideoDevice(
      undefined,  // usa device padrão
      videoRef.current!,
      (result, error) => {
        if (result) {
          // Vibra para feedback
          navigator.vibrate?.(200);
          // Envia barcode para API
          onBarcodeDetected(result.getText());
        }
      }
    );
  };

  return { videoRef, startScan, stopScan };
}

Manifest (PWA)

{
  "name": "Aletheia - Scanner de Rótulos",
  "short_name": "Aletheia",
  "description": "Descubra a verdade sobre o que você come",
  "start_url": "/",
  "display": "standalone",
  "orientation": "portrait",
  "theme_color": "#16A34A",
  "background_color": "#FFFFFF",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}

Monetização Técnica

Planos

Feature Free Premium (R$14,90/mês)
Scans por dia 3 Ilimitado
Histórico Últimos 7 dias Completo
Análise IA Básica Detalhada + perfil
Alternativas Top 2 Top 5 + comparativo
Perfil alimentar Alergias apenas Completo (condições)
Export (CSV/PDF)
Leaderboard
Sem anúncios

Fluxo Stripe

Usuário clica "Upgrade Premium"
        │
        ▼
POST /billing/checkout-session
  → Stripe Checkout Session (mode: subscription)
  → Redirect para Stripe hosted page
        │
        ▼
Stripe processa pagamento
        │
        ▼
Webhook: invoice.paid
  → Backend atualiza user.plan_id = 2
  → Redis: SET user:{id}:plan premium
        │
        ▼
Usuário retorna ao app → plano ativo

Sistema de Créditos

async def check_scan_credits(user_id: str) -> bool:
    today = date.today().isoformat()
    key = f"credits:{user_id}:{today}"
    
    # Usuário premium → sempre permitido
    if await is_premium(user_id):
        return True
    
    used = await redis.get(key)
    if used and int(used) >= DAILY_FREE_LIMIT:  # 3
        return False
    
    await redis.incr(key)
    await redis.expire(key, 86400)  # expira em 24h
    return True

Performance

Metas

Métrica Target Estratégia
Scan → resultado < 5s (cold) / < 1s (cached) Cache 3 camadas
TTFB (first byte) < 200ms Edge deploy (Vercel)
LCP < 2.5s SSR + lazy load imagens
Bundle size < 150KB (gzipped) Tree shaking, dynamic imports
Lighthouse PWA > 90 Service worker, manifest, HTTPS
API p95 latency < 500ms (cached) Redis, connection pooling
Uptime 99.9% Health checks, auto-restart

Estratégias de Cache

┌─────────────────────────────────────────────┐
│           Cache Architecture                 │
│                                              │
│  Client (React Query)                        │
│    └─ staleTime: 5min                       │
│    └─ Produtos escaneados ficam em memória   │
│                                              │
│  Service Worker (Workbox)                    │
│    └─ StaleWhileRevalidate para /products    │
│    └─ CacheFirst para imagens estáticas      │
│                                              │
│  Redis (Server)                              │
│    └─ product:{barcode} → TTL 24h           │
│    └─ analysis:{product_id}:{hash} → TTL 7d │
│    └─ user:{id}:credits:{date} → TTL 24h   │
│                                              │
│  PostgreSQL                                  │
│    └─ Source of truth, updated via cron      │
│    └─ Produtos atualizados a cada 30 dias   │
└─────────────────────────────────────────────┘

Otimizações Backend

  • Connection pooling: SQLAlchemy async pool (min=5, max=20)
  • Bulk operations: Batch insert para alternativas
  • Índices: barcode (B-tree), category (B-tree), scans por user+date
  • Async everywhere: FastAPI + httpx (Open Food Facts) + asyncpg
  • Streaming response: Para análises longas, usar SSE (Server-Sent Events)

Design & UX

Identidade Visual

Elemento Especificação
Nome Aletheia (ἀλήθεια = verdade)
Logo Olho grego estilizado (Nazar/Mati) com íris em forma de barcode
Cores primárias Verde #16A34A (saúde) + Branco #FFFFFF (clean)
Cores secundárias Cinza #6B7280 (texto) + Verde claro #DCFCE7 (backgrounds)
Cores de score 🔴 #EF4444 · 🟠 #F97316 · 🟡 #EAB308 · 🟢 #22C55E
Tipografia Inter (UI) + Plus Jakarta Sans (headings)
Ícones Lucide Icons (consistente, leve)
Bordas rounded-xl (16px), sombras suaves
Espaçamento Grid 8px, padding generoso

Telas Principais

1. SPLASH / ONBOARDING
   - Logo Aletheia (olho grego animado)
   - "Descubra a verdade sobre o que você come"
   - 3 slides: Scan → Entenda → Escolha melhor
   - CTA: "Começar" → setup perfil alimentar

2. HOME
   - Header: logo + streak 🔥 + XP bar
   - Botão central grande: "📷 Escanear"
   - Últimos scans (horizontal scroll)
   - Score médio da semana (mini gauge)
   - Tip do dia (IA)

3. SCANNER
   - Câmera fullscreen com overlay
   - Guia visual: "Aponte para o código de barras"
   - Auto-detect + vibração
   - Loading: animação do olho "analisando"

4. RESULTADO
   - Score gauge animado (destaque principal)
   - Alertas personalizados (cards vermelhos/amarelos)
   - Lista de ingredientes (expandível, com ícones)
   - Alternativas (cards horizontais)
   - Botões: Salvar | Compartilhar | Escanear outro

5. HISTÓRICO
   - Timeline vertical com mini scores
   - Filtros: período, categoria, score
   - Gráfico de tendência (line chart)

6. PERFIL
   - Dados pessoais
   - Perfil alimentar (alergias, dieta, goals)
   - Plano (free/premium)
   - Gamificação (nível, badges, streak)
   - Configurações

Micro-interações

  • Scan detectado: Vibração + flash verde + som sutil
  • Score reveal: Animação circular de 0 até valor final (1.5s)
  • Ingrediente tap: Expande com animação suave (Framer Motion)
  • Achievement unlocked: Toast animado com confetti
  • Pull to refresh: Animação do olho grego piscando

Roadmap

MVP — Semanas 1-3

Semana 1: Infraestrutura + Scanner

  • Setup monorepo (turborepo ou pasta separada)
  • Backend: FastAPI boilerplate, auth JWT, migrations
  • Frontend: Next.js 14, PWA setup, layout base
  • Scanner: câmera + ZXing-js funcionando
  • Integração Open Food Facts (busca básica)

Semana 2: IA + Core Features

  • Análise IA com GPT-4o-mini
  • Score de saúde (algoritmo v1)
  • Tela de resultado completa
  • Cache Redis para produtos + análises
  • Histórico básico (lista de scans)

Semana 3: Polish + Deploy

  • Design system (cores, tipografia, componentes)
  • Rate limiting (3 scans/dia free)
  • Alternativas básicas
  • Error handling e loading states
  • Deploy: Vercel (front) + Railway (back)
  • Testes E2E dos fluxos principais

Entregáveis MVP:

  • PWA funcional no celular
  • Scan → resultado com score + ingredientes explicados
  • 3 scans grátis por dia
  • Histórico dos últimos 7 dias

v1.0 — Semanas 4-6

  • Perfil alimentar completo
  • Alertas personalizados baseados no perfil
  • Stripe integration (premium)
  • Gamificação (XP, streaks, badges)
  • Onboarding flow
  • Push notifications (Web Push API)
  • Offline mode (scans salvos localmente)

v2.0 — Semanas 7-10

  • Compartilhar resultado (social cards)
  • Comparar 2 produtos lado a lado
  • Leaderboard semanal
  • OCR de ingredientes (foto da lista quando sem barcode)
  • Cadastro de produtos por usuários
  • API pública para desenvolvedores
  • Internacionalização (PT-BR, EN, ES)

v3.0 — Futuro

  • App nativo (React Native ou Capacitor)
  • Integração com supermercados (preços)
  • Scan de cardápios de restaurantes
  • Diário alimentar com score diário
  • Recomendações de receitas saudáveis
  • Integração com Apple Health / Google Fit

Estrutura de Diretórios

aletheia/
├── backend/
│   ├── app/
│   │   ├── __init__.py
│   │   ├── main.py                 # FastAPI app factory
│   │   ├── config.py               # Settings (pydantic-settings)
│   │   ├── database.py             # SQLAlchemy async engine
│   │   ├── dependencies.py         # Shared deps (get_db, get_current_user)
│   │   ├── models/                 # SQLAlchemy models
│   │   │   ├── user.py
│   │   │   ├── product.py
│   │   │   ├── scan.py
│   │   │   ├── analysis.py
│   │   │   ├── dietary_profile.py
│   │   │   └── plan.py
│   │   ├── schemas/                # Pydantic schemas
│   │   │   ├── user.py
│   │   │   ├── product.py
│   │   │   ├── scan.py
│   │   │   └── analysis.py
│   │   ├── routers/                # API routes
│   │   │   ├── auth.py
│   │   │   ├── scan.py
│   │   │   ├── products.py
│   │   │   ├── analysis.py
│   │   │   ├── history.py
│   │   │   ├── alternatives.py
│   │   │   ├── profile.py
│   │   │   ├── gamification.py
│   │   │   └── billing.py
│   │   ├── services/               # Business logic
│   │   │   ├── auth_service.py
│   │   │   ├── scan_service.py
│   │   │   ├── product_service.py
│   │   │   ├── ai_service.py
│   │   │   ├── score_service.py
│   │   │   ├── alternatives_service.py
│   │   │   ├── credits_service.py
│   │   │   └── gamification_service.py
│   │   ├── integrations/           # External APIs
│   │   │   ├── open_food_facts.py
│   │   │   ├── openai_client.py
│   │   │   └── stripe_client.py
│   │   └── utils/
│   │       ├── cache.py
│   │       ├── security.py
│   │       └── prompts.py
│   ├── alembic/                    # Migrations
│   ├── tests/
│   ├── Dockerfile
│   ├── requirements.txt
│   └── pyproject.toml
│
├── frontend/
│   ├── src/
│   │   ├── app/                    # Next.js App Router
│   │   │   ├── layout.tsx
│   │   │   ├── page.tsx            # Home
│   │   │   ├── scan/page.tsx       # Scanner
│   │   │   ├── result/[id]/page.tsx
│   │   │   ├── history/page.tsx
│   │   │   ├── profile/page.tsx
│   │   │   └── premium/page.tsx
│   │   ├── components/
│   │   │   ├── ui/                 # Design system
│   │   │   ├── scanner/
│   │   │   │   ├── CameraView.tsx
│   │   │   │   └── BarcodeOverlay.tsx
│   │   │   ├── result/
│   │   │   │   ├── ScoreGauge.tsx
│   │   │   │   ├── IngredientList.tsx
│   │   │   │   ├── AlertCards.tsx
│   │   │   │   └── AlternativeCards.tsx
│   │   │   └── gamification/
│   │   │       ├── XPBar.tsx
│   │   │       └── BadgeGrid.tsx
│   │   ├── hooks/
│   │   │   ├── useBarcodeScan.ts
│   │   │   ├── useAuth.ts
│   │   │   └── useScan.ts
│   │   ├── lib/
│   │   │   ├── api.ts              # Axios instance
│   │   │   └── utils.ts
│   │   └── stores/
│   │       ├── authStore.ts        # Zustand
│   │       └── scanStore.ts
│   ├── public/
│   │   ├── manifest.json
│   │   ├── sw.js
│   │   └── icons/
│   ├── next.config.js
│   ├── tailwind.config.ts
│   └── package.json
│
├── docs/
│   ├── ARQUITETURA-TECNICA.md      # Este arquivo
│   ├── API.md                      # Documentação da API
│   └── DEPLOY.md                   # Guia de deploy
│
├── docker-compose.yml              # PostgreSQL + Redis + Backend
├── .env.example
└── README.md

Estimativa de Custos (10K usuários ativos)

Serviço Custo/mês
Vercel (frontend) Free (hobby) ou $20 (pro)
Railway (backend + DB + Redis) ~$20-40
OpenAI GPT-4o-mini (~30K scans/mês) R$90 ($17)
Stripe (2.5% + fees) Variável
Domínio ~R$40/ano
Total estimado ~R$250-400/mês

Break-even: ~25-30 assinantes premium (R$14,90 × 30 = R$447)


"Aletheia — porque você merece saber a verdade sobre o que come." 👁️