Playbook

CQRS (Command Query Responsibility Segregation) en Sistemas Distribuidos: Evento, Broker y Proyección (CQRS Command Query Responsibility Segregation EN Sistemas Distribuidos Evento Broker Y Proyeccion)

¿Cómo funciona CQRS en sistemas distribuidos? Examine eventos de dominio, intermediario de mensajes, proyección, bandeja de salida, idempotencia y posibles decisiones de coherencia de un extremo a otro.

CQRS (Segregación de responsabilidad de consulta de comando): anatomía de las decisiones

Parte 3 de 4

Distributed CQRS event flow from command to projections and read models

The problem we are solving first

An order has been created. Inventory must know, notifications must send an email, analytics must prepare a report, and search must index the new order.

Order Service
  → HTTP call for Inventory
  → HTTP call for Notifications
  → HTTP call for Analytics
  → HTTP call for Search

Should the Order Service call each of them over HTTP? Every new capability expands the call chain, failure surface, and pressure to deploy together. No. The Order Service should share the business fact that happened; how it is used belongs to each consumer.

En las dos primeras partes de CQRS, separamos cómo procede una solicitud en el lado del comando y en el lado de la consulta. La nueva pregunta en un sistema distribuido es: una vez que se acepta el pedido, ¿cómo se enteran de forma segura los equipos de inventario, notificación, análisis y búsqueda sobre este cambio?text POST /orders → Command Handler → OrderPlaced olayı → Outbox → Relay → Message Broker ├─ Inventory projection ├─ Notification consumer ├─ Analytics projection └─ Search read model Evento es un hecho comercial real e inmutable; OrderPlaced no es una intención, es un hecho que sucedió. Broker mueve el evento del productor al consumidor. El consumidor procesa el evento bajo su propia responsabilidad. Proyección produce una vista adecuada para leer desde el flujo de eventos.

Conceptos en la primera mención```text

📦 Domain event Geçmiş zamanda ifade edilen, iş alanında gerçekleşmiş değişmez bir gerçektir.

📦 Broker Mesajı kalıcılaştıran ve tüketicilere dağıtan iletişim katmanıdır.

📦 Projection Event'lerden belirli bir ekran veya sorgu için üretilen read modeldir.

📦 Idempotency Aynı event iki kez gelirse sonucu ikinci kez değiştirmeme özelliğidir. ```Un comando dice PlaceOrder; Solicita un comportamiento al sistema. Si tiene éxito, se puede publicar OrderPlaced. El comando puede ser rechazado; El evento es un hecho que ya no puedes cambiar.

La entrega del agente suele realizarse al menos una vez: es posible que vuelva a aparecer el mismo mensaje. Por lo tanto, el consumidor debe manejar los duplicados de forma segura ocultando el ID del mensaje o diseñando la transacción para que sea inherentemente idempotente.```text if processedEvents.contains(event.id): return

applyProjection(event) markProcessed(event.id)


## Why the design looks this way

~~~text
PlaceOrder  → an intent.
OrderPlaced → a fact that happened.
~~~

We make this distinction **because** a command can fail, while an event is a completed fact other systems can safely react to.

- **Outbox** is needed **because** the broker and database do not share one ACID transaction.
- A **projection** is needed **because** an admin panel, mobile app, dashboard, and analytics need the same order in different shapes without loading the aggregate.
- **Idempotency** is needed **because** a broker can redeliver the same message for reliable delivery.
- A **saga** is needed **because** inventory must not remain reserved forever when payment fails in e-commerce.
- Most applications that use CQRS do not use Event Sourcing; they are independent decisions.

## ¿Qué cambia cuando se excede el límite de servicio único?

En una sola aplicación, se pueden gestionar transacciones, registro de datos y efectos secundarios en el mismo proceso. Cuando el sistema se distribuye, el inventario, el correo electrónico y los informes tienen sus propios ciclos de vida. Parece fácil a corto plazo que un servicio realice una llamada HTTP sincrónica a otro servicio; Pero cada llamada añade latencia, propagación de errores y presión de implementación conjunta.

El flujo basado en eventos mueve esta dependencia al contrato de datos. El servicio de pedidos es responsable del esquema del evento `OrderPlaced`; El servicio de stock consume sólo la parte que necesita. El productor no conoce la base de datos ni el tiempo de ejecución del consumidor. La capacidad de implementación independiente es simplemente un desplazamiento del riesgo sin observabilidad independiente.

## Eliminar de forma segura el evento del límite de la transacción

El flujo ingenuo es:```text
Save order
  → Commit
  → Publish OrderPlaced
```Si la confirmación se realiza correctamente pero la publicación falla, el sistema conoce el orden, pero los demás servicios no. No publicar y confirmar en orden inverso también produce un evento fantasma. **Patrón de bandeja de salida** requiere dos escrituras en la misma transacción local:```text
Transaction
  → Order kaydını yaz
  → Outbox'a OrderPlaced kaydını yaz
  → Commit

Relay
  → Outbox kaydını broker'a ilet
  → teslim edildi olarak işaretle
```Outbox no establece transacciones distribuidas; Asegura el hecho crítico: si hay una orden en la base de datos, hay un evento por publicar. El relé puede intentarlo nuevamente. Por tanto, la necesidad de idempotencia por parte del consumidor no desaparece.

**CDC** puede capturar cambios del registro de la base de datos. CDC es poderoso para propagar cambios en una base de datos existente; Sin embargo, es posible que tenga un control limitado sobre el idioma del dominio. Outbox, por otro lado, permite a la aplicación seleccionar claramente qué evento tiene significado comercial.

| Pregunta | CDC | Bandeja de salida |
| --- | --- | --- |
| Seleccionar evento por idioma de dominio | Limitado | Claro y completo |
| Enlace a la transacción de la solicitud | Indirecto | Directo |
| Necesidad de la idempotencia del consumidor | Sí | Sí |

## Proyección: vista orientada a la intención, no una copia

El modelo de lectura no es una copia incompleta del modelo de escritura. `ProductSearchRow` se puede generar para una lista de productos y `OrderFulfilmentSummary` se puede generar para el panel de operación. Un mismo evento puede alimentar distintas proyecciones de distintos equipos.```text
OrderPlaced
  → OrderSummaryProjection
  → { orderId, customerName, total, status }

OrderPlaced
  → InventoryProjection
  → { sku, reservedQuantity, availability }
```Las proyecciones deben ser reconstruibles. Cuando el código cambia o se corrige el error, el flujo de eventos se reproduce de manera controlada y se verifica el nuevo modelo de lectura. Para ello se debe medir el orden de los eventos, el punto de control, el versionado y la velocidad de reproducción. El retraso de la proyección debe ser visible en el idioma del producto: ¿Es aceptable que aparezca en la lista unos segundos después de que el usuario realiza un pedido? La respuesta es una decisión empresarial, no técnica.

## Elegir un corredor no es una elección de marca

Corredor; Ofrece garantías de clasificación, persistencia, grupo de consumidores, reproducción y cola de errores. Si el orden es importante para una misma clave, se debe diseñar una clave de partición. Si el consumidor puede quedarse atrás, se deben seguir estrategias de retraso, reintento y letra muerta. Si el esquema está cambiando, agregue un nuevo campo, no elimine el campo anterior rápidamente; La versión del evento debe seguir siendo compatible con versiones anteriores.

## Event Sourcing y CQRS no son lo mismo

CQRS separa las responsabilidades de lectura y escritura. Event Sourcing, por otro lado, produce el estado agregado a partir del flujo de eventos de solo agregar en lugar de la fila actual. Se pueden utilizar juntos; Sin embargo, Event Sourcing no es obligatorio para CQRS y Kafka no es obligatorio para Event Sourcing. Cuando se selecciona Event Sourcing, la instantánea, la versión de transmisión y el costo de reproducción se diseñan por separado.

## Saga: decisión de compensación en el flujo de trabajo distribuido

Un pedido puede pasar por pasos de pago, stock y envío; Estos pasos no pueden caber en una sola transacción ACID. Saga define un paso de compensación para cada operación local.```text
Order placed
  → reserve inventory
  → capture payment
  → create shipment

payment fails
  → release inventory
  → mark order failed
```**Coreografía** permite que los servicios se activen entre sí con eventos; La dependencia local es baja, pero resulta difícil seguir todo el flujo. **Orquestación** hace que el flujo sea visible para el administrador de procesos central; a su vez, añade responsabilidad de coordinación.

## Lista de verificación operativa

1. ¿Se nombra cada evento en tiempo pasado y en lenguaje comercial?
2. ¿Outbox cierra la brecha entre la grabación de bases de datos y la publicación de eventos?
3. ¿Es seguro para los consumidores duplicados, corrupción de colas y eventos tardíos?
4. ¿Se pueden observar el punto de control de proyección, el retraso y el procedimiento de reconstrucción?
5. ¿Están claros el propietario del contrato del evento, la estrategia de lanzamiento y la regla de compatibilidad con versiones anteriores?
6. ¿La eventual ventana de consistencia visible para el usuario es aceptada por el producto?

Si no se responden estas preguntas, el sistema parece impulsado por eventos; pero actúa impulsado por el fracaso.

## Distinciones frecuentemente confusas```text
❌ Event = Command
✓ Command niyettir; event gerçekleşmiş gerçektir.

❌ Broker = Event Store
✓ Broker event'i taşır; Event Store domain geçmişinin kalıcı kaynağı olabilir.

❌ Projection = cache
✓ Cache hız için geçicidir; projection iş sorgusu için bilinçli bir read modeldir.

❌ At-least-once = hata
✓ Tekrar teslimat normaldir; consumer idempotent olmalıdır.
```## Verdadera transmisión de extremo a extremo```text
POST /orders
  → Controller
  → Mediator.Send()
  → Validation + Authorization
  → Transaction Behavior
  → PlaceOrderHandler
  → Order aggregate
  → Order + Outbox commit
  → Relay
  → Broker
  → Projection consumer
  → Read database
  → GET /orders
  → Query Handler
  → OrderSummaryDto
  → Frontend

¿Cuándo debo usar este modelo?

Comience primero con CQRS lógico: nombre los comandos en lenguaje comercial, reduzca las consultas a lo que necesita la pantalla y aclare los límites de la transacción. Los corredores y las proyecciones separadas solo deben agregarse cuando se demuestren consumidores independientes, carga de lectura asimétrica o la necesidad de una integración reproducible.

Aunque el costo de cada nuevo consumidor puede parecer un cambio de código O(1), el costo operativo no es fijo: se requieren contrato, tablero, alarma, reintento, propiedad y escenario de prueba.

Reflexión

La promesa de CQRS distribuido no es más mensajes, sino responsabilidades más visibles. Los eventos llevan la historia, los corredores llevan el flujo y las proyecciones llevan el resultado visible para el usuario.

Una secuencia de eventos se convierte en una decisión arquitectónica solo si es segura cuando llega nuevamente, llega tarde y se reproduce.

En la siguiente sección, veremos qué cambia cuando un sistema falla mientras se está ejecutando: coherencia, duplicados, proyecciones corruptas y estrategias de recuperación.

What should remain with you?

If you remember only five things:

  1. A command requests behavior.
  2. An event is a fact that happened.
  3. A broker carries events to the right consumers.
  4. A projection reads the same fact in the shape each screen needs.
  5. Outbox prevents event loss because the broker and database do not share one ACID transaction.

Every separation in this chapter has a reason: idempotency is needed because a broker can redeliver; a projection is needed because querying an aggregate for every screen is expensive and the wrong abstraction.

FAQ

Frequently asked questions

¿Qué es un evento de dominio?

Expresado en tiempo pasado, es un hecho inmutable que ha ocurrido en el ámbito de los negocios.

¿Qué es un corredor?

Es la capa de comunicación la que perpetúa el mensaje y lo distribuye a los consumidores.

¿Es correcto "Evento=Comando"?

El mando es intención; El evento es real.

Principios de ingeniería aprendidos

  • En un sistema distribuido, un evento no es un comando; Es un hecho comercial establecido e inmutable.
  • El equivalente a una entrega al menos una vez es consumidor idempotente; El mensaje duplicado es un aporte de diseño, no una excepción.
  • La proyección debe ser reconstruible, mensurable y su retraso debe estar definido en el lenguaje del producto.

Continuar leyendo

Continuar leyendo

Siguiente en la serie

Siguiente en la serie

Misma serie

Paylaş