¿Cómo debo diseñar un recurso de lista ordenada en un servicio tranquilo?

11

Me he encontrado con este mismo problema una y otra vez y no he encontrado una solución que realmente me pareció óptima.

Digamos que en una aplicación, tiene una lista ordenada y deja que el usuario cambie ese orden arrastrando y soltando o algo así. Desea que los cambios en el orden persistan. ¿Cómo modelas eso?

¿Cómo debo diseñar un servicio reparador de un recurso de lista ordenada?

En particular, ¿cómo debería diseñar el listy itemmodelar de un recurso de descanso? El diseño más común que he visto es la itementidad que tiene una ordero positionpropiedad. Otro enfoque que he escuchado es una lista doblemente vinculada en los artículos.

¿Qué enfoque no escribe demasiado en la base de datos y generalmente es rápido de actualizar y leer para los clientes? ¿Cómo se deben exponer los puntos finales?

Rico Kahler
fuente
Por curiosidad, ¿por qué es importante devolver una lista ordenada específicamente?
Adam Wells
Bueno, supongo que no estoy buscando especialmente devolver un recurso de lista ordenada, sino simplemente mantener el orden / posición del recurso que puede ser parte de un recurso de lista real o simplemente parte de una lista. Quiero que digas que permitas al usuario cambiar el orden de un todo en una lista de todos. Pero no importa lo que haya, todavía hay una lista donde importa el orden. Lo que no puedo encontrar es una buena manera de diseñar esto
Rico Kahler

Respuestas:

14

Representar una lista ordenada es uno de los problemas difíciles con las bases de datos relacionales. Agregar una propiedad de posición a la relación lista-membresía es la forma más común de hacerlo, ya que puede recuperar fácilmente la lista ordenada agregando ORDER BY positiona su consulta SQL, y porque puede insertar fácilmente elementos en el medio de la lista promediando el valores del miembro de la lista anterior y posterior, suponiendo que la posición es flotante en lugar de entero.

Se debe evitar el uso de listas doblemente vinculadas, ya que es fácil hacer que los enlaces sean inconsistentes accidentalmente y terminar con un gráfico o árbol cíclico.

Sin embargo, las API RESTful no sufren las restricciones de las bases de datos relacionales. Puedes hacer algo que se siente natural, en lugar de usar un truco como una propiedad de posición.

Si solo tiene unos pocos cientos de elementos en la lista, simplemente transfiera la lista completa en una solicitud. Suponiendo que queremos reordenar [1, 2, 3, 4]donde los miembros de la lista son ID, podríamos

POST /url/of/the/list
Content-type: application/json
...

[1, 2, 4, 3]

El backend puede traducir esto a cualquier tecnología de base de datos que esté utilizando, pero el usuario de la API no tiene que considerar estos detalles.

Si la lista es grande y los artículos generalmente se solicitarían individualmente, puede permitir un índice en la URL:

GET /page/7

Si está interesado en HATEOAS, la respuesta puede incluir enlaces prev / next para simplificar la navegación, si el recurso generalmente se consumiría así. Sin embargo, esto no implica necesariamente que su base de datos también contenga esta lista doblemente vinculada.

Si la lista es muy grande, es posible que desee exponer ArrayListoperaciones similares como inserto push/ append. Podría imaginar una llamada como

POST /url/of/the/list?at=1357;mode=insert
...

description of the item to insert

Si el reordenamiento es un caso de uso común y el reordenamiento debe confirmarse de inmediato, entonces podría ofrecer un punto final apropiado en su API:

POST /url/of/the/list/reorder-item?from=783;to=1357

Si la lista reordenada debe confirmarse explícitamente, será más fácil transferir el nuevo pedido como un documento JSON, ver arriba.

Ahora no es del todo cierto que pueda ver su API completamente separada de la tecnología de base de datos que está utilizando. Sin embargo, es mejor mantener la API externa lo más libre posible de los detalles de implementación. Si alguna reordenación toca unas 30 filas solo para actualizar una columna de orden de enteros, no es gran cosa. Simplemente haga lo más simple posible y actualice siempre la lista completa. Si su báscula requiere que el uso de su base de datos sea más sofisticado, prefiera capturar esta sofisticación en el backend, donde es más fácil mantener la coherencia.

amon
fuente
1
Tenga en cuenta que el enfoque de "reemplazar la lista completa" puede volverse problemático si hay varios clientes haciendo cambios. Si no toma precauciones, puede terminar sobrescribiendo los cambios de otra persona ("el último en escribir gana").
oefe
Lista similar a las operaciones no debería tener este problema, siempre y cuando los parámetros (a, a, a) son los identificadores, no índices lista
oefe
2

Creo que la viabilidad de los diferentes enfoques depende en gran medida de la base de datos subyacente que se utilice.

Este enfoque sugiere mover un elemento en el pedido:

POST / url / of / the / list / reorder-item? From = 783; to = 1357

Eso puede estar sujeto a condiciones de carrera a menos que tenga transaccionalidad SERIALIZABLE. ¡El nivel predeterminado de READ_COMMITTED en la mayoría de los DB no eliminará una condición de carrera!

De hecho, creo que en la mayoría de los casos, siempre que la lista sea relativamente pequeña, el enfoque de reemplazar la lista completa tiene menos problemas con las condiciones de carrera y no puede corromper los datos. Si el conjunto de elementos ha cambiado desde que el cliente realizó la solicitud, puede devolver un 409 (Conflicto).

Sufre de últimas victorias de escritura, pero eso es cierto, literalmente, de CUALQUIER API. No importa qué API esté implementando, otro cliente puede haber actualizado la cosa mientras está viendo una página.

Charles Capps
fuente