🏗️ Infraestrutura & Escalabilidade

Load Balancing, Cache e Redundância de Banco de Dados — Cloudflare Stack

Command Agendamento • Fevereiro 2026

📐 1. Arquitetura Atual

Stack de Tecnologias em Produção

Componente Tecnologia Plano Atual
API / Backend Cloudflare Workers + Hono 4.x Free / Paid ($5/mês)
Banco Principal (Grades/Slots) Cloudflare D1 — command-agendamento-db Free / Paid
Banco Histórico (Agendamentos) Cloudflare D1 — command-agendamento-historico Free / Paid
Banco Usuários (Pacientes/Admin) Cloudflare D1 — command-agendamento-usuarios Free / Paid
Banco Parâmetros (Config) Cloudflare D1 — command-agendamento-parametros Free / Paid
Storage de Arquivos Cloudflare R2 — command-agenda Free (10GB)
Frontend ADM Cloudflare Pages + React 18 + Vite Free
Frontend Paciente Cloudflare Pages + React 18 + Vite Free
Documentação Cloudflare Pages (HTML estático) Free
WhatsApp (OTP/Notificações) Whapi.cloud (API externa) Variável
E-mail Mailjet (API externa) Free / Paid

🗺️ Diagrama da Arquitetura Atual

┌──────────────────────────────────────────────────────────────────────┐ │ CLOUDFLARE NETWORK (Global) │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Pages (ADM) │ │ Pages (Paciente)│ │ Pages (Docs) │ │ │ │ React + Vite │ │ React + Vite │ │ HTML Estático │ │ │ └────────┬────────┘ └────────┬────────┘ └─────────────────┘ │ │ │ │ │ │ └──────────┬──────────┘ │ │ ▼ │ │ ┌───────────────────────────────────────┐ │ │ │ Cloudflare Workers (Hono 4.x) │───────┐ │ │ │ command-agenda-api │ │ │ │ │ • JWT Auth (8h admin / 24h pac.) │ │ │ │ │ • RBAC 5 níveis │ │ │ │ │ • CORS multi-origin │ ▼ │ │ │ • 15 módulos de rotas │ ┌─────────┐ │ │ └──┬──────┬──────┬──────┬───────────────┘ │ R2 │ │ │ │ │ │ │ │ Storage │ │ │ ▼ ▼ ▼ ▼ └─────────┘ │ │ ┌─────┐┌─────┐┌─────┐┌─────┐ │ │ │ D1 ││ D1 ││ D1 ││ D1 │ │ │ │ DB ││HIST.││USRS.││PARM.│ │ │ └─────┘└─────┘└─────┘└─────┘ │ └──────────────────────────────────────────────────────────────────────┘ │ ┌────────────┴────────────┐ ▼ ▼ ┌───────────┐ ┌───────────┐ │ Whapi │ │ Mailjet │ │(WhatsApp) │ │ (E-mail) │ └───────────┘ └───────────┘
💡 Ponto Importante Cloudflare Workers já possuem load balancing nativo — o código é distribuído em 300+ data centers globalmente. Não há um servidor único. O foco deste documento é em cache de dados, redundância de banco e otimizações de performance.

⚖️ 2. Load Balancing

✅ O que já temos (nativo)

Cloudflare Workers já funciona como um load balancer global por design:

  • Anycast Network: Requisições são roteadas automaticamente para o data center mais próximo do usuário (300+ PoPs no mundo)
  • Auto-scaling: Workers escalam automaticamente — não há conceito de "instância" ou "servidor". Cada request roda em isolate V8 separado
  • Zero Cold Start: Workers inicializam em menos de 5ms, sem cold start significativo
  • Sem ponto único de falha: Se um data center falhar, o tráfego é automaticamente roteado para o próximo
✅ Conclusão sobre Load Balancing Para a arquitetura atual com Workers, não é necessário configurar um load balancer adicional. Isso já é feito nativamente pela rede Cloudflare. Um load balancer dedicado (Cloudflare Load Balancing) seria necessário apenas se houvesse servidores de origem tradicionais (VPS, EC2, etc).

📊 Cloudflare Load Balancing (quando seria necessário?)

O serviço de Load Balancing da Cloudflare seria relevante apenas em cenários onde:

  • A API migra para servidores de origem (VPS, containers)
  • Integração com serviços externos que precisam de failover (ex: API de WhatsApp backup)
  • Multi-cloud ou hybrid-cloud com backends em AWS/GCP/Azure
Recurso Custo Necessário Hoje?
Load Balancing (básico) $5/mês + $0.50/500K health checks NÃO
Load Balancing (multi-region) $5/mês + $5/origin NÃO
Health Checks Inclusos no Workers JÁ TEMOS

🗄️ 3. Estratégia de Cache

⚠️ Estado Atual A API atualmente não possui nenhuma camada de cache. Cada requisição faz consulta direta ao D1. Implementar cache pode reduzir latência em até 80% para dados que não mudam frequentemente.

3.1 🟢 Cache API (Workers Runtime) — Recomendado como 1ª etapa

API nativa do Cloudflare Workers que permite armazenar respostas HTTP no edge mais próximo do usuário.

Característica Detalhe
Custo GRÁTIS — incluído no Workers
Escopo Por data center (não replica globalmente)
TTL Controlado via Cache-Control headers
Invalidação Manual via cache.delete()
Restrição Funciona apenas em domínios customizados (não *.workers.dev)

Endpoints ideais para Cache API:

Endpoint TTL Sugerido Motivo
GET /api/especialidades 1 hora Raramente muda
GET /api/unidades 1 hora Raramente muda
GET /api/consultorios 30 min Baixa frequência de mudança
GET /api/tipos-slot 1 hora Configuração estática
GET /api/medicos 15 min Pode mudar com cadastros
GET /api/medicos/:id 10 min Dados do médico individual
GET /api/cotas 30 min Cotas por especialidade
GET /api/slots/disponiveis 2 min Muda com agendamentos

Exemplo de implementação:

// middleware/cache.js — Middleware de Cache para Hono export function cacheMiddleware(ttlSeconds = 300) { return async (c, next) => { const cache = caches.default; const cacheKey = new Request(c.req.url, { method: 'GET' }); // Apenas cachear GET requests if (c.req.method !== 'GET') { return next(); } // Tentar retornar do cache const cached = await cache.match(cacheKey); if (cached) { return cached; } // Executar handler e cachear resposta await next(); const response = c.res.clone(); response.headers.set('Cache-Control', `public, max-age=${ttlSeconds}`); c.executionCtx.waitUntil(cache.put(cacheKey, response)); }; } // Uso nas rotas: app.get('/api/especialidades', cacheMiddleware(3600), handler); app.get('/api/unidades', cacheMiddleware(3600), handler); app.get('/api/slots/disponiveis', cacheMiddleware(120), handler);

Invalidação do cache ao modificar dados:

// Após criar/editar/deletar uma especialidade: const cache = caches.default; await cache.delete(new Request('https://api.dominio.com/api/especialidades')); // Função helper para invalidar múltiplas chaves: async function invalidateCache(...urls) { const cache = caches.default; await Promise.all( urls.map(url => cache.delete(new Request(url))) ); }

3.2 🟡 Workers KV — Cache Global Persistente (2ª etapa)

Key-Value store global com replicação eventual. Ideal para dados que precisam de cache consistente em todas as regiões.

Característica Detalhe
Custo (Free) 100K leituras/dia, 1K escritas/dia, 1GB storage
Custo (Paid) 10M leituras/mês incluídas, +$0.50/milhão
Escopo Global — replica automaticamente em todos os edge nodes
Consistência Eventual (até 60 segundos para propagar)
TTL Configurável por chave (expirationTtl)
Tamanho máximo do valor 25MB por chave

Casos de uso recomendados para KV:

  • Sessões de paciente/admin: Cache de JWT validados para evitar consultas ao DB_USUARIOS a cada request
  • Configurações do sistema: Parâmetros, tipos de slot, especialidades com cache longo
  • Rate limiting: Contadores de tentativas de login por IP/CPF
  • Tokens OTP temporários: Já que possuem TTL natural

Configuração no wrangler.toml:

# Adicionar ao wrangler.toml [[kv_namespaces]] binding = "KV_CACHE" id = "<namespace-id-gerado>" [[kv_namespaces]] binding = "KV_SESSIONS" id = "<namespace-id-gerado>" [[kv_namespaces]] binding = "KV_RATE_LIMIT" id = "<namespace-id-gerado>"

Exemplo de uso — Cache de Especialidades:

// services/kvCache.js export async function getWithCache(kvNamespace, key, fetchFn, ttl = 3600) { // 1. Tentar KV primeiro const cached = await kvNamespace.get(key, { type: 'json' }); if (cached) return cached; // 2. Se não tiver, buscar no D1 const data = await fetchFn(); // 3. Salvar no KV com TTL await kvNamespace.put(key, JSON.stringify(data), { expirationTtl: ttl }); return data; } // Uso na rota de especialidades: const especialidades = await getWithCache( c.env.KV_CACHE, 'especialidades:all', () => c.env.DB_PARAMETROS.prepare('SELECT * FROM ca_especialidade WHERE ca_status = ?') .bind('Ativo').all(), 3600 // 1 hora );

3.3 🟢 Cloudflare Pages — Cache de Assets Estáticos (já ativo)

Os frontends em Cloudflare Pages já possuem cache automático para assets estáticos (JS, CSS, imagens). O CDN Cloudflare distribui automaticamente em todos os PoPs.

✅ Já configurado automaticamente Nenhuma ação adicional necessária para cache de frontends. Cloudflare Pages serve assets com headers de cache otimizados e invalidação automática no deploy.

3.4 🟢 R2 — Cache de Imagens

Fotos de médicos servidas via R2 público já se beneficiam do CDN Cloudflare. Para otimizar:

  • Adicionar Cache-Control: public, max-age=86400 nos uploads para cache de 24h
  • Usar URL com hash do arquivo para cache-busting automático ao atualizar foto

🔁 4. Redundância do Banco de Dados (D1)

4.1 🔵 D1 Read Replication — Redundância Global de Leitura

O Cloudflare D1 oferece Read Replication nativo, que cria cópias somente-leitura do banco em múltiplas regiões do mundo.

Recurso Detalhe
Custo GRÁTIS — sem custo extra por réplicas
Regiões ENAM, WNAM, WEUR, EEUR, APAC, OC (6 regiões globais)
Consistência Sequencial (via Sessions API com bookmarks)
Escritas Sempre redirecionadas ao primary
Leituras Servidas pela réplica mais próxima
API env.DB.withSession()
💡 Vantagem Principal Como nosso sistema é 80%+ leitura (consultar grades, slots disponíveis, dados de médicos/unidades), Read Replication pode reduzir a latência significativamente para usuários distantes da região do primary database, sem custo adicional.

Como habilitar (Dashboard):

1

Acessar D1 no Dashboard

Ir para dash.cloudflare.com → Workers & Pages → D1

2

Selecionar cada banco

Selecionar command-agendamento-db, ir em SettingsEnable Read Replication

3

Repetir para todos os 4 bancos

Habilitar em: DB, DB_HISTORICO, DB_USUARIOS, DB_PARAMETROS

Como habilitar (REST API):

# Habilitar Read Replication via API para cada banco: # DB (command-agendamento-db) curl -X PUT "https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/d1/database/df3472a6-39ae-48e3-8dbc-c0dca22ed985" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"read_replication": {"mode": "auto"}}' # DB_HISTORICO curl -X PUT ".../d1/database/b5a8a19c-0b9c-489a-9187-eb7eb635d99e" \ -H "Authorization: Bearer $TOKEN" \ -d '{"read_replication": {"mode": "auto"}}' # DB_USUARIOS curl -X PUT ".../d1/database/db895e72-76e4-4df4-9727-e793da6296e4" \ -H "Authorization: Bearer $TOKEN" \ -d '{"read_replication": {"mode": "auto"}}' # DB_PARAMETROS curl -X PUT ".../d1/database/ee193a8f-f9d2-47ea-896c-9aef41dcb747" \ -H "Authorization: Bearer $TOKEN" \ -d '{"read_replication": {"mode": "auto"}}'

Adaptação do código — Sessions API:

// ANTES (sem réplicas — todas as queries vão ao primary): const result = await c.env.DB.prepare('SELECT * FROM ca_gradeagenda').all(); // DEPOIS (com réplicas — leituras vão à réplica mais próxima): // Para leituras que não precisam ser 100% atualizadas: const session = c.env.DB.withSession(); // "first-unconstrained" const result = await session .prepare('SELECT * FROM ca_gradeagenda WHERE ca_status = ?') .bind('Ativo') .all(); // Para operações que precisam ver dados mais recentes (após write): const session = c.env.DB.withSession("first-primary"); const result = await session .prepare('SELECT * FROM ca_agendamento WHERE ca_pacienteid = ?') .bind(pacienteId) .all(); // Para manter consistência entre requests (via bookmark): const bookmark = request.headers.get('x-d1-bookmark') ?? 'first-unconstrained'; const session = c.env.DB.withSession(bookmark); // ... executar queries ... response.headers.set('x-d1-bookmark', session.getBookmark() ?? '');

4.2 📊 Qual tipo de sessão usar por rota?

Rota / Operação Tipo de Session Motivo
GET /api/especialidades withSession() Dados estáticos, réplica OK
GET /api/unidades withSession() Dados estáticos, réplica OK
GET /api/medicos withSession() Lista geral, tolera lag
GET /api/slots/disponiveis withSession("first-primary") Precisa estar atualizado
POST /api/agendamentos Primary direto Escrita, vai ao primary
GET /api/agendamentos/meus withSession(bookmark) Após agendar, precisa ver imediato
GET /api/backlog withSession(bookmark) Após alocar, precisa refletir
POST /api/auth-paciente/login Primary direto Escrita de sessão
GET /api/dashboard/* withSession() Dados analíticos, tolera lag

💾 5. Estratégia de Backup

5.1 D1 Time Travel (Nativo)

O D1 oferece Time Travel, permitindo restaurar o banco para qualquer ponto nos últimos 30 dias (Paid plan).

Plano Retenção Custo
Free Nenhum Grátis
Paid ($5/mês Workers) 30 dias de histórico Incluído
# Restaurar banco para um timestamp específico: npx wrangler d1 time-travel restore command-agendamento-db \ --timestamp="2026-02-20T10:00:00Z" # Ver bookmarks disponíveis: npx wrangler d1 time-travel info command-agendamento-db

5.2 Backup Automatizado via Cron (Recomendado)

Criar um Worker com Cron Trigger que exporta os dados do D1 para R2 diariamente:

// backup-worker/src/index.js export default { async scheduled(event, env, ctx) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); // Exportar cada banco const databases = [ { name: 'db', binding: env.DB }, { name: 'historico', binding: env.DB_HISTORICO }, { name: 'usuarios', binding: env.DB_USUARIOS }, { name: 'parametros', binding: env.DB_PARAMETROS } ]; for (const db of databases) { const tables = await db.binding .prepare("SELECT name FROM sqlite_master WHERE type='table'") .all(); let dump = ''; for (const table of tables.results) { const rows = await db.binding .prepare(`SELECT * FROM ${table.name}`).all(); dump += JSON.stringify({ table: table.name, rows: rows.results }) + '\n'; } // Salvar no R2 await env.R2_BUCKET.put( `backups/${db.name}/${timestamp}.json`, dump ); } } }; // wrangler.toml do backup worker: // [triggers] // crons = ["0 3 * * *"] # Todo dia às 03:00 UTC

🛡️ 6. Rate Limiting & Proteção

⚠️ Estado Atual A API não possui rate limiting. Isso é um risco de segurança — um atacante pode fazer brute-force em OTPs, sobrecarregar endpoints ou realizar denial-of-wallet.

6.1 Rate Limiting com Workers KV

// middleware/rateLimit.js export function rateLimit({ maxRequests = 60, windowSeconds = 60 } = {}) { return async (c, next) => { const ip = c.req.header('CF-Connecting-IP') || 'unknown'; const key = `rate:${ip}:${Math.floor(Date.now() / (windowSeconds * 1000))}`; const current = await c.env.KV_RATE_LIMIT.get(key, { type: 'text' }); const count = current ? parseInt(current) : 0; if (count >= maxRequests) { return c.json({ error: 'Rate limit exceeded' }, 429); } await c.env.KV_RATE_LIMIT.put(key, String(count + 1), { expirationTtl: windowSeconds }); c.res.headers.set('X-RateLimit-Limit', String(maxRequests)); c.res.headers.set('X-RateLimit-Remaining', String(maxRequests - count - 1)); return next(); }; } // Limites sugeridos por rota: app.use('/api/auth-paciente/login', rateLimit({ maxRequests: 5, windowSeconds: 300 })); app.use('/api/auth-paciente/otp/*', rateLimit({ maxRequests: 3, windowSeconds: 600 })); app.use('/api/agendamentos', rateLimit({ maxRequests: 10, windowSeconds: 60 })); app.use('/api/*', rateLimit({ maxRequests: 120, windowSeconds: 60 }));

📋 7. Passo a Passo de Implementação

Ordem de Prioridade de Implementação

🔴 Prioridade 1 — Crítico

D1 Read Replication

Custo ZERO, ganho imediato de latência. Ativar no dashboard em 5 minutos.

🔴 Prioridade 1 — Crítico

Rate Limiting

Proteção essencial contra abuse. Requer Workers KV.

🟡 Prioridade 2 — Alta

Cache API (Edge)

Grátis, reduz 80% das queries D1 para endpoints de leitura.

🔵 Prioridade 3 — Média

Workers KV Cache

Cache global para config e sessões. Custo baixo.

🔵 Prioridade 3 — Média

Backup Automatizado

Cron Worker + R2 para dumps diários.

⚪ Prioridade 4 — Futuro

Workers Logs

Observabilidade completa para troubleshooting.

🔴 Fase 1 — Fundamentos (Semana 1)

1

Ativar D1 Read Replication nos 4 bancos

Dashboard → D1 → Cada banco → Settings → Enable Read Replication

Tempo: 10 minutos | Custo: $0 | Risco: Nenhum

2

Migrar código para Sessions API

Atualizar todas as queries de leitura para usar withSession()

Tempo: 2-4 horas | Custo: $0 | Risco: Baixo (testar em staging)

3

Criar KV Namespaces

npx wrangler kv namespace create "KV_CACHE" npx wrangler kv namespace create "KV_SESSIONS" npx wrangler kv namespace create "KV_RATE_LIMIT"

Tempo: 5 minutos | Custo: Incluído no plan | Risco: Nenhum

4

Implementar Rate Limiting

Criar middleware rateLimit.js e aplicar nas rotas críticas (auth, OTP, agendamentos)

Tempo: 2 horas | Custo: $0 (KV Free) | Risco: Baixo

🟡 Fase 2 — Cache (Semana 2-3)

5

Implementar Cache API (Edge Cache)

Criar middleware cacheMiddleware e aplicar nos endpoints de leitura de parâmetros

Tempo: 3-4 horas | Custo: $0 | Risco: Médio (requer domínio custom)

6

Implementar invalidação de cache

Nas rotas de escrita (POST/PUT/DELETE), invalidar cache das rotas de leitura correspondentes

Tempo: 2-3 horas | Custo: $0 | Risco: Médio (testar invalidação)

7

Cache KV para dados frequentes

Implementar getWithCache() para especialidades, unidades, tipos de slot e configurações

Tempo: 3 horas | Custo: ~$0 (dentro do Free) | Risco: Baixo

🟣 Plano B — Se o D1 ultrapassar 10GB

O Cloudflare D1 tem um limite de armazenamento (soft limit de 10GB no Beta/Paid atual). Caso o banco cresça além disso, temos 3 estratégias:

A

Cold Storage no R2 (Mais barato)

Mover dados históricos (agendamentos > 2 anos, logs antigos) para arquivos Parquet/JSON no R2. O R2 é infinitamente escalável e muito mais barato ($0.015/GB).

Custo: Baixíssimo. Esforço: Médio (Worker de arquivamento).

B

Sharding (Múltiplos Bancos D1)

Criar múltiplos bancos D1 (ex: db_2026, db_2027) e rotear no Worker. A conta Cloudflare suporta até 50.000 bancos D1.

Custo: $0 (pagamento por uso). Esforço: Alto (lógica de roteamento).

C

Banco SQL Externo (Neon / Supabase / Turso)

Migrar o banco principal para um serviço Postgres serverless especializado (Neon Tech ou Supabase) e manter o Worker como API Proxy via Hyperdrive (aceleração de conexão).

Custo: ~$19-50/mês. Esforço: Médio (driver Postgres no código).

🔵 Fase 3 — Resilência (Semana 4+)

8

Worker de Backup Automatizado

Criar backup-worker com Cron Trigger (diário às 03:00 UTC) que exporta todos os 4 bancos para R2

Tempo: 3-4 horas | Custo: ~$0 (R2 Free tier cobre) | Risco: Nenhum

9

Monitoramento e Alertas

Configurar Workers Logs + notificações via email/WhatsApp para erros 5xx

Tempo: 2 horas | Custo: Incluído no Paid | Risco: Nenhum

10

Criar índices D1 otimizados

Criar índices nas colunas mais consultadas para reduzir rows_read e custo

-- Índices recomendados para performance: -- DB (Grades e Slots) CREATE INDEX idx_grade_medico ON ca_gradeagenda(ca_medicoid, ca_status); CREATE INDEX idx_grade_unidade ON ca_gradeagenda(ca_unidadeid, ca_status); CREATE INDEX idx_slot_grade ON ca_slotagenda(ca_gradeagendaid, ca_status); CREATE INDEX idx_slot_data ON ca_slotagenda(ca_datainicio, ca_status); CREATE INDEX idx_diacancel_grade ON ca_grade_dia_cancelado(ca_gradeagendaid, ca_data); -- DB_HISTORICO (Agendamentos) CREATE INDEX idx_agendamento_slot ON ca_agendamento(ca_slotagendaid, ca_status); CREATE INDEX idx_agendamento_paciente ON ca_agendamento(ca_pacienteid, ca_status); CREATE INDEX idx_backlog_paciente ON ca_backlog(ca_pacienteid, ca_status); CREATE INDEX idx_backlog_status ON ca_backlog(ca_status, ca_datacriacao); -- DB_USUARIOS CREATE INDEX idx_paciente_cpf ON ca_paciente(ca_cpf); CREATE INDEX idx_paciente_telefone ON ca_paciente(ca_telefone); CREATE INDEX idx_admin_email ON ca_usuario_admin(ca_email); CREATE INDEX idx_sessao_jti ON ca_sessao(ca_tokenjti, ca_revogado); CREATE INDEX idx_sessao_pac_jti ON ca_sessao_paciente(ca_tokenjti, ca_revogado); -- DB_PARAMETROS CREATE INDEX idx_especialidade_status ON ca_especialidade(ca_status); CREATE INDEX idx_unidade_status ON ca_unidade(ca_status); CREATE INDEX idx_tiposlot_status ON ca_tiposlot(ca_status);

Tempo: 1 hora | Custo: Insignificante (mais writes no index) | Risco: Baixo

⚠️ 8. Análise de Riscos

8.1 Riscos Atuais (SEM as melhorias)

Risco Severidade Probabilidade Impacto
Denial-of-Wallet (sem rate limiting) CRÍTICO Média Atacante gera milhões de requests, custo inesperado na fatura
Brute-force no OTP/Login ALTO Alta Atacante tenta todas as combinações de OTP (6 dígitos = 1M tentativas)
Sem backup manual/automatizado MÉDIO Baixa Perda de dados se D1 Time Travel falhar ou erro humano
Latência alta para usuários distantes MÉDIO Variável Todas as queries vão ao primary (provavelmente US), latência alta para BR
Sem cache = mais rows_read = mais custo MÉDIO Alta Cada refresh de página gera 5-10 queries ao D1

8.2 Riscos da Implementação

Componente Risco Severidade Mitigação
D1 Read Replication Replica Lag — leitura pode retornar dados desatualizados MÉDIO Usar withSession("first-primary") para queries que exigem dados mais recentes (ex: após criar agendamento). Sessions API garante consistência sequencial.
Cache API Stale data — cache não invalidado após mutação MÉDIO Implementar invalidação explícita em todas as rotas de escrita. Usar TTLs curtos (2-5 min) para dados voláteis.
Cache API Requer domínio customizado (não funciona em *.workers.dev) BAIXO Configurar domínio custom para a API (ex: api.commandagenda.com.br). Cloudflare DNS é grátis.
Workers KV Consistência eventual (até 60s de delay na propagação global) BAIXO Aceitável para cache de configurações. Para sessões, usar TTL alinhado ao JWT.
Rate Limiting (KV) Contadores podem ficar inconsistentes entre regiões BAIXO Aceitável — se um IP fizer 5 requests em 2 PoPs diferentes, pior caso são 10 requests ao invés de 5. Suficiente para prevenir abuse.
Backup Worker Export via SELECT * pode ser lento em tabelas grandes BAIXO Paginar queries (LIMIT/OFFSET) e exportar em chunks. Volume atual é pequeno.
Índices D1 Aumenta marginalmente o custo de escritas BAIXO Benefício em leituras (80%+ das operações) supera largamente o custo extra em escritas. Cada write adiciona ~1 row_written por index.

8.3 Riscos de NÃO implementar (cenário de crescimento)

🚨 Cenário: 1.000 pacientes ativos, 50 médicos, 10 unidades
  • Sem cache: ~50K-100K queries D1/dia → ~1.5M-3M rows_read/dia → dentro do free tier, MAS próximo do limite
  • Sem rate limiting: Um bot pode gerar 1M+ requests/dia facilmente → custo imprevisível
  • Sem réplicas: Latência de 100-300ms por query D1 (dependendo da região), afetando UX do paciente
  • Sem backup: Uma migration mal feita pode corromper dados sem recuperação

💰 9. Análise de Custos

9.1 Tabela de Preços Cloudflare (Referência Fevereiro 2026)

Serviço Free Tier Plano Paid (Incluído) Custo Excedente
Workers 100K req/dia 10M req/mês + 30M CPU-ms $0.30/milhão req + $0.02/milhão CPU-ms
D1 Rows Read 5M/dia 25 bilhões/mês $0.001/milhão rows
D1 Rows Written 100K/dia 50M/mês $1.00/milhão rows
D1 Storage 5 GB total 5 GB incluído $0.75/GB-mês
D1 Read Replication ✅ GRÁTIS — sem custo extra por réplicas
Workers KV Reads 100K/dia 10M/mês $0.50/milhão
Workers KV Writes 1K/dia 1M/mês $5.00/milhão
Workers KV Storage 1 GB 1 GB $0.50/GB-mês
R2 Storage 10 GB 10 GB $0.015/GB-mês
R2 Class A Ops 1M/mês 1M/mês $4.50/milhão
R2 Class B Ops 10M/mês 10M/mês $0.36/milhão
Pages ✅ GRÁTIS — unlimited bandwidth
Cache API ✅ GRÁTIS — incluído no Workers runtime

9.2 Cenário A — Uso Atual / Baixo Volume

~100 pacientes ativos, 10 médicos, 3 unidades, ~5K req/dia

💰 Custo Estimado Mensal — Cenário A

Workers Paid Plan (base)$5.00
D1 (4 bancos) — dentro do incluído$0.00
D1 Read Replication$0.00
Workers KV (cache + rate limit)$0.00
R2 Storage (fotos + backups)$0.00
Cache API$0.00
Pages (3 sites)$0.00
TOTAL ESTIMADO$5.00/mês
✅ Com o Workers Paid ($5/mês), TODAS as melhorias ficam dentro do limite incluído para este volume.

9.3 Cenário B — Crescimento Médio

~1.000 pacientes ativos, 50 médicos, 10 unidades, ~50K req/dia

💰 Custo Estimado Mensal — Cenário B

Workers Paid Plan (base)$5.00
Workers Requests (~1.5M/mês) — dentro do incluído$0.00
D1 Rows Read (~30M/mês, com cache ~10M) — incluído$0.00
D1 Rows Written (~2M/mês) — incluído$0.00
D1 Read Replication$0.00
Workers KV (~3M reads/mês) — incluído$0.00
R2 (backups ~1GB) — incluído$0.00
TOTAL ESTIMADO$5.00/mês
✅ Ainda dentro dos limites incluídos! O cache reduz os rows_read significativamente.

9.4 Cenário C — Alto Volume

~10.000 pacientes ativos, 200 médicos, 30 unidades, ~500K req/dia

💰 Custo Estimado Mensal — Cenário C

Workers Paid Plan (base)$5.00
Workers Requests (~15M/mês) — 5M excedente$1.50
D1 Rows Read (~100M/mês, com cache ~30M) — incluído$0.00
D1 Rows Written (~10M/mês) — incluído$0.00
D1 Read Replication$0.00
Workers KV (~15M reads/mês) — 5M excedente$2.50
Workers KV Writes (~500K/mês) — incluído$0.00
R2 (backups ~5GB) — incluído$0.00
TOTAL ESTIMADO~$9.00/mês
💡 Mesmo com 10x o volume, o custo mensal fica abaixo de $10. O cache é fundamental para manter os custos baixos.

9.4.1 Cenário D — Escala Massiva (50k Usuários)

~50.000 pacientes ativos, 1.000 médicos, 150 unidades, ~2.5M req/dia

💰 Custo Estimado Mensal — Cenário D

Workers Paid Plan (base)$5.00
Workers Requests (~75M/mês) — 65M excedente$19.50
D1 Rows Read (~500M/mês, com cache ~100M) — incluído$0.00
D1 Rows Written (~50M/mês) — limite do plano$0.00
D1 Read Replication$0.00
Workers KV (~75M reads/mês) — 65M excedente$32.50
Workers KV Writes (~2.5M/mês) — 1.5M excedente$7.50
R2 (backups ~25GB) — 15GB excedente$0.23
TOTAL ESTIMADO~$65.00/mês
⚠️ Ponto de Atenção: Neste volume, o custo de KV Reads torna-se o maior ofensor. A estratégia deve ser refinar o cache local (Cache API) para reduzir ainda mais a dependência do KV global.

9.5 ⚠️ Impacto do Cache nos Custos (Cenário C sem cache)

Métrica SEM Cache COM Cache Economia
D1 Rows Read/mês ~300M (full table scans a cada request) ~30M (80% servido do cache) 90% redução
Custo D1 Read (excedente) $0.275 (~275M excedente) $0.00 (dentro do incluído) $0.275/mês
Latência média 100-300ms (D1 query) 5-20ms (cache hit) 80-95% mais rápido
💡 Nota sobre custos D1: O D1 Paid plan inclui 25 bilhões de rows read/mês. Mesmo sem cache, os custos de D1 são extremamente baixos. O benefício principal do cache é a redução de latência (5ms vs 100-300ms), não a economia financeira.

🗺️ 10. Roadmap de Implementação

Timeline Sugerida

Semana 1 — Fundamentos (Custo: $0)

  • ✅ Ativar D1 Read Replication nos 4 bancos (10 min)
  • ✅ Criar KV Namespaces (5 min)
  • ✅ Implementar middleware de Rate Limiting (2h)
  • ✅ Migrar queries de leitura para Sessions API (3-4h)

Semana 2 — Cache Edge (Custo: $0)

  • ✅ Configurar domínio customizado para API
  • ✅ Implementar Cache API middleware
  • ✅ Invalidação de cache nas rotas de escrita
  • ✅ Testar TTLs e validar comportamento

Semana 3 — Cache KV + Índices (Custo: $0)

  • ✅ Implementar cache KV para configurações globais
  • ✅ Cache KV para sessões validadas
  • ✅ Criar índices D1 nas tabelas mais consultadas
  • ✅ Monitorar rows_read antes/depois dos índices

Semana 4 — Backup & Monitoramento (Custo: $0)

  • ✅ Criar Worker de backup automatizado (Cron)
  • ✅ Configurar Workers Logs
  • ✅ Alertas de erro via WhatsApp/email
  • ✅ Documentar runbook de recuperação de desastres

🏛️ 11. Arquitetura Proposta (Após Implementação)

┌──────────────────────────────────────────────────────────────────────────────┐ │ CLOUDFLARE NETWORK (Global Edge) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │Pages ADM │ │Pages PAC.│ │Pages Docs│ CDN Cache Automático ✅ │ │ └────┬─────┘ └────┬─────┘ └──────────┘ │ │ └──────┬───────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────┐ │ │ │ 🛡️ Rate Limiting (KV) │ ← NOVO │ │ │ Middleware Global │ │ │ └─────────────────┬───────────────────────────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────┐ │ │ │ 🗄️ Cache API (Edge) │ ← NOVO │ │ │ GET params → cache hit → retorno │ │ │ └─────────────────┬───────────────────────────┘ │ │ ▼ (cache miss) │ │ ┌─────────────────────────────────────────────┐ ┌──────────────────┐ │ │ │ Cloudflare Workers (Hono 4.x) │ │ Workers KV │ │ │ │ command-agenda-api │────▶│ • KV_CACHE │ │ │ │ • JWT Auth • RBAC • CORS │ │ • KV_SESSIONS │ │ │ │ • 15 módulos de rotas │ │ • KV_RATE_LIMIT│ │ │ └──┬──────┬──────┬──────┬─────────────────────┘ └──────────────────┘ │ │ │ │ │ │ ← NOVO │ │ ▼ ▼ ▼ ▼ │ │ ┌─────┐┌─────┐┌─────┐┌─────┐ ┌─────────────────────────┐ │ │ │ D1 ││ D1 ││ D1 ││ D1 │ │ R2 Storage │ │ │ │ DB ││HIST.││USRS.││PARM.│ │ • Fotos médicos │ │ │ │ ││ ││ ││ │ │ • Backups diários ← NOVO│ │ │ ├─────┤├─────┤├─────┤├─────┤ └─────────────────────────┘ │ │ │ 🔁 ││ 🔁 ││ 🔁 ││ 🔁 │ │ │ │READ ││READ ││READ ││READ │ ← NOVO (Read Replication) │ │ │REPL.││REPL.││REPL.││REPL.│ 6 regiões globais cada │ │ └─────┘└─────┘└─────┘└─────┘ │ │ │ │ ┌───────────────────────────────────┐ │ │ │ ⏰ Cron Worker (Backup) │ ← NOVO │ │ │ Todo dia 03:00 UTC → R2 │ │ │ └───────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────┘

📊 Resumo Executivo

💰 Custo Total

$5 - $65

Escala automática de 100 até 50.000 usuários mensais

⚡ Melhoria de Latência

80-95%

Redução com cache + read replication

⏱️ Tempo Total

~4 semanas

Implementação gradual em 4 fases

Ferramentas Cloudflare Utilizadas

Ferramenta Finalidade Custo Adicional Status
Workers API Runtime Já em uso JÁ TEMOS
D1 Banco de Dados (4x) Já em uso JÁ TEMOS
D1 Read Replication Redundância + Performance $0 ATIVAR
D1 Sessions API Consistência com réplicas $0 IMPLEMENTAR
Cache API Cache no Edge (por data center) $0 IMPLEMENTAR
Workers KV Cache global + Rate Limiting + Sessões $0 (dentro do Paid) IMPLEMENTAR
R2 Storage + Backups Já em uso JÁ TEMOS
Pages Frontends (3 sites) Já em uso JÁ TEMOS
Cron Triggers Backup automatizado $0 IMPLEMENTAR
D1 Time Travel Point-in-time recovery $0 (Paid plan) JÁ TEMOS