Polyglot persistence e materialized views

TL;DR

Persistência poliglota é a ideia de que não existe o banco certo — existe o banco certo para cada carga: relacional para transações, documento para agregados, chave-valor para cache/sessão, motor de busca para texto livre, grafo para relacionamentos, colunar para analytics. Um sistema sério combina vários. E como manter dados sincronizados entre eles sem afogar a escrita? Com materialized views / read models: cópias desnormalizadas e pré-computadas de uma consulta, mantidas atualizadas — o coração do CQRS, que separa o modelo de escrita (normalizado, transacional) dos modelos de leitura (um por consulta, prontos para ler). O ganho é cada carga na ferramenta ideal; o preço é complexidade operacional (N bancos para rodar, monitorar e proteger) e consistência eventual (o read model atrasa em relação à escrita). A armadilha-mãe: poliglota prematuro — cinco bancos onde um Postgres bem-ajustado resolveria.

Um banco só é sempre um compromisso

Um sistema real tem necessidades que puxam para lados opostos. O checkout precisa de transações ACID (relacional). O carrinho e a sessão precisam de leitura/escrita rápida por chave (chave-valor). A busca de produtos precisa de texto livre, ranking e faceting (Elasticsearch). O feed de recomendação precisa navegar relacionamentos (grafo). O dashboard executivo precisa de agregações sobre milhões de linhas (colunar/OLAP). Nenhum banco é excelente em tudo — escolher um é aceitar que ele será medíocre em várias dessas cargas.

A persistência poliglota é aceitar essa realidade de frente: use, no mesmo sistema, o motor certo para cada padrão de acesso. O termo é de Fowler/Sadalage, e a era da nuvem gerenciada o tornou barato — subir um DynamoDB + um Elasticsearch + um Postgres é questão de Terraform, não de meses. O que não ficou barato foi operá-los todos.

Materialized views: o dado pré-computado

Se o mesmo fato vive em vários bancos, alguém precisa mantê-los em sincronia — e reconstruir a visão de leitura a cada request (com joins caros) mataria a performance. A resposta é a materialized view: o resultado de uma consulta calculado uma vez e guardado pronto, desnormalizado, atualizado quando a fonte muda. No banco relacional é o MATERIALIZED VIEW (Postgres) que você dá REFRESH; na arquitetura, é o read model — uma projeção desenhada para uma tela, alimentada pelos eventos da escrita.


graph LR
    W["Modelo de escrita<br/>(relacional, ACID)"] -->|"eventos de mudança"| BUS{{"stream / eventos"}}
    BUS --> R1["read model: busca<br/>(Elasticsearch)"]
    BUS --> R2["read model: cache<br/>(Redis)"]
    BUS --> R3["view: dashboard<br/>(colunar)"]

    style W fill:#4A90D9,color:#fff
    style R1 fill:#F5A623,color:#000
    style R2 fill:#F5A623,color:#000
    style R3 fill:#F5A623,color:#000

Essa separação escrita normalizada → leituras desnormalizadas é o CQRS (Command Query Responsibility Segregation): comandos alteram o modelo de escrita; consultas leem dos read models otimizados. É o mesmo espírito do Command do GoF — separar a intenção de mudança da de leitura — agora no nível da arquitetura de dados. A propagação por eventos (event sourcing / change data capture) é o que mantém as views vivas; o assunto ganha corpo próprio na família de Padrões de Eventos, ainda por escrever.

Fundamento: o teorema que cobra a conta

Por que o read model atrasa? Porque distribuir o mesmo dado por vários bancos esbarra no teorema CAP: sob partição de rede, você escolhe entre consistência e disponibilidade. Manter N réplicas fortemente consistentes exigiria coordenação síncrona que anula o ganho de ter bancos separados. Na prática, escolhe-se consistência eventual: a escrita confirma no modelo transacional, e os read models convergem logo depois (milissegundos a segundos). O sistema fica, por uma janela, com o dashboard mostrando um número e o banco de escrita já noutro. Poliglota é um sistema distribuído — e herda todas as durezas de um: ordering de eventos, reprocessamento, idempotência, a janela de inconsistência. Ignorar isso é a origem das duas armadilhas seguintes.

Armadilhas comuns

Poliglota prematuro

O que acontece: um sistema jovem, com carga modesta, nasce com cinco bancos “porque é a arquitetura certa” — e a equipe gasta a energia integrando armazenamentos em vez de entregar produto. Por quê: cada banco novo é um imposto fixo (operação, expertise, integração) cobrado desde o dia 1, independente da escala. E um Postgres moderno já faz documento (JSONB), chave-valor, busca full-text (tsvector), fila (SKIP LOCKED) e materialized views nativas — cobrindo muita carga antes de você precisar de um motor especializado. Como evitar: comece monolítico no dado (um relacional bem-ajustado), e introduza cada banco novo quando um access pattern medido justificar — não por antecipação. Poliglota é resposta a uma dor real de escala, não ponto de partida.

Consistência eventual mal gerida

O que acontece: o usuário salva algo, é redirecionado para uma tela que lê do read model, e não vê a própria alteração — porque a projeção ainda não convergiu. O bug “sumiu o que acabei de criar”. Por quê: o read model é eventualmente consistente, e a UI foi desenhada como se fosse imediata. A janela de atraso existe por design, mas ninguém a tratou. Como evitar: projete para a janela — read-your-writes (ler do modelo de escrita logo após uma alteração), UI otimista, ou indicar “processando”. Torne a inconsistência visível e finita, nunca uma suposição silenciosa de imediatismo.

Sincronização frágil entre os bancos

O que acontece: o pipeline que alimenta os read models falha em silêncio (um evento perdido, um consumidor travado), e as visões divergem da fonte sem ninguém perceber — até um relatório não bater. Por quê: manter N cópias em sincronia é um problema distribuído de verdade; sem idempotência, reprocessamento e monitoramento de lag, a deriva é questão de tempo. Como evitar: trate a sincronização como componente de primeira classe — consumidores idempotentes, capacidade de reconstruir a view do zero a partir da fonte (o read model é descartável por design), e alarme sobre o lag de replicação. A fonte de verdade é uma; as views são derivações recriáveis.

Como explicar em inglês

“Polyglot persistence is the idea that there’s no single right database — there’s a right database per workload: relational for transactions, document for aggregates, key-value for cache, a search engine for full-text, a graph for relationships, columnar for analytics. A serious system combines several. To keep data in sync across them without drowning writes, you use materialized views or read models — denormalized, precomputed copies of a query kept up to date. That’s the heart of CQRS: separate the write model, normalized and transactional, from read models optimized per query, propagated by events. The payoff is each workload on its ideal engine; the price is operational complexity — N databases to run, monitor, and secure — and eventual consistency, since the read model lags the write. The big trap is premature polyglot: five stores where a well-tuned Postgres, which already does JSONB, key-value, full-text, and materialized views, would do. Polyglot is an answer to a measured scaling pain, not a starting point.”

PTEN
persistência poliglotapolyglot persistence
visão materializadamaterialized view
modelo de leituraread model
segregação leitura/escrita (CQRS)command-query responsibility segregation
consistência eventualeventual consistency
janela de inconsistênciainconsistency window
ler-suas-escritasread-your-writes

O que vem a seguir

Isto fecha a família Acesso a Dados — do descasamento objeto↔relacional até a persistência poliglota. Vale amarrar o mapa da escolha, que é a pergunta sênior de verdade:

O próximo passo do catálogo é a família de Integração Empresarial (EIP) — como os sistemas que escolhemos aqui conversam entre si por mensagens.

Veja também

  • Engenharia de Dados — pipelines, CDC e a Modern Data Stack que materializam views em escala.
  • Cloud — os bancos gerenciados que tornaram o poliglota barato de provisionar (e caro de operar).

Fontes

  • Martin FowlerPolyglot Persistence — o termo e a tese do “banco certo para cada carga”.
  • Martin FowlerCQRS e Reporting Database — read models e views materializadas.
  • Pramod Sadalage & Martin FowlerNoSQL Distilled (2012) — persistência poliglota como conclusão do livro.