Pular para o conteúdo

Padrões de Banco de Dados que Você Deve Conhecer Antes de Escolher Seu Próximo Banco de Dados

A escolha entre Postgres e MongoDB não é sobre qual é 'melhor'. É sobre entender os padrões de acesso, requisitos de consistência e restrições operacionais do seu sistema.

Durval Pereira
Durval Pereira
5 min

A pergunta errada

"Devo usar Postgres ou MongoDB?" é a primeira pergunta errada. A primeira pergunta certa é: "Quais são meus padrões de acesso?"

Todo banco de dados é otimizado para padrões de acesso específicos. Escolher um banco de dados sem entender seus padrões de acesso é como escolher uma estrutura de dados sem entender seu algoritmo — você pode ter sorte, mas provavelmente estará dificultando sua vida.

Análise de padrões de acesso

Antes de avaliar qualquer banco de dados, documente seus padrões de acesso explicitamente:

interface AccessPattern {
  name: string
  type: 'read' | 'write' | 'read-write'
  frequency: 'hot' | 'warm' | 'cold'
  latencyRequirement: 'realtime' | 'fast' | 'batch'
  consistency: 'strong' | 'eventual' | 'causal'
  description: string
}

const patterns: AccessPattern[] = [
  {
    name: 'user-profile-read',
    type: 'read',
    frequency: 'hot',
    latencyRequirement: 'realtime',
    consistency: 'eventual',
    description: 'Fetch user profile by ID on every page load',
  },
  {
    name: 'order-creation',
    type: 'write',
    frequency: 'warm',
    latencyRequirement: 'fast',
    consistency: 'strong',
    description: 'Create an order with line items, update inventory',
  },
  {
    name: 'analytics-aggregation',
    type: 'read',
    frequency: 'cold',
    latencyRequirement: 'batch',
    consistency: 'eventual',
    description: 'Aggregate daily metrics across all users',
  },
]

Este exercício, por si só, esclarecerá 80% das suas decisões de banco de dados. Um sistema dominado por leituras "quentes" (hot reads) com consistência eventual é um problema completamente diferente de um sistema dominado por escritas transacionais com consistência forte.

Padrão: Tabela única, múltiplos padrões de acesso

O padrão mais subestimado em bancos de dados relacionais. Em vez de normalizar tudo em uma dezena de tabelas com JOINs complexos, projete sua tabela primária em torno do seu padrão de acesso mais "quente" (hottest access pattern) e use estruturas secundárias para o restante.

-- Primary table: optimized for the hot path (fetch by ID)
CREATE TABLE documents (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  type        TEXT NOT NULL,
  owner_id    UUID NOT NULL,
  data        JSONB NOT NULL,
  metadata    JSONB DEFAULT '{}',
  created_at  TIMESTAMPTZ DEFAULT now(),
  updated_at  TIMESTAMPTZ DEFAULT now()
);

-- Indexes for secondary access patterns
CREATE INDEX idx_documents_owner ON documents(owner_id);
CREATE INDEX idx_documents_type ON documents(type);
CREATE INDEX idx_documents_created ON documents(created_at DESC);

-- GIN index for JSONB queries when needed
CREATE INDEX idx_documents_data ON documents USING GIN (data);

Essa abordagem oferece a flexibilidade de um document store com as garantias transacionais do Postgres. A coluna JSONB lida com a variação de esquema sem exigir migrações para cada alteração de campo.

Padrão: Escritas event-sourced, leituras materializadas

Quando seus padrões de escrita e leitura são fundamentalmente diferentes, tentar atender a ambos a partir da mesma estrutura de dados gera problemas. Event sourcing os separa de forma limpa.

interface DomainEvent {
  id: string
  aggregateId: string
  type: string
  data: Record<string, unknown>
  timestamp: Date
  version: number
}

// Write side: append events
async function processCommand(command: CreateOrder): Promise<void> {
  const events: DomainEvent[] = [
    {
      id: generateId(),
      aggregateId: command.orderId,
      type: 'OrderCreated',
      data: { customerId: command.customerId, items: command.items },
      timestamp: new Date(),
      version: 1,
    },
    {
      id: generateId(),
      aggregateId: command.orderId,
      type: 'InventoryReserved',
      data: { items: command.items },
      timestamp: new Date(),
      version: 2,
    },
  ]

  await eventStore.appendEvents(command.orderId, events)
}

// Read side: maintain materialized views
async function handleEvent(event: DomainEvent): Promise<void> {
  switch (event.type) {
    case 'OrderCreated':
      await orderReadModel.upsert({
        id: event.aggregateId,
        status: 'created',
        ...event.data,
      })
      break
    case 'InventoryReserved':
      await inventoryReadModel.decrementStock(event.data.items)
      break
  }
}

Este padrão adiciona complexidade, mas se destaca quando você tem requisitos de auditoria rigorosos, precisa reconstruir modelos de leitura do zero ou tem necessidades de escalabilidade de leitura e escrita dramaticamente diferentes.

O padrão Postgres

Para a maioria das aplicações, a resposta pragmática é: comece com Postgres.

Não porque Postgres é o melhor em tudo. Não é. Mas porque:

  1. É bom o suficiente na maioria das coisas. Consultas relacionais, documentos JSON, busca de texto completo (full-text search), dados geoespaciais, séries temporais com extensões — o Postgres lida com tudo isso de forma competente.
  2. Maturidade operacional. As ferramentas de backup, replicação, monitoramento e recuperação para Postgres são testadas em batalha (battle-tested).
  3. Conhecimento da equipe. A maioria dos engenheiros conhece SQL. Menos conhecem as linguagens de consulta de bancos de dados especializados.
  4. Flexibilidade. Você sempre pode extrair um banco de dados especializado para um padrão de acesso específico mais tarde. Começar especializado é muito mais difícil de reverter.

A hora certa de considerar um banco de dados especializado é quando você pode provar — com dados, não intuição — que o Postgres é o gargalo para um padrão de acesso específico. E mesmo assim, a resposta é frequentemente "adicione um armazenamento de dados especializado ao lado do Postgres" em vez de "substitua o Postgres."

Quando olhar além do Postgres

Existem casos legítimos para bancos de dados especializados:

Dados de séries temporais em escala. Se você está ingerindo milhões de pontos de dados por segundo com consultas de agregação baseadas em tempo, o TimescaleDB (extensão do Postgres) ou um banco de dados de séries temporais dedicado como o ClickHouse superará significativamente o Postgres "puro" (vanilla Postgres).

Traversais de grafo. Se suas consultas principais envolvem a travessia de relacionamentos em muitos níveis de profundidade — redes sociais, mecanismos de recomendação, grafos de dependência — um banco de dados de grafo como o Neo4j expressará essas consultas de forma mais natural e as executará com mais eficiência.

Busca em tempo real. Se a busca de texto completo (full-text search) é um recurso primário com pontuação de relevância complexa, o Elasticsearch ou o Typesense fornecerão melhor qualidade de busca do que o tsvector do Postgres.

Alto throughput de escrita. Se você precisa escrever milhões de linhas por segundo com indexação mínima, um banco de dados estruturado em log como ScyllaDB ou Apache Cassandra é projetado exatamente para isso.

A checklist de modelagem de dados

Antes de finalizar seu esquema, responda a estas perguntas:

  1. Qual é a sua consulta de leitura mais "quente" (hottest read query)? Otimize seus índices primários para isso.
  2. Quais são seus requisitos de consistência? Consistência forte custa performance. Seja explícito sobre onde você precisa dela.
  3. Como seus dados crescem? Séries temporais, somente anexação (append-only) ou mutáveis? Isso afeta a estratégia de armazenamento.
  4. Quais são seus padrões de JOIN? Se você nunca faz JOIN de duas tabelas, elas podem não precisar estar no mesmo banco de dados.
  5. Quais são seus requisitos de backup e recuperação? Alguns bancos de dados tornam a recuperação pontual (point-in-time recovery) trivial. Outros a tornam dolorosa.

A melhor decisão de banco de dados é aquela que é chata, previsível e bem compreendida pela sua equipe. Guarde as escolhas tecnológicas interessantes para os problemas que realmente precisam delas.


Este artigo faz parte de uma série sobre fundamentos de infraestrutura de dados.

Tagsdatabasespostgresdata-modelingsystem-design