A cozinha e o salão do restaurante
O garçom anota o pedido e o entrega à cozinha (command side): lá existem regras, ordem e controle. O salão consulta o painel de 'pedidos prontos' (read model): uma visão simples, feita para ser lida de relance. Ninguém entra na cozinha para saber se o prato saiu — e o painel atualiza alguns segundos depois do fogão (consistência eventual).
O problema
Um único modelo servindo leitura e escrita é um mau compromisso para os dois lados: as regras de escrita pedem normalização, invariantes e consistência forte; as telas de leitura pedem dados agregados, desnormalizados e rápidos.
E os dois lados escalam diferente — quase sempre há muito mais leituras. Presos ao mesmo modelo e à mesma base, otimizar um tende a piorar o outro.
Os sintomas aparecem cedo: joins caros para montar telas, caches invalidados a cada escrita, índices de leitura que deixam o INSERT lento, e um ORM tentando servir dois senhores.
Não confunda com CQS. CQS (Bertrand Meyer) é um princípio de nível de método: um método ou é comando (muda estado) ou é query (retorna dado) — nunca ambos. CQRS eleva a mesma ideia à arquitetura: modelos, serviços e possivelmente bancos inteiros separados por responsabilidade. CQS é sempre saudável e de graça; CQRS é uma decisão arquitetural com custo real.
O preço principal do CQRS é a consistência eventual: entre a escrita e a projeção existe uma janela em que a leitura está desatualizada. Todo o design de UX e de contratos precisa assumir essa janela — de 'seu pedido está sendo processado' a versões/etags.
- Comando
- valida regrasWrite model
- publicaEvento / Projeção
- atualizaRead model
- serveConsulta
Veja a consistência eventual acontecendo
Mexa nos controles: a teoria acima vira comportamento observável.
Command side (escrita)
Query side (leitura)
O comando muda o write store na hora; o read model é atualizado pela projeção. Por isso a leitura pode ficar um instante atrás (consistência eventual).
Navegue pelos lados
Camada 1/3Command side (escrita)
use ← →Camada 1
Command side (escrita)
intenções passam por regras
Recebe comandos ('criar pedido', 'alterar endereço'), valida invariantes de negócio e persiste no write store — normalizado e consistente. É o único lugar do sistema autorizado a mudar estado.
No código
commandHandler.handle(new CriarPedido(itens));Se violar: se a tela escrever direto no banco, as invariantes deixam de valer e o read model diverge sem ninguém perceber.
Fluxo completo (sequência)
Código
// Command side: recebe intenção, valida e persiste
interface Command { tipo: string; }
class CriarPedido implements Command {
readonly tipo = "CriarPedido";
constructor(public itens: string[]) {}
}
class CommandHandler {
constructor(private writeStore: WriteStore, private bus: EventBus) {}
async handle(cmd: CriarPedido): Promise<void> {
const pedido = Pedido.novo(cmd.itens); // regras de negócio
await this.writeStore.salvar(pedido);
this.bus.publicar({ tipo: "PedidoCriado", pedido });
}
}
// Query side: lê de um read model desnormalizado
class ConsultaPedidos {
constructor(private readStore: ReadStore) {}
porCliente(clienteId: string) {
return this.readStore.listar(clienteId); // sem regras, só leitura
}
}// Command side
record CriarPedido(List<String> itens) {}
class CommandHandler {
private final WriteStore writeStore;
private final EventBus bus;
CommandHandler(WriteStore ws, EventBus bus) { this.writeStore = ws; this.bus = bus; }
void handle(CriarPedido cmd) {
Pedido pedido = Pedido.novo(cmd.itens()); // regras de negócio
writeStore.salvar(pedido);
bus.publicar(new PedidoCriado(pedido));
}
}
// Query side: lê read model otimizado
class ConsultaPedidos {
private final ReadStore readStore;
ConsultaPedidos(ReadStore rs) { this.readStore = rs; }
List<PedidoView> porCliente(String clienteId) {
return readStore.listar(clienteId); // só leitura
}
}Casos de uso
Dashboard pesado sobre operação transacional
Cenário
Um marketplace tem checkout transacional e um painel do vendedor com agregações (vendas por dia, top produtos, funil).
Como o padrão entra
As escritas seguem no modelo normalizado; projeções alimentam tabelas agregadas por painel. O dashboard lê pré-calculado em milissegundos, sem competir com o checkout.
Trade-off: Os números do painel podem atrasar alguns segundos — aceitável para analytics, inaceitável para saldo; escolha por visão.
Busca com tecnologia dedicada
Cenário
O catálogo vive em SQL, mas a busca precisa de relevância, facetas e typo-tolerance.
Como o padrão entra
Eventos de produto projetam para um índice (ex.: OpenSearch/Meilisearch). O command side não sabe que a busca existe; o read model é um índice inteiro em outra tecnologia.
Trade-off: Mais uma peça para operar e reindexar; o replay de eventos vira a ferramenta de reconstrução.
Auditoria e Event Sourcing
Cenário
Um sistema financeiro precisa provar como cada saldo chegou ao valor atual.
Como o padrão entra
Com CQRS o passo para Event Sourcing é curto: o write store passa a ser o log de eventos, e todos os read models (saldo, extrato, relatórios) são projeções reconstruíveis do log.
Trade-off: Event Sourcing amplifica custos (versionamento de eventos, replay, GDPR) — só vale onde o histórico é requisito, não luxo.
Armadilhas
CQRS em CRUD simples
Duplicar modelos, handlers e projeções para um cadastro básico só multiplica arquivos. Se um modelo único serve bem os dois lados, aplique apenas CQS nos métodos e siga em frente.
Fingir que a leitura é imediata
UI que lê logo após escrever e espera ver o dado novo quebra com projeção assíncrona. Desenhe para a janela: confirme com o resultado do comando, use 'processando…', ou leia a própria resposta da escrita.
Read model como cópia 1:1
Projetar o read model idêntico ao write model desperdiça o padrão — o ganho vem de moldar cada visão à tela que a consome. Cópia espelhada é só replicação com passos extras.
Quando usar × quando evitar
Use quando
3- Leitura e escrita têm requisitos de modelo, carga ou escala muito diferentes.
- Há muitas visões de leitura distintas sobre os mesmos dados de escrita.
- Combina com Event Sourcing, integração por eventos ou domínios complexos (DDD).
Evite quando
2- CRUDs e domínios simples — a duplicação de modelos vira complexidade sem retorno.
- Quando toda leitura precisa refletir a escrita imediatamente (consistência forte ponta a ponta).
Testar o que ficou
3 armadilhas de CQRS (Command Query Responsibility Segregation) viram perguntas, com o nome do padrão escondido, misturadas com padrões parecidos.
Onde isto aparece de verdade
Você já usa isto, talvez sem saber o nome.
réplica de leituraEscritas vão para o primário e as consultas para réplicas: dois caminhos com modelos e escala próprios para ler e para gravar.Elasticsearch ao lado do bancoO banco transacional guarda a verdade das escritas e um índice de busca serve as leituras — modelos de leitura e escrita separados de fato.materialized viewsA view materializada é um modelo de leitura pré-computado, alimentado a partir das escritas e otimizado só para consultar rápido.
O padrão em uma linha
Sem nome de domínio, sem cerimônia. Só a ideia.
// Modelo de escrita ≠ modelo de leitura.
await comandos.criarPedido(cmd);
const view = await leituras.pedidoDetalhe(id);O que ele cobra
2 saltos a mais entre a chamada e o efeito.
- Dois modelos a manter, de escrita e de leitura, que podem divergir enquanto sincronizam
- A leitura passa a ser eventualmente consistente em relação à escrita
Não vale se: leitura e escrita cabem no mesmo modelo sem se atrapalhar — separar aí dobra o trabalho sem resolver gargalo nenhum.
Costuma ser confundido com
Onde isto aparece
Continue por aqui
- CQS (Command Query Separation)Todo método deve ser ou um comando que muda estado e não retorna dado, ou uma query que retorna dado sem efeito colateral — nunca os dois ao mesmo tempo.
- SagaCoordena uma transação distribuída de longa duração como uma sequência de transações locais, cada uma com uma ação compensatória para desfazer semanticamente em caso de falha.
- Arquitetura HexagonalIsola o domínio no centro e o conecta ao mundo externo por portas (interfaces) e adaptadores (implementações), tornando infraestrutura um detalhe plugável.