Você já esteve nessa reunião. Um produto com alguns milhares de contas pagas, um banco de dados Postgres que não está sofrendo, e um design doc que propõe um modelo de leitura separado, um modelo de escrita separado, e um event bus para que eles conversem entre si. Você pergunta por quê, e a resposta é alguma versão de "é assim que escala". Ninguém na sala pergunta escala para quê, nem de qual número para qual número. Isso é o CQRS (Command Query Responsibility Segregation) entrando pela porta, não porque o sistema tenha um problema que ele resolve, mas porque alguém leu isso num guia de entrevistas de system design e estava esperando uma desculpa para usar.
CQRS é uma ferramenta real. Resolve problemas reais em empresas reais. Também é adotado, constantemente, em equipes que não têm esses problemas, porque fica bonito num diagrama de arquitetura e soa bem num currículo. O padrão em si não é o problema. O problema é que quase ninguém para para checar se o seu sistema tem de verdade o descompasso que o CQRS foi desenhado para resolver antes de construí-lo.
O que o CQRS realmente é, sem o hype
CQRS (Command Query Responsibility Segregation) significa que você deixa de usar um único modelo para escrever seus dados e para lê-los de volta. Os comandos passam por um modelo pensado para validação e regras de negócio. As consultas passam por um modelo separado, muitas vezes um store separado, pensado para o que a UI ou o relatório realmente precisam. É essa a ideia inteira. Não precisa de event sourcing, não precisa de um message bus, embora as pessoas somem isso o tempo todo e depois chamem "CQRS" o pacote completo como se fosse uma coisa só.
É isso que ninguém te conta no diagrama: no momento em que você separa o modelo, você trouxe junto um problema de sincronização. Seu lado de escrita e seu lado de leitura agora precisam concordar entre si, e se não estiverem na mesma transação, vão discordar de vez em quando. Isso não é um bug que você introduziu por acidente. É o acordo que você assinou. A pergunta que vale a pena fazer, sempre, é se o acordo valeu a pena.
O circuito de preparação para entrevistas que está te vendendo esse padrão
Repare onde a maioria dos engenheiros cruza com o CQRS pela primeira vez. Não é num postmortem. É num guia de entrevistas de system design, ao lado de sharding e consistent hashing, apresentado como algo que um engenheiro sênior deveria saber e usar. Depois ele reaparece num post de "como o Netflix faz" ou "como o Uber faz", descrevendo um sistema que processa milhões de eventos por segundo com uma base de usuários global.
Nenhuma dessas duas fontes está mentindo. O CQRS se justifica nessa escala. O problema é o que acontece depois: um engenheiro que acabou de estudar esse material entra numa equipe que constrói uma ferramenta interna, ou um produto B2B SaaS com alguns milhares de contas pagas, e recorre ao mesmo padrão porque está com aquilo fresco na cabeça e parece a marca de alguém que leva arquitetura a sério. A equipe não tem a proporção de leitura/escrita da Netflix. Não tem a complexidade de domínio da Uber. Tem um app CRUD com um dashboard.
Isso é arquitetura de currículo, e vale a pena chamar assim diretamente em vez de enrolar. Adotar um padrão porque vai ficar bem na sua próxima entrevista, ou porque implementá-lo parece mais "engenharia de verdade" do que entregar endpoints CRUD, é uma motivação real e comum. Só que não é uma razão técnica, e o código não liga para como a decisão pareceu no momento.
Quando o descompasso entre leitura e escrita é real de verdade
CQRS é uma resposta legítima para um problema específico e mensurável: sua carga de leitura e sua carga de escrita querem escalar de formas diferentes, em hardware diferente, em ritmos diferentes. Um feed social é o caso canônico. As escritas são um gotejar: um post, um like, um comentário de cada vez. As leituras são uma enchente: o feed de cada usuário é recalculado ou buscado o tempo todo, ramificando entre seguidores, filtros e rankings. Tentar atender os dois a partir de um único schema num único banco de dados significa estar constantemente sacrificando um lado para proteger o outro.
Se você consegue mostrar números concretos, logs de requests, gráficos de carga do banco de dados, um padrão de consultas que está degradando a performance das escritas, então você tem um caso real. Se a sua justificativa é "leitura e escrita provavelmente vão se separar conforme a gente crescer", você não tem um caso. Você tem uma suposição, e um CRUD com bons índices e uma read replica vai absorver a maioria das suposições sem problema.
Quando o domínio realmente precisa de duas formas distintas
O outro gatilho legítimo não tem nada a ver com carga e tudo a ver com forma. Alguns domínios têm um modelo de escrita que não se parece em nada com as consultas de que o negócio precisa. Um sistema de trading recebe comandos atômicos (colocar ordem, cancelar ordem, executar operação) e precisa responder perguntas como "me mostra os ganhos realizados dessa conta por classe de ativo no último trimestre, ajustados por eventos corporativos". Forçar essa necessidade de reporting através das mesmas tabelas normalizadas que você usa para colocar ordens significa, ou um modelo de escrita inflado com preocupações de reporting, ou consultas tão retorcidas que viram aquilo que ninguém quer tocar.
Esse é um argumento de complexidade de domínio, e é diferente do argumento de carga. Você pode ter um sem o outro. Um sistema de baixo tráfego com um domínio genuinamente incômodo pode justificar CQRS só pela forma. Um app CRUD de alto tráfego com um domínio simples geralmente não pode, não importa quanta carga receba, porque escalar leituras e escritas em ritmos diferentes não exige modelos diferentes, só infraestrutura diferente na frente do mesmo modelo.
A consistência eventual é uma decisão de negócio, não técnica
Toda implementação de CQRS que separa o store de escrita do store de leitura está apostando que um atraso entre "a escrita aconteceu" e "a leitura reflete isso" é aceitável. Às vezes esse atraso é de milissegundos. Às vezes, sob carga ou com uma fila acumulada, é maior do que ninguém modelou.
Isso não é algo que a engenharia possa decidir sozinha. Se um cliente pede um reembolso e logo depois confere o saldo da conta e vê o número antigo, isso é um bug report ou um trade-off aceito? Se um responsável de depósito atualiza o estoque e uma venda é processada contra estoque desatualizado dez segundos depois, quem paga esse custo? São perguntas de produto e de negócio, e precisam de uma resposta real de alguém com autoridade para dá-la, antes de escolher a arquitetura. Se ninguém do lado de negócio jamais foi consultado sobre se "a consistência eventual está de boa aqui", tome isso como um sinal de que a decisão foi tomada no vácuo.
A conta que você não vê até o terceiro mês
A versão de CQRS de preparação para entrevistas pula o custo de manutenção, porque entrevistas não pedem que você mantenha algo rodando por um ano. Em produção, separar o seu modelo significa:
Agora você depura dois sistemas em vez de um toda vez que o lado de leitura mostra algo que não bate com o lado de escrita. Esse tipo de bug é especialmente doloroso porque os dois lados, individualmente, parecem corretos. O projector ou o job de sincronização que os mantém alinhados é código novo, com seus próprios modos de falha, que não existia na versão de modelo único. As mudanças de schema agora acontecem duas vezes, uma para o lado de comandos e outra para o lado de consultas, e alguém precisa lembrar de atualizar os dois, na ordem certa, sem downtime.
Nada disso desqualifica o padrão se o descompasso que você está resolvendo é real. Tudo isso é peso morto, que você paga a cada sprint, se você adotou o padrão porque parecia bonito e não porque era necessário. Um app CRUD que somou CQRS por valor de currículo agora tem duas coisas para manter sincronizadas para sempre, em troca de um problema que nunca teve.
Um teste antes de recorrer ao CQRS
Antes de separar o modelo, anote, com precisão, o que quebra hoje sem isso. Não o que poderia quebrar a dez vezes o seu tráfego atual. Não o que um post de blog disse que quebra na escala de outra empresa. O que quebra no seu sistema, neste trimestre, com os seus números reais. Se você consegue apontar um gráfico de carga ou uma consulta que está realmente brigando por recursos com as suas escritas, ou um domínio onde a forma de escrita e a forma de leitura se separaram tanto que uma consulta é um stored procedure de quinhentas linhas que ninguém entende, você tem um caso. Anote esse caso e consiga que alguém com autoridade de produto assine o trade-off de consistência antes de construir qualquer coisa.
Se, em vez disso, o que você tem é um pressentimento de que CQRS é o que os sistemas "de verdade" fazem, ou uma entrevista chegando em que você quer poder dizer que implementou isso, não construa no seu sistema de produção para descobrir. Monte um projeto de teste. O app CRUD da sua equipe não precisa carregar esse custo só para você treinar uma resposta.
CQRS é uma ferramenta legítima para um conjunto delimitado de problemas: uma divergência real de carga entre leitura e escrita, ou um domínio cuja forma de consulta realmente ficou grande demais para sua forma de escrita, ou os dois. Fora disso, não é arquitetura. É uma aposta na complexidade feita com o orçamento operacional de outra pessoa, e a maioria das equipes que faz isso nunca teve uma mão que valesse a pena apostar.




