CQRS no es un patrón de arquitectura. Es una apuesta a la complejidad

Este artículo plantea que CQRS suele adoptarse por currículum y no por necesidad real, y ofrece un marco para decidir cuándo separar los modelos de lectura y escritura está justificado y cuándo solo suma complejidad y bugs de sincronización a un CRUD.

Equipo de desarrollo de software en reunión de producción
15 de jul. de 202610 min de leitura
Atualizado em 24 de jul. de 2026

Ya estuviste en esta reunión. Un producto con algunos miles de cuentas pagas, una base de datos Postgres que no está sufriendo, y un design doc que propone un modelo de lectura separado, un modelo de escritura separado, y un event bus para que se hablen entre sí. Preguntás por qué, y la respuesta es alguna versión de "así es como escala". Nadie en la sala pregunta escala a qué, ni de qué número a qué número. Eso es CQRS (Command Query Responsibility Segregation) entrando por la puerta, no porque el sistema tenga un problema que resuelve, sino porque alguien lo leyó en una guía de entrevistas de system design y estaba esperando una excusa para usarlo.

CQRS es una herramienta real. Resuelve problemas reales en empresas reales. También se adopta, constantemente, en equipos que no tienen esos problemas, porque se ve bien en un diagrama de arquitectura y se lee bien en un currículum. El patrón en sí no es el problema. El problema es que casi nadie se detiene a chequear si su sistema tiene de verdad el desajuste que CQRS está diseñado para resolver antes de construirlo.

Qué es CQRS en realidad, sin el hype

CQRS (Command Query Responsibility Segregation) significa que dejás de usar un solo modelo para escribir tus datos y para leerlos de vuelta. Los comandos pasan por un modelo pensado para validación y reglas de negocio. Las consultas pasan por un modelo separado, muchas veces un store separado, pensado para lo que la UI o el reporte realmente necesitan. Esa es toda la idea. No hace falta event sourcing, no hace falta un message bus, aunque la gente los suma todo el tiempo y después llama "CQRS" al paquete completo como si fuera una sola cosa.

Esto es lo que nadie te dice en el diagrama: en el momento en que separás el modelo, te llevaste puesto un problema de sincronización. Tu lado de escritura y tu lado de lectura ahora tienen que estar de acuerdo entre sí, y si no están en la misma transacción, van a estar en desacuerdo de vez en cuando. Eso no es un bug que introdujiste por accidente. Es el trato que firmaste. La pregunta que vale la pena hacerse, cada vez, es si el trato valió la pena.

El circuito de preparación de entrevistas que te está vendiendo este patrón

Fijate dónde se cruza la mayoría de los ingenieros con CQRS por primera vez. No es un postmortem. Es una guía de entrevistas de system design, al lado de sharding y consistent hashing, presentado como algo que un ingeniero senior debería saber y usar. Después vuelve a aparecer en un post de "cómo lo hace Netflix" o "cómo lo hace Uber", describiendo un sistema que maneja millones de eventos por segundo con una base de usuarios global.

Ninguna de esas dos fuentes está mintiendo. CQRS se justifica a esa escala. El problema es lo que pasa después: un ingeniero que acaba de estudiar ese material se une a un equipo que construye una herramienta interna, o un producto B2B SaaS con algunos miles de cuentas pagas, y recurre al mismo patrón porque lo tiene fresco en la cabeza y parece la marca de alguien que piensa la arquitectura en serio. El equipo no tiene el ratio de lectura/escritura de Netflix. No tiene la complejidad de dominio de Uber. Tiene una app CRUD con un dashboard.

Esto es arquitectura de currículum, y vale la pena nombrarlo así directamente en vez de darle vueltas. Adoptar un patrón porque va a quedar bien en tu próxima entrevista, o porque implementarlo se siente más como "ingeniería de verdad" que entregar endpoints CRUD, es una motivación real y común. Solo que no es una razón técnica, y el código no le importa cómo se sintió la decisión en el momento.

Cuándo el desajuste entre lectura y escritura es real de verdad

CQRS es una respuesta legítima a un problema específico y medible: tu carga de lectura y tu carga de escritura quieren escalar distinto, en hardware distinto, a ritmos distintos. Un feed social es el caso canónico. Las escrituras son un goteo: un post, un like, un comentario a la vez. Las lecturas son una inundación: el feed de cada usuario se recalcula o se busca todo el tiempo, ramificándose entre seguidores, filtros y rankings. Tratar de servir ambos desde un solo schema en una sola base de datos significa estar constantemente comprometiendo un lado para proteger al otro.

Si podés mostrar números concretos, logs de requests, gráficos de carga de la base de datos, un patrón de consultas que está degradando el rendimiento de las escrituras, entonces tenés un caso real. Si tu justificación es "lectura y escritura probablemente se van a separar a medida que crezcamos", no tenés un caso. Tenés una suposición, y un CRUD con buenos índices y una read replica va a absorber la mayoría de las suposiciones sin problema.

Cuándo el dominio realmente necesita dos formas distintas

El otro disparador legítimo no tiene nada que ver con la carga y todo que ver con la forma. Algunos dominios tienen un modelo de escritura que no se parece en nada a las consultas que el negocio necesita. Un sistema de trading recibe comandos atómicos (colocar orden, cancelar orden, ejecutar operación) y necesita responder preguntas como "mostrame las ganancias realizadas de esta cuenta por clase de activo en el último trimestre, ajustadas por eventos corporativos". Forzar esa necesidad de reporting a través de las mismas tablas normalizadas que usás para colocar órdenes significa, o un modelo de escritura inflado con preocupaciones de reporting, o consultas tan retorcidas que se convierten en lo que nadie quiere tocar.

Ese es un argumento de complejidad de dominio, y es distinto del argumento de carga. Podés tener uno sin el otro. Un sistema de bajo tráfico con un dominio genuinamente incómodo puede justificar CQRS solo por la forma. Una app CRUD de alto tráfico con un dominio simple generalmente no puede, sin importar cuánta carga reciba, porque escalar lecturas y escrituras a ritmos distintos no requiere modelos distintos, solo infraestructura distinta delante del mismo modelo.

La consistencia eventual es una decisión de negocio, no técnica

Toda implementación de CQRS que separa el store de escritura del store de lectura está apostando a que un retraso entre "la escritura pasó" y "la lectura lo refleja" es aceptable. A veces ese retraso son milisegundos. A veces, bajo carga o con una cola acumulada, es más largo de lo que nadie modeló.

Esto no es algo que ingeniería pueda decidir sola. Si un cliente pide un reembolso y enseguida chequea el saldo de su cuenta y ve el número viejo, ¿eso es un bug report o un trade-off aceptado? Si un encargado de depósito actualiza el inventario y una venta se procesa contra stock desactualizado diez segundos después, ¿quién paga ese costo? Son preguntas de producto y de negocio, y necesitan una respuesta real de alguien con la autoridad para darla, antes de elegir la arquitectura. Si nadie del lado de negocio fue consultado nunca sobre si "la consistencia eventual está bien acá", tomá eso como una señal de que la decisión se tomó en el vacío.

La factura que no ves hasta el mes tres

La versión de CQRS de preparación de entrevistas se salta el costo de mantenimiento, porque las entrevistas no te piden hacer correr algo durante un año. En producción, separar tu modelo significa:

Ahora depurás dos sistemas en vez de uno cada vez que el lado de lectura muestra algo que no coincide con el lado de escritura. Esa clase de bug es especialmente dolorosa porque los dos lados, individualmente, se ven correctos. El projector o el job de sincronización que los mantiene alineados es código nuevo, con sus propios modos de falla, que no existía en la versión de un solo modelo. Los cambios de schema ahora pasan dos veces, una para el lado de comandos y otra para el lado de consultas, y alguien tiene que acordarse de actualizar los dos, en el orden correcto, sin downtime.

Nada de esto descalifica el patrón si el desajuste que estás resolviendo es real. Todo esto es peso muerto, que pagás cada sprint, si adoptaste el patrón porque se veía bien y no porque hacía falta. Una app CRUD que sumó CQRS por valor de currículum ahora tiene dos cosas que mantener sincronizadas para siempre, a cambio de un problema que nunca tuvo.

Una prueba antes de recurrir a CQRS

Antes de separar el modelo, anotá, con precisión, qué se rompe hoy sin eso. No lo que podría romperse a diez veces tu tráfico actual. No lo que un post de blog dijo que se rompe a la escala de otra empresa. Qué se rompe en tu sistema, este trimestre, con tus números reales. Si podés señalar un gráfico de carga o una consulta que de verdad está peleando por recursos con tus escrituras, o un dominio donde la forma de escritura y la forma de lectura se separaron tanto que una consulta es un stored procedure de quinientas líneas que nadie entiende, tenés un caso. Anotá ese caso y conseguí que alguien con autoridad de producto firme el trade-off de consistencia antes de construir nada.

Si en cambio lo que tenés es una corazonada de que CQRS es lo que hacen los sistemas "de verdad", o una entrevista próxima donde querés poder decir que lo implementaste, no lo construyas en tu sistema de producción para averiguarlo. Armá un proyecto de prueba. La app CRUD de tu equipo no necesita cargar ese costo para que puedas ensayar una respuesta.

CQRS es una herramienta legítima para un conjunto acotado de problemas: una divergencia real de carga entre lectura y escritura, o un dominio cuya forma de consulta realmente le quedó grande a su forma de escritura, o ambos. Fuera de eso, no es arquitectura. Es una apuesta a la complejidad hecha con el presupuesto operativo de otro, y la mayoría de los equipos que la hacen nunca tuvieron una mano que valiera la pena apostar.

ESCRITO POR

Logotipo de Howdy.com
Redacción Howdy.com
COMPARTILHAR