En este artículo, el autor afirma que
A veces, se requiere exponer una operación en la API que inherentemente no es RESTful.
y eso
Si una API tiene demasiadas acciones, es una indicación de que fue diseñada con un punto de vista RPC en lugar de usar principios RESTful, o que la API en cuestión es, naturalmente, una mejor opción para un modelo de tipo RPC.
Esto refleja lo que he leído y escuchado en otros lugares también.
Sin embargo, esto me parece bastante confuso y me gustaría obtener una mejor comprensión del asunto.
Ejemplo I: cerrar una máquina virtual a través de una interfaz REST
Creo que hay dos formas fundamentalmente diferentes de modelar el apagado de una VM. Cada camino puede tener algunas variaciones, pero concentrémonos en las diferencias más fundamentales por ahora.
1. Parchear la propiedad del estado del recurso
PATCH /api/virtualmachines/42
Content-Type:application/json
{ "state": "shutting down" }
(Alternativamente, PUT
en el subrecurso /api/virtualmachines/42/state
).
La VM se apagará en segundo plano y en algún momento posterior, dependiendo de si el apagado se realizará correctamente o no, el estado podría actualizarse internamente con "apagado".
2. PUT o POST en la propiedad de acciones del recurso
PUT /api/virtualmachines/42/actions
Content-Type:application/json
{ "type": "shutdown" }
El resultado es exactamente el mismo que en el primer ejemplo. El estado se actualizará a "apagado" de inmediato y tal vez eventualmente a "apagado".
¿Ambos diseños son RESTful?
¿Qué diseño es mejor?
Ejemplo II: CQRS
¿Qué sucede si tenemos un dominio CQRS con muchas de esas "acciones" (también conocidas como comandos) que podrían conducir a actualizaciones de múltiples agregados o no pueden asignarse a operaciones CRUD en recursos y subrecursos concretos?
¿Deberíamos tratar de modelar tantos comandos como concreto crea o actualiza en recursos concretos, siempre que sea posible (siguiendo el primer enfoque del ejemplo I) y usar "puntos finales de acción" para el resto?
¿O deberíamos asignar todos los comandos a puntos finales de acción (como en el segundo enfoque del ejemplo I)?
¿Dónde deberíamos dibujar la línea? ¿Cuándo el diseño se vuelve menos RESTANTE?
¿Es un modelo CQRS más adecuado para un RPC como API?
De acuerdo con el texto citado arriba, según tengo entendido.
Como puede ver en mis muchas preguntas, estoy un poco confundido sobre este tema. ¿Me pueden ayudar a comprenderlo mejor?
fuente
Respuestas:
En el primer caso (apagado de máquinas virtuales), no consideraría ninguna de las alternativas OP RESTful. Por supuesto, si usa el modelo de madurez de Richardson como criterio, ambos son API de nivel 2 porque usan recursos y verbos.
Sin embargo, ninguno de ellos usa controles hipermedia, y en mi opinión, ese es el único tipo de REST que diferencia el diseño de API RESTful de RPC. En otras palabras, quédese con el nivel 1 y 2, y en la mayoría de los casos tendrá una API de estilo RPC.
Para modelar dos formas diferentes de apagar una VM, expondría la VM como un recurso que (entre otras cosas) contiene enlaces:
Si un cliente desea cerrar la
Ploeh
VM, debe seguir el enlace con elshut-down
tipo de relación. (Normalmente, como se describe en el RESTful Web Services Cookbook , usaría un IRI o un esquema de identificación más elaborado para los tipos de relación, pero elegí mantener el ejemplo lo más simple posible).En este caso, hay poca otra información para proporcionar con la acción, por lo que el cliente simplemente debe hacer una POST vacía contra la URL en
href
:(Dado que esta solicitud no tiene cuerpo, sería tentador modelar esto como una solicitud GET, pero las solicitudes GET no deberían tener efectos secundarios observables, por lo que POST es más correcto).
Del mismo modo, si un cliente quiere apagar la VM, seguirá el
power-off
enlace en su lugar.En otras palabras, los tipos de relación de los enlaces proporcionan posibilidades que indican la intención. Cada tipo de relación tiene un significado semántico específico. Esta es la razón por la que a veces hablamos de la web semántica .
Para mantener el ejemplo lo más claro posible, oscurecí intencionalmente las URL en cada enlace. Cuando el servidor de alojamiento recibe la solicitud entrante, se sabría que
fdaIX
los medios cerrados , yCHTY91
los medios de apagado .Normalmente, solo codificaría la acción en la propia URL, de modo que las URL serían
/vms/1234/shut-down
y/vms/1234/power-off
, pero al enseñar, eso borra la distinción entre los tipos de relación (semántica) y las URL (detalles de implementación).Dependiendo de los clientes que tenga, puede considerar hacer URL RESTful no pirateables .
CQRS
Cuando se trata de CQRS, una de las pocas cosas en las que Greg Young y Udi Dahan están de acuerdo es que CQRS no es una arquitectura de alto nivel . Por lo tanto, sería cauteloso al hacer una API RESTful demasiado similar a CQRS, porque eso significaría que los clientes se convierten en parte de su arquitectura.
A menudo, la fuerza impulsora detrás de una API RESTful real (nivel 3) es que desea poder evolucionar su API sin romper clientes y sin tener el control de los clientes. Si esa es su motivación, entonces CQRS no sería mi primera opción.
fuente
DELETE
parece extraño porque después de apagar el vm seguirá existiendo, solo en estado "apagado" (o algo así).Apagar una VM a través de una interfaz REST
Este es en realidad un ejemplo algo famoso, presentado por Tim Bray en 2009 .
Roy Fielding, discutiendo el problema, compartió esta observación :
En resumen, tiene un recurso de información que devuelve una representación actual del estado supervisado; esa representación puede incluir un enlace hipermedia a un formulario para solicitar un cambio a ese estado, y el formulario tiene otro enlace a un recurso para manejar (cada) solicitud de cambio.
Seth Ladd tenía las ideas clave sobre el problema.
La programación RESTful es la burocracia de Vogon a escala web. ¿Cómo haces algo RESTful? Inventar nuevos documentos para ello y digitalizar el papeleo.
En un lenguaje algo más sofisticado, lo que está haciendo es definir el protocolo de aplicación de dominio para "apagar una VM" e identificar los recursos que necesita para exponer / implementar ese protocolo
Mirando tus propios ejemplos
Está bien; en realidad no está tratando la solicitud en sí como su propio recurso de información por separado, pero aún podría administrarla.
Te has perdido un poco en tu representación del cambio.
Por ejemplo, el tipo de medio JSON Patch formatea las instrucciones como si estuviera modificando directamente un documento JSON
En su alternativa, la idea es cercana, pero obviamente no es correcta.
PUT
es un reemplazo completo del estado del recurso en la URL de destino , por lo que probablemente no elegiría una ortografía que parezca una colección como el objetivo de una representación de una sola entidad.Es coherente con la ficción de que estamos agregando una acción a una cola
Es coherente con la ficción de que estamos haciendo una actualización del elemento de cola en la cola; Es un poco extraño hacerlo de esta manera. El principio de menor sorpresa recomienda dar a cada PUT su propio identificador único, en lugar de ponerlos a todos en un solo lugar y modificar múltiples recursos al mismo tiempo.
Tenga en cuenta que, en la medida en que estamos discutiendo la ortografía de URI, a REST no le importa;
/cc719e3a-c772-48ee-b0e6-09b4e7abbf8b
es un URI perfectamente cromulento en lo que respecta a REST. La legibilidad, como con los nombres de variables, es una preocupación separada. El uso de ortografías que sean consistentes con RFC 3986 hará que las personas sean mucho más felices.CQRS
Greg Young en CQRS
Dado que está hablando de CQRS en el contexto de HTTP / REST, parece razonable suponer que está trabajando en este último contexto, así que vamos con eso.
Este, sorprendentemente, es incluso más fácil que su ejemplo anterior. La razón de esto es simple: los comandos son mensajes .
Jim Webber describe HTTP como el protocolo de aplicación de una oficina de la década de 1950; el trabajo se realiza tomando mensajes y colocándolos en bandejas de entrada. La misma idea es válida: obtenemos una copia en blanco de un formulario, lo completamos con los detalles que conocemos y lo entregamos. Ta da
Sí, en la medida en que los "recursos concretos" son mensajes, en lugar de entidades en el modelo de dominio.
Idea clave: su API REST sigue siendo una interfaz ; debería poder cambiar el modelo subyacente sin que los clientes necesiten cambiar los mensajes. Cuando lanza un nuevo modelo, lanza una nueva versión de sus puntos finales web que saben cómo tomar su protocolo de dominio y aplicarlo al nuevo modelo.
En realidad no, en particular, los cachés web son un gran ejemplo de un "modelo de lectura eventualmente consistente". Hacer que cada una de sus vistas sea direccionable de forma independiente, cada una con sus propias reglas de almacenamiento en caché, le brinda un montón de escalado de forma gratuita. Hay relativamente poco atractivo para un enfoque exclusivamente RPC para las lecturas.
Para las escrituras, es una pregunta clave: enviar todos los comandos a un único controlador en un único punto final, o una sola familia de puntos finales, es ciertamente más fácil . REST es realmente más sobre cómo encontrar la comunicación del punto final al cliente.
Tratar un mensaje como su propio recurso único tiene la ventaja de que puede usar PUT, alertando a los componentes intermediarios sobre el hecho de que el manejo del mensaje es idempotente, para que puedan participar en ciertos casos de manejo de errores. . (Nota: desde el punto de vista de los clientes, si los recursos tienen un URI diferente, entonces son recursos diferentes; el hecho de que todos puedan tener el mismo código de controlador de solicitud en el servidor de origen es un detalle de implementación oculto por el uniforme interfaz).
Fielding (2008)
fuente
Puede aprovechar 5 niveles de tipo de medio para especificar el comando en el campo de encabezado de tipo de contenido de la solicitud.
En el ejemplo de VM, sería algo así.
Luego
O
Ver https://www.infoq.com/articles/rest-api-on-cqrs
fuente
El ejemplo en el artículo vinculado se basa en la idea de que iniciar la máquina y apagarla debe estar dirigida por comandos en lugar de por cambios en el estado de los recursos modelados. Esto último es más o menos lo que REST vive y respira. Un mejor modelado de la VM requiere una mirada a cómo funciona su contraparte en el mundo real y cómo usted, como humano, interactuaría con ella. Esto es de largo aliento, pero creo que da una buena idea del tipo de pensamiento requerido para hacer un buen modelado.
Hay dos formas de controlar el estado de alimentación de la computadora en mi escritorio:
Para una VM, ambos pueden modelarse como valores booleanos de lectura / escritura:
power
- Cuando se cambia atrue
, no sucede nada más que una nota de que el interruptor se ha colocado en este estado. Cuando se cambia afalse
, la VM se ordena en un estado de apagado inmediato. En aras de la integridad, si el valor no cambia después de una escritura, no sucede nada.onoff
- Cuando se cambia atrue
, no sucede nada sipower
es asífalse
; de lo contrario, se ordena al VM que se inicie. Cuando se cambia afalse
, no sucede nada sipower
es asífalse
; de lo contrario, se le ordena a la VM que notifique al software que realice un apagado ordenado, lo que hará y luego notifique a la VM que puede pasar al estado de apagado. Nuevamente, para completar, una escritura sin cambios no hace nada.Con todo esto, nos damos cuenta de que hay una situación en la que el estado de la máquina no refleja el estado de los interruptores, y eso es durante el apagado.
power
seguirá siendotrue
yonoff
seguirá siendofalse
, pero el procesador todavía está ejecutando su apagado, y para eso necesitamos agregar otro recurso para que podamos saber qué está haciendo realmente la máquina:running
- Un valor de solo lectura que estrue
cuando la VM se está ejecutando yfalse
cuando no, se determina preguntando al hipervisor su estado.El resultado de esto es que si quieres una máquina virtual para poner en marcha, hay que asegurarse de que el
power
yonoff
los recursos se han establecidotrue
. (Puede permitirpower
que se salte el paso haciendo que se reinicie automáticamente, de modo que si se establece enfalse
, se conviertetrue
después de que la VM se haya detenido de manera verificable. Si eso es algo RESTfully-puro que hacer es forraje para otra discusión). Si usted quiere que haga un cierre ordenado, se estableceonoff
afalse
y volver más tarde para ver si la máquina se detuvo en realidad, el establecimientopower
defalse
si no lo hizo.Al igual que en el mundo real, todavía tiene el problema de que se le indique que inicie la VM después de que se haya
onoff
cambiado,false
pero todavía serunning
debe a que está en el medio del apagado. Cómo lidiar con eso es una decisión política.fuente
Entonces, si quieres pensar tranquilamente, entonces olvídate de los comandos. El cliente no le dice al servidor que apague la VM. El cliente "cierra" (metafóricamente) su copia de la representación de recursos actualizando su estado y luego PONE esa representación nuevamente en el servidor. El servidor acepta esa nueva representación de estado y, como efecto secundario de esto, en realidad apaga la VM. El aspecto del efecto secundario se deja en manos del servidor.
Es menos de
Hola servidor, cliente aquí, ¿te importaría apagar la VM?
y más de
Hola servidor, cliente, actualicé el estado del recurso VM 42 en el estado de apagado, actualicé tu copia de este recurso y luego hice lo que creas apropiado
Como efecto secundario de que el servidor acepta este nuevo estado, puede verificar qué acciones tiene que realizar realmente (como apagar físicamente VM 42), pero esto es transparente para el cliente. El cliente no se preocupa por las acciones que el servidor debe tomar para ser coherente con este nuevo estado
Así que olvídate de los comandos. Los únicos comandos son los verbos en HTTP para la transferencia de estado. El cliente no sabe, ni le importa, cómo el servidor llevará la VM física al estado de apagado. El cliente no está emitiendo comandos al servidor para lograr esto, solo está diciendo que este es el nuevo estado, descúbrelo .
El poder de esto es que desacopla al cliente del servidor en términos de control de flujo. Si más tarde el servidor cambia la forma en que funciona con las máquinas virtuales, al cliente no le importa. No hay puntos finales de comando para actualizar. En RPC, si cambia el punto final de API de
shutdown
ashut-down
, ha roto todos sus clientes, ya que ahora no conocen el comando para llamar al servidor.REST es similar a la programación declarativa, en la que en lugar de enumerar un conjunto de instrucciones para cambiar algo, simplemente establece cómo desea que sea y deja que el entorno de programación lo descubra.
fuente
POST /api/virtualmachines/42/shutdown
lugar de tener cualquier "efecto secundario". La API debe ser comprensible para el usuario, ¿cómo sabré que, por ejemploDELETE /api/virtualmachines/42
, apagará la VM? Un efecto secundario para mí es un error, debemos diseñar nuestras API para que sean comprensibles y autodescriptivas