Pregunta

Todavía estoy luchando con lo que debe ser básico (y resuelto) temas relacionados con la arquitectura de estilo CQRS:

¿Cómo implementamos las reglas de negocio que se basan en un conjunto de Raíces de agregado?

Tomemos, por ejemplo, una solicitud de reserva. Puede que le permiten reservar entradas para un concierto, asientos para una película o una mesa en un restaurante. En todos los casos, no sólo va a ser un número limitado de '' artículos para la venta.

Imaginemos que el evento o lugar es muy popular. Cuando las ventas se abren para un nuevo evento o ranura de tiempo, las reservas comienzan a llegar muy rápidamente - tal vez muchos por segundo

.

En el lado de consulta que puede incrementar de forma masiva, y las reservas se ponen en una cola para ser manejado de forma asíncrona por un componente autónomo. Al principio, cuando salga de reserva comandos de la cola de las aceptaremos, pero en cierto momento vamos a tener que empezar rechazando el resto .

¿Cómo sabemos cuando alcanzamos el límite?

Para cada reserva Comando tendríamos que consultar algún tipo de tienda para averiguar si podemos acomodar la solicitud. Esto significa que vamos a necesitar saber cuántas reservas que ya hemos recibido en ese momento.

Sin embargo, si la tienda de dominio es almacenar un datos no relacionales tales como, por ejemplo, Tabla de almacenamiento de Windows Azure, que no puede muy bien hacer un SELECT COUNT(*) FROM ...

Una opción sería la de mantener una separada agregada raíz que simplemente se hace un seguimiento de la cuenta corriente, así:

  • AR: Reserva (?? Que cuántos)
  • AR: / Hora del evento ranura / Fecha (recuento global)

El segundo agregado de raíz sería una agregación desnormalizado de la primera, pero cuando el almacén de datos subyacente no admite transacciones, entonces es muy probable que estos se pierden la sincronización en escenarios de gran volumen (que es lo que nosotros tratan de abordar en primer lugar).

Una posible solución es serializar el manejo de la Reserva de comandos para que sólo uno a la vez se maneja, pero esto va en contra de nuestros objetivos de escalabilidad (y la redundancia).

Estas situaciones me recuerdan de la norma "agotado" escenarios, pero la diferencia es que no se puede muy bien poner la reserva en orden de nuevo. Una vez que un evento se agotó, se vende a cabo, así que no puedo ver lo que sería una acción de compensación.

¿Cómo manejamos esos escenarios?

¿Fue útil?

Solución

Después de pensar acerca de esto durante algún tiempo, finalmente me di cuenta de que el problema de fondo está menos relacionada con CQRS de lo que es el no trasactional naturaleza de los servicios REST dispares.

Realmente se reduce a este problema:? Si necesita actualizar varios recursos, ¿cómo se asegura la consistencia si falla la segunda operación de escritura

Vamos a imaginar que queremos escribir actualizaciones de recursos A y B Recursos en secuencia.

  1. Recursos A está actualizado correctamente
  2. El intento de actualizar el recurso B falla

La primera operación de escritura no puede ser fácilmente revertido en la cara de una excepción, por lo que ¿qué podemos hacer? La captura y la supresión de la excepción para realizar una acción de compensación en contra de Recursos A no es una opción viable. En primer lugar es difícil de implementar, pero en segundo lugar no es seguro: ¿qué ocurre si la primera excepción ocurrió a causa de una conexión de red se ha podido? En ese escenario, no podemos escribir una acción de compensación en contra de Recursos Un tampoco.

La clave está en explícita idempotencia . Mientras que Windows Azure colas no garantizan exactamente una vez semántica, que hacen garantía al menos una vez semántica. Esto significa que en la cara de excepciones intermitentes, el mensaje será más tarde repite .

En el escenario anterior, esto es lo que sucede a continuación:

  1. Recursos A se intenta actualizar. Sin embargo, se detecta la repetición por lo que el estado de A no se ve afectada. Sin embargo, la operación de 'escribir' tiene éxito.
  2. Recursos B se actualiza correctamente.

Cuando todas las operaciones de escritura son idempotente, consistencia eventual se puede lograr con mensaje repeticiones .

Otros consejos

Una pregunta interesante y con éste se clavan uno de los puntos de dolor en CQRS.

La forma Amazon está manejando esto es haciendo que el escenario de negocios frente a un estado de error si los artículos solicitados están agotadas. El estado de error es simplemente para notificar al cliente por correo electrónico de que los artículos no solicitados actualmente en stock y el día estimado para el envío.

Sin embargo -. Esto no responde plenamente a su pregunta

Pensando en un escenario de la venta de entradas me aseguraría al decirle al cliente que la solicitud se dieron fuera un Solicitud de reserva . Que la solicitud de reserva conseguiría procesado tan pronto como sea posible y de que van a revivir la respuesta final en un correo electrónico más tarde. Por esta alowing algunos clientes podrían obtener un correo electrónico con un rechazo a su solicitud.

Ahora. Podríamos hacer esta rejestion menos doloroso? Ciertamente. Mediante la inserción de una llave en nuestra caché distribuida con el porcentaje o la cantidad de artículos en stock y decrementar el contador cuando cada vez que se vende un artículo. De esta manera podríamos advertir al usuario antes de administrar la solicitud de reserva, digamos que si sólo el 10% del número inicial de las partidas se deja, que el cliente puede no ser capaz de conseguir el artículo en cuestión. Si el contador está a cero simplemente nos negamos a aceptar cualquier solicitud de reserva más.

Mi punto es:

1) dejar que el usuario sabe que se trata de una petición que están haciendo y que esto podría obtener negado 2) informar al usuario de que las posibilidades de éxito de conseguir el artículo en cuestión es baja

No es exactamente una respuesta precisa a su pregunta, pero esto es cómo iba a manejar un escenario como este cuando se trata de CQRS.

El etag que la concurrencia optimista que se puede utilizar en lugar del bloqueo transaccional para actualizar un documento y manejar con seguridad posibles condiciones de carrera. Vea las observaciones aquí http://msdn.microsoft.com/en-us/library/ dd179427.aspx para obtener más información.

La historia podría ser algo como esto: Un usuario crea un evento E con entradas máximo de 2, etag es 123. Debido a la alta demanda 3 usuarios intentan comprar los boletos casi al mismo tiempo. El usuario B crea una solicitud de reserva B. El usuario C crea una solicitud de reserva C. Usuario D crea una solicitud de reserva D.

Sistema S recibe solicitud de reserva B, lee evento con ETAG 123 y cambia el evento de tener 1 boleto restante, S presenta la actualización incluyendo etag 123 que coincide con etag original, por lo que la actualización se realiza correctamente. El etag es ahora 456. Solicitud de reserva se aprueba y se notifica el usuario tuvo éxito.

Otro sistema S2 recibe solicitud de reserva C al mismo tiempo como sistema S estaba procesando petición B, por lo que también lee caso de que el evento con ETAG 123 cambia a 1 billete y los intentos para actualizar el documento restantes. Esta vez, sin embargo, la ETAG 123 no coincide con lo que la actualización falla con una excepción. Sistema S2 intentos de volver a intentar la operación de re-leer el documento que ahora tiene ETAG 456 y un recuento de 1 por lo que disminuye a 0 y vuelve a presentar con ETAG 456.

Por desgracia para el usuario el usuario C, Sistema S comenzó el procesamiento de la petición del usuario D's inmediatamente después de usuario B y también leyó el documento con ETAG 456, sino porque el sistema S es más rápido que el sistema S2 era capaz de actualizar el evento con ETAG 456 antes de sistema S2 por lo usuario D también reservado con éxito su billete. ETAG es ahora 789

Así Sistema S2 vuelve a fallar, sin embargo, le da una oportunidad más, pero esta vez cuando se lee el evento con ETAG 789 se ve que no hay entradas disponibles y por lo tanto niega solicitud de reserva del usuario C.

¿Cómo usted notifique a los usuarios que sus solicitudes de reserva tuvieron éxito o no depende de usted. Se podía consultar al servidor cada pocos segundos y esperar a que el estado de la reserva que se debe actualizar.

Echemos un vistazo a la perspectiva de negocio (que se ocupan en cosas similares - reserva de citas en las ranuras libres) ...

La primera cosa en su análisis que me parece estar fuera es que no hay noción de un boleto / asiento / mesa puede reservarse. Estos están siendo reservados los recursos.

En caso de ser transaccional, puede utilizar algún tipo de singularidad para asegurar que una doble reserva no sucede para el mismo billete / asiento / mesa (más información en http://seabites.wordpress.com/2010/11/11/consistent-indexes-constraints ). Este escenario exige síncrona (pero aún concurrente) el procesamiento de comandos.

En caso de no ser transaccional, puede supervisar de forma retroactiva el flujo de eventos y compensar el comando. Incluso se puede dar al usuario final una experiencia de esperar la confirmación de la reserva hasta que el sistema sabe con seguridad - es decir, después de que el análisis de flujo de eventos - que el comando se ha completado y fue o no fue compensada (que se reduce a "se hizo la reserva? ¿si o no?"). En otras palabras, la compensación podría hacerse parte del ciclo de confirmación.

posterior paso de dejar un poco más ...

Cuando facturación está involucrado como (por ejemplo, la venta de entradas en línea) así, creo que todo este escenario evoluciona en una saga de todos modos (entrada + billete factura de reserva). Incluso sin la facturación, que tendría una saga (tabla de reserva de reserva + confirmar) para hacer la experiencia creíble. Así que, aunque sólo está zoom sobre un solo aspecto de reservar un billete / mesa / asiento (es decir, es que todavía está disponible), la "larga duración" transacción no se completa hasta que había pagado por ella o hasta que lo confirmé . Compensación va a suceder de todos modos, liberando el billete de nuevo cuando abortar la transacción por la razón que fuera. La parte interesante ahora se convierte en la forma en que la empresa quiere hacer frente a esto: tal vez algún otro cliente habría completado la transacción se había determinado que lo / ella el mismo billete. En este caso la devolución podría llegar a ser más interesante cuando reserva un boleto doble / asiento / mesa - incluso ofreciendo un descuento en un próximo evento / similar a compensar las molestias. La respuesta está en el modelo de negocio, no el modelo técnico.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top