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:
- Definir uma fonte da verdade (source of truth): geralmente o banco relacional ou o banco que recebe os dados originais
- Usar CDC e streams para propagar mudanças, evitando sincronização síncrona
- Implementar retry e idempotência nos consumidores de eventos
- Monitorar latência e divergência entre bancos com ferramentas como Prometheus e Grafana
- Documentar claramente qual banco é responsável por qual domínio de dados
Referências
- Martin Fowler - Polyglot Persistence — Artigo seminal de Martin Fowler definindo o conceito e seus benefícios
- MongoDB Documentation - Data Modeling — Guia oficial sobre modelagem de dados para bancos documentais
- Neo4j Graph Database - Use Cases — Exemplos reais de uso de bancos de grafos em redes sociais, fraudes e recomendações
- Debezium Documentation - CDC with PostgreSQL — Tutorial completo de Change Data Capture usando Debezium e Kafka
- TimescaleDB - Time-Series Data Best Practices — Guia prático para modelagem de séries temporais em bancos analíticos
- Redis - Caching Patterns — Padrões de cache e sessão usando Redis, com exemplos de código
- ClickHouse - Documentation — Documentação oficial do ClickHouse, incluindo exemplos de consultas analíticas