Me encontré con el diseño de Event Sourcing y me gustaría usarlo en una aplicación donde se necesita un cliente REST (RESTful para ser precisos). Sin embargo, no puedo conectarlos, ya que REST es bastante CRUDO y el abastecimiento de eventos se basa en tareas. Me preguntaba cómo puede diseñar la creación de comandos basados en solicitudes al servidor REST. Considere este ejemplo:
Con REST puede poner un nuevo estado al recurso llamado Archivo. En una solicitud, puede enviar un nuevo nombre de archivo, puede cambiar la carpeta principal y / o cambiar el propietario del archivo, etc.
Cómo construir el servidor para que pueda usar el abastecimiento de eventos. Estaba pensando en estas posibilidades:
Determinar el servidor de los campos que se han cambiado y crear comandos apropiados (
RenameFileCommand
,MoveFileCommand
,ChangeOwnerCommand
, ...) y envían estos de forma individual. Sin embargo, en esta configuración, cada uno de los comandos puede fallar dejando a otros fuera de la transacción y, por lo tanto, fuera del cambio "atómico" del recurso.Despacho sólo un comando (
UpdateFileCommand
) y en el controlador de comandos, más precisamente en el agregado, determinar qué campos se han cambiado y enviar eventos individuales en lugar (FileRenamedEvent
,FileMovedEvent
,OwnerChangedEvent
, ...)Este no me gusta en absoluto: en la solicitud al servidor, especificaría en los encabezados qué comando usar, porque la interfaz de usuario todavía está basada en tareas (pero la comunicación se realiza a través de REST). Sin embargo, fallará en cualquier otro uso de la comunicación REST (por ejemplo, en aplicaciones externas) ya que no están obligados a cambiar solo un campo en una solicitud. También traigo un acoplamiento bastante grande en la interfaz de usuario, REST y back-end basado en ES.
¿Cuál preferirías o hay alguna forma mejor de manejar esto?
Nota al margen: aplicación escrita en Java y Axon Framework para el abastecimiento de eventos.
fuente
Respuestas:
Creo que puede tener un proceso de usuario para la implementación de la discordancia aquí.
Primero: ¿un usuario honestamente querrá realizar múltiples cambios en un archivo simultáneamente? Un cambio de nombre (que puede incluir o no un cambio de ruta), un cambio de propiedad y tal vez un cambio en el contenido del archivo (en aras de la discusión) parecen acciones separadas.
Tomemos el caso donde la respuesta es "sí" - sus usuarios realmente quieren hacer estos cambios simultáneamente.
En ese caso, yo recomiendo fuertemente contra cualquier aplicación que envía múltiples eventos -
RenameFileCommand
,MoveFileCommand
,ChangeOwnerCommand
- para representar esta única intención del usuario.¿Por qué? Porque los eventos pueden fallar. Tal vez sea extremadamente raro, pero su usuario envió una operación que parecía atómica: si falla uno de los eventos posteriores, el estado de su aplicación ahora no es válido.
También está invitando a los riesgos de carrera en un recurso que se comparte claramente entre cada uno de los controladores de eventos. Deberá escribir "ChangeOwnerCommand" de tal manera que el nombre y la ruta del archivo no importen, ya que podrían estar desactualizados para cuando se reciba el comando.
Al implementar un sistema de reposo no controlado por eventos con mover y renombrar archivos, prefiero garantizar la coherencia mediante el uso de algo como un sistema eTag: asegúrese de que la versión del recurso que se está editando es la versión que el usuario recuperó por última vez, y fallará si ha sido modificado desde entonces. Pero si está enviando múltiples comandos para esta operación de usuario único, necesitará incrementar su versión de recurso después de cada comando, por lo que no tiene forma de saber que el recurso que el usuario está editando realmente es la misma versión que el recurso que leyeron por última vez .
Lo que quiero decir con eso es: ¿qué pasa si alguien más realiza otra operación en el archivo casi al mismo tiempo? Los 6 comandos podrían acumularse en cualquier orden. Si solo tuviéramos 2 comandos atómicos, el comando anterior podría tener éxito y el comando posterior podría fallar "el recurso se ha modificado desde la última vez que se recuperó". Pero no hay protección contra esto cuando los comandos no son atómicos, por lo que se viola la coherencia del sistema.
Curiosamente, hay un movimiento hacia algo como la arquitectura basada en eventos en REST, llamada "Descanse sin poner", recomendada en el radar de tecnología de Thoughtworks, enero de 2015 . Hay un blog considerablemente más largo sobre Descanso sin poner aquí .
Esencialmente, la idea es que POST, PUT, DELETE y GET están bien para aplicaciones pequeñas, pero cuando necesita comenzar a suponer cómo interpretar put y post y delete en el otro extremo, introduce el acoplamiento. (p. ej., "cuando ELIMINO el recurso asociado con mi cuenta bancaria, la cuenta debería cerrarse") Y la solución propuesta es tratar REST de una manera más basada en eventos. es decir, PUBLICAR la intención del usuario como un recurso de evento único.
El otro caso es más simple. Si sus usuarios no quieren hacer todas esas operaciones simultáneamente, no las deje. PUBLICA un evento para cada intento del usuario. Ahora puede usar el control de versiones etag en sus recursos.
En cuanto a las otras aplicaciones que están utilizando una API muy diferente a sus recursos. Eso huele a problemas. ¿Puedes construir una fachada de la antigua API sobre tu API RESTful y apuntarla a la fachada? es decir, exponer un servicio que realiza múltiples actualizaciones a un archivo en secuencia a través del servidor REST?
Si no construye la interfaz RESTful sobre la solución anterior, ni construye una fachada de la interfaz anterior sobre la solución REST, e intenta mantener ambas API apuntando a un recurso de datos compartido, experimentará grandes dolores de cabeza.
fuente
Justo ahora me encontré con el siguiente artículo, que alienta a especificar los nombres de los comandos en la solicitud al servidor en el encabezado Content-Type (mientras sigue los 5 niveles de tipo de medios).
En el artículo, mencionan que el estilo RPC es malo para REST y sugieren extender Content-Type para especificar el nombre del comando:
El artículo está aquí: http://www.infoq.com/articles/rest-api-on-cqrs
Puede leer más sobre 5 niveles de tipo de medios aquí: http://byterot.blogspot.co.uk/2012/12/5-levels-of-media-type-rest-csds.html
Aunque están exponiendo los eventos de dominio a la API REST, lo que consideraría una mala práctica, me gusta la solución, ya que no crea un nuevo "protocolo" únicamente para CQRS, ya sea enviando nombres de comandos en el cuerpo o en extra encabezado, y se mantiene fiel a los principios RESTful.
fuente