Polyglot persistence: usando o banco certo para cada problema

1. O Conceito de Polyglot Persistence e Sua Relevância

Polyglot persistence é a prática de utilizar múltiplos sistemas de gerenciamento de banco de dados (SGBDs) em uma mesma aplicação, cada um escolhido para resolver um problema específico de armazenamento. O termo foi cunhado por Neal Ford e Martin Fowler, inspirado no conceito de polyglot programming (uso de múltiplas linguagens de programação em um mesmo sistema).

A ideia central é simples: nenhum banco de dados é universalmente superior para todos os cenários. Um banco relacional como PostgreSQL é excelente para transações financeiras, mas péssimo para armazenar grafos de amizades em uma rede social. Um Redis é perfeito para cache de sessões, mas inadequado para consultas complexas com joins. A escolha do banco certo para cada problema traz benefícios diretos:

  • Performance: cada banco é otimizado para seu modelo de dados nativo
  • Escalabilidade: bancos NoSQL horizontais escalam melhor para cargas específicas
  • Adequação ao modelo de dados: documentos, grafos, séries temporais — cada um tem seu lugar

2. Quando Adotar Múltiplos Bancos de Dados

A decisão de adotar polyglot persistence geralmente surge quando a aplicação apresenta alta heterogeneidade de dados. Por exemplo:

  • Dados transacionais (pedidos, pagamentos) exigem ACID forte
  • Dados analíticos (métricas de uso, logs) exigem alta taxa de ingestão e consultas agregadas
  • Dados de sessão exigem baixíssima latência e expiração automática

Outro gatilho comum são requisitos de latência e throughput distintos por serviço. Um microsserviço de catálogo de produtos pode tolerar consistência eventual (BASE), enquanto um serviço de contabilidade exige consistência imediata (ACID). Nesses casos, forçar um único banco a atender ambos os cenários resulta em compromissos dolorosos.

3. Bancos Relacionais: Onde Eles Ainda São a Melhor Escolha

Os bancos relacionais (PostgreSQL, MySQL, SQL Server) continuam sendo a melhor opção para:

  • Transações complexas com múltiplas tabelas e integridade referencial
  • Consultas ad-hoc que exigem joins, agregações e subconsultas arbitrárias
  • Sistemas financeiros, ERPs e cadastros mestres (clientes, produtos, fornecedores)

Exemplo de schema relacional para um sistema financeiro:

CREATE TABLE contas (
    id SERIAL PRIMARY KEY,
    cliente_id INTEGER NOT NULL REFERENCES clientes(id),
    saldo DECIMAL(15,2) NOT NULL DEFAULT 0.00,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE transacoes (
    id SERIAL PRIMARY KEY,
    conta_id INTEGER NOT NULL REFERENCES contas(id),
    valor DECIMAL(15,2) NOT NULL,
    tipo VARCHAR(10) CHECK (tipo IN ('credito', 'debito')),
    created_at TIMESTAMP DEFAULT NOW()
);

-- Consulta de saldo atual
SELECT c.nome, SUM(CASE WHEN t.tipo = 'credito' THEN t.valor ELSE -t.valor END) as saldo
FROM contas ct
JOIN clientes c ON c.id = ct.cliente_id
LEFT JOIN transacoes t ON t.conta_id = ct.id
WHERE ct.id = 123
GROUP BY c.nome;

Aqui, a integridade referencial e as transações ACID são indispensáveis.

4. Bancos NoSQL: Principais Famílias e Aplicações Típicas

Documentos (MongoDB, Couchbase)

Ideal para dados semi-estruturados, catálogos de produtos, perfis de usuário com campos variáveis.

// Documento de produto no MongoDB
{
  "_id": ObjectId("..."),
  "nome": "Smartphone X",
  "preco": 2999.90,
  "categoria": "eletronicos",
  "especificacoes": {
    "tela": "6.5 polegadas",
    "bateria": "5000mAh",
    "cores_disponiveis": ["preto", "branco", "azul"]
  },
  "avaliacoes": [
    { "usuario": "joao", "nota": 4.5, "comentario": "Ótimo custo-benefício" }
  ]
}

Chave-Valor (Redis, DynamoDB)

Perfeito para caches, sessões de usuário, filas de mensagens e rate limiting.

// Redis: armazenar sessão com expiração
SET session:user123 '{"nome":"Maria","role":"admin","ultimo_acesso":"2025-03-20T10:30:00"}'
EXPIRE session:user123 3600

// Redis: contador de requisições para rate limiting
INCR rate_limit:api:user123
EXPIRE rate_limit:api:user123 60

Grafos (Neo4j, ArangoDB)

Excelente para redes sociais, sistemas de recomendação, detecção de fraudes e qualquer cenário com relacionamentos complexos.

// Neo4j: criar nós e relacionamentos
CREATE (a:Usuario {nome: "Ana", idade: 30})
CREATE (b:Usuario {nome: "Bruno", idade: 28})
CREATE (c:Produto {nome: "Notebook", preco: 4500})
CREATE (a)-[:COMPROU {data: "2025-03-15"}]->(c)
CREATE (b)-[:AMIGO_DE]->(a)

// Consulta: recomendar produtos que amigos compraram
MATCH (u:Usuario {nome: "Ana"})-[:AMIGO_DE]->(amigo)-[:COMPROU]->(produto)
WHERE NOT (u)-[:COMPROU]->(produto)
RETURN produto.nome, produto.preco

Colunares (Cassandra, HBase)

Indicado para séries temporais, grandes volumes de escrita e dados de IoT.

// Cassandra: tabela para métricas de servidor
CREATE TABLE metricas_servidor (
    servidor_id UUID,
    timestamp TIMESTAMP,
    cpu_usage FLOAT,
    memory_usage FLOAT,
    disk_io FLOAT,
    PRIMARY KEY (servidor_id, timestamp)
) WITH CLUSTERING ORDER BY (timestamp DESC);

// Inserir métricas
INSERT INTO metricas_servidor (servidor_id, timestamp, cpu_usage, memory_usage, disk_io)
VALUES (uuid(), toTimestamp(now()), 75.5, 68.2, 120.0);

5. Bancos Analíticos e de Séries Temporais

Bancos como ClickHouse, TimescaleDB e InfluxDB são especializados em consultas analíticas e métricas em tempo real. Eles oferecem compressão superior e agregações extremamente rápidas.

-- ClickHouse: tabela para logs de acesso
CREATE TABLE logs_acesso (
    timestamp DateTime,
    ip String,
    url String,
    status_code UInt16,
    tempo_resposta UInt32
) ENGINE = MergeTree()
ORDER BY (timestamp, url);

-- Consulta analítica: top 10 URLs mais lentas na última hora
SELECT url, avg(tempo_resposta) as media, count() as total
FROM logs_acesso
WHERE timestamp >= now() - INTERVAL 1 HOUR
GROUP BY url
ORDER BY media DESC
LIMIT 10;

A integração com ferramentas de BI (Grafana, Metabase, Superset) é direta, permitindo dashboards em tempo real.

6. Estratégias de Integração e Orquestração

Gerenciar múltiplos bancos exige uma camada de abstração e mecanismos de sincronização. Duas abordagens comuns:

Change Data Capture (CDC) com Kafka e Debezium

Permite capturar mudanças em um banco (ex: PostgreSQL) e propagá-las para outros (ex: Elasticsearch para busca, Redis para cache).

// Debezium: conector para PostgreSQL
{
  "name": "postgres-connector",
  "config": {
    "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
    "database.hostname": "postgres",
    "database.port": "5432",
    "database.user": "debezium",
    "database.password": "dbz",
    "database.dbname": "meudb",
    "database.server.name": "my-app-connector",
    "table.include.list": "public.pedidos",
    "plugin.name": "pgoutput"
  }
}

Sagas e Compensações

Para transações distribuídas entre bancos, o padrão Saga com eventos compensatórios é preferível a transações distribuídas (XA), que são lentas e complexas.

// Exemplo de Saga: criar pedido e atualizar estoque
Passo 1: Inserir pedido no PostgreSQL (status: pendente)
Passo 2: Publicar evento "PedidoCriado" no Kafka
Passo 3: Serviço de estoque no Redis debita quantidade
Passo 4: Se estoque insuficiente, publicar evento "EstoqueInsuficiente"
Passo 5: Serviço de pedidos atualiza status para "cancelado" (compensação)

7. Desafios e Boas Práticas na Implementação

Polyglot persistence não é isento de desafios:

  • Complexidade operacional: cada banco exige conhecimento específico de configuração, backup, monitoramento e tuning
  • Custo: licenças, infraestrutura e equipe especializada aumentam
  • Consistência: manter dados sincronizados entre bancos exige cuidado com eventual consistency, idempotência e compensações

Boas práticas incluem:

  1. Definir uma fonte da verdade (source of truth): geralmente o banco relacional ou o banco que recebe os dados originais
  2. Usar CDC e streams para propagar mudanças, evitando sincronização síncrona
  3. Implementar retry e idempotência nos consumidores de eventos
  4. Monitorar latência e divergência entre bancos com ferramentas como Prometheus e Grafana
  5. Documentar claramente qual banco é responsável por qual domínio de dados

Referências