CQRS es un problema de read model, no de write model

Las demos de CQRS se centran en command handlers y event stores. CQRS en producción falla en el lado de lectura — lag de proyección, drift semántico y consultas que nadie diseñó. El read model es la parte difícil; el write path es la fácil.

Ingeniería7 min de lectura
CQRSArquitectura de softwareRead modelsEvent sourcingSistemas distribuidos
Compartir

Command Query Responsibility Segregation separa escrituras de lecturas para que cada lado optimice de forma independiente. Las charlas de conferencia hacen sonar esto como una ceremonia del lado de escritura: agregados, command buses, event stores. Los incidentes de producción cuentan otra historia. Los problemas del read model en CQRS — proyecciones stale, drift entre verdad de comandos y tablas de consulta, dashboards mostrando datos que contradicen lo que el usuario acaba de guardar — generan los tickets de soporte. El write path suele ser una mutación CRUD bien testeada con pasos extra. El read path es un warehouse desnormalizado sin dueño, actualizado asíncronamente por un worker de proyección que se quedó atrás durante el deploy.

Los equipos que adoptan CQRS partiendo el write model primero heredan consistencia eventual sin diseñarla. Obtienen dos bases de datos, un event bus, y lag medido en segundos que se convierte en minutos bajo carga — sin reconciliación, sin contrato de frescura, y sin respuesta cuando un usuario pregunta "¿por qué no aparece mi cambio?"

El CQRS del lado de escritura es lo que los equipos ya saben hacer

El write model en CQRS es lógica de dominio que el equipo ya escribía — encapsulada en agregados o servicios, validada, persistida. Agregar commands y events cambia el transporte, no la dificultad fundamental. La mayoría de las reglas de negocio ya vivían en el write path: invariantes, autorización, validación.

La parte seductora es la pureza arquitectónica. Renombrar updateOrder a ShipOrderCommand, emitir OrderShipped, sentirse maduro. El read model se difiere: "proyectaremos a una tabla de consulta después." Después llega cuando el primer dashboard lee de orders_view que va tres segundos detrás de orders y soporte abre un bug que no se reproduce en el write side.

CQRS sin estrategia de read model son dos bases de datos y una plegaria.

La consistencia eventual no es un delay fijo. Bajo carga normal, el lag de proyección puede ser 50 ms. Bajo deploy, rebalanceo de broker o reinicio del worker de proyección, el lag se estira a segundos o minutos. El sistema promete consistencia eventualmente — no cuándo. Consumidores que asumen frescura — humanos refrescando una página, agentes leyendo estado para decidir la siguiente acción — se rompen en silencio.

El read model define para qué sirve CQRS

El read model existe porque las consultas tienen forma distinta a las escrituras. Un write almacena filas de pedidos normalizadas. Un read necesita user_orders_view con nombre del cliente, títulos de ítems y estado pre-joineados — cero joins en tiempo de consulta, lecturas sub-milisegundo al costo de actualizaciones asíncronas.

Diseñar el read model significa responder preguntas que el write model no hace:

  • ¿Qué consultas corre el producto? Vistas de lista, páginas de detalle, búsqueda, agregaciones, exports — cada una puede necesitar una proyección distinta.
  • ¿Qué staleness es aceptable por consulta? Dashboard: 5 segundos puede estar bien. Confirmación de pago: casi cero — leer del write model o proyección síncrona.
  • ¿Quién es dueño de la corrección de proyecciones? Cuando orders_view.total discrepa de orders.total, ¿cuál es verdad y quién arregla el pipeline?
  • ¿Cómo se detecta el drift? Jobs de reconciliación comparando estado del write side con proyecciones en schedule — no descubierto por usuarios.
Superficie de consultaTolerancia de stalenessRead path
Dashboard admin1–5 segundosTabla de proyección
Historial de pedidos del cliente< 1 segundoProyección con monitor de lag
Recibo de pagoCeroWrite model o read síncrono
Índice de búsqueda10–60 segundosProyección async + rebuild
Consulta de herramienta de agenteDepende de autonomíaContrato de frescura requerido

El patrón de event sourcing sin ceremonia cubre logs append ligeros con proyecciones inline. CQRS completo agrega múltiples read models por write stream — cada uno una decisión de producto, no un default de framework.

El lag de proyección es un bug de corrección, no una métrica de performance

Los equipos monitorean lag de offset del broker y lo llaman saludable a 10.000 eventos atrás. Los usuarios lo llaman roto cuando su actualización no aparece. Importan métricas de lag con significado de negocio: "delay de visibilidad de pedidos p99 en segundos," "frescura del índice de búsqueda," "edad de datos del dashboard."

Modos de fallo:

Desaceleración gradual. El worker de proyección procesa menos eventos por segundo de los que produce el write side. El lag crece de segundos a horas antes de que alguien lo note. El read model está materialmente incorrecto; ponerse al día toma más de lo que los stakeholders toleran.

Eventos veneno. Un evento malformado crashea el handler de proyección. El procesamiento se detiene. El write side continúa. La divergencia se compone.

Drift semántico. El write model evoluciona — campos nuevos, estados renombrados. El código de proyección se queda atrás. El read model habla un idioma distinto al command side. No desacuerdo de timing — desacuerdo de significado.

Los jobs de reconciliación reparan drift: comparar verdad del write side con estado de proyección, emitir correcciones, alertar en mismatch. Construí reconciliación antes del primer incidente, no después. El patrón es el mismo que logs append de event sourcing — verdad en un lado, estado derivado en el otro, camino de reparación explícito.

CQRS y agentes de IA se rompen con lecturas stale

Agentes autónomos leen estado, deciden, actúan, leen de nuevo. La consistencia eventual convierte el loop en drift de contexto: el agente actúa sobre datos stale, observa datos stale otra vez, replanifica incorrectamente, quema tokens en acciones contradictorias. Los humanos refrescan y esperan. Los agentes confían en lo que leen.

Los read models orientados a agentes necesitan contratos de frescura explícitos:

  • Devolver timestamp projected_at con cada respuesta de consulta.
  • Rechazar o marcar lecturas cuando el lag excede umbral para workflows autónomos.
  • Enrutar consultas de agente de alto riesgo al write model o path síncrono.

CQRS para dashboards humanos tolera segundos de lag. CQRS para orquestación de agentes sin garantías de frescura es un accidente esperando un trace de producción.

¿Cuándo justifica CQRS la complejidad del read model?

Estas preguntas determinan si CQRS gana su costo operacional para un bounded context.

¿Debería este bounded context usar CQRS?

CQRS se justifica cuando los perfiles de carga de lectura y escritura divergen fuertemente — muchas lecturas por escritura, joins costosos, múltiples formas de consulta sobre los mismos datos — y cuando el equipo puede aceptar consistencia eventual para la mayoría de consultas con excepciones explícitas. Saltá CQRS cuando los patrones de lectura y escritura son similares, cuando se requiere consistencia fuerte en todos lados, o cuando el equipo no tiene capacidad para ser dueño de proyecciones y reconciliación.

¿Cuántos read models son suficientes?

Un read model por patrón de consulta distinto — no uno por pantalla. order_list_view y order_detail_view pueden fusionarse si la vista de detalle es lookup por clave en la misma tabla desnormalizada. Índices de búsqueda, agregados analíticos y formatos de export son read models separados con contratos de staleness separados.

¿Qué pasa cuando proyección y write model discrepan?

El write model gana. Las proyecciones son derivadas y reparables. Ejecutá reconciliación, identificá la fuente de divergencia, replay de eventos si hace falta, arreglá código de proyección, documentá el incidente. Nunca "arregles" el write model para coincidir con una proyección stale. El monolito modular puede hospedar CQRS dentro de límites de módulo antes de que la red multiplique lag y dominios de fallo.

Un argumento común va en la otra dirección

La postura opuesta sostiene que CQRS es over-engineering — que read replicas, materialized views y caching resuelven escalado de lectura sin modelos separados de command y query.

Read replicas y materialized views funcionan para muchos sistemas. CQRS agrega valor cuando múltiples read models heterogéneos deben evolucionar independientemente de un write stream, cuando replay de eventos debe reconstruir proyecciones tras cambios de lógica, o cuando escalado de escritura y lectura requiere tecnologías de almacenamiento distintas. El error es aplicar CQRS por arquitectura orientada al currículum cuando read replicas de Postgres bastarían.

Key takeaways

  • Los fallos de CQRS en producción son fallos de read model: lag, drift, reconciliación faltante.
  • Diseñá read models primero — formas de consulta, contratos de staleness, ownership.
  • El lag de consistencia eventual es ilimitado bajo carga; monitoreá frescura con significado de negocio.
  • Jobs de reconciliación comparan verdad del write con proyecciones antes de que usuarios encuentren drift.
  • Consumidores agente necesitan metadata de frescura; lecturas stale causan errores autónomos.
  • La ceremonia del write side es fácil; las operaciones del read side son donde CQRS vive o muere.

Conclusion

El entrenamiento en CQRS enfatiza commands. Las operaciones de CQRS enfatizan proyecciones. Los equipos que tienen éxito empiezan con las consultas que su producto realmente ejecuta, definen cuán stale puede ser cada respuesta, y construyen reconciliación antes de que el worker de proyección se quede atrás por primera vez.

La pregunta de la revisión arquitectónica no es "¿deberíamos separar lecturas y escrituras?" Es "¿qué read models necesitamos, quién es dueño, y qué pasa cuando mienten?" Respondé eso antes de partir el write path. El write side nunca fue la parte difícil.

Artículos relacionados

Paleta de comandos

Buscá un comando para ejecutar...