Como devo projetar um recurso de lista ordenada em um serviço reparador?

11

Encontrei esse mesmo problema repetidamente e não encontrei uma solução que realmente considerasse ideal.

Digamos que em um aplicativo, você tenha uma lista ordenada e permita que o usuário altere essa ordem arrastando e soltando ou algo assim. Você deseja que as alterações na ordem persistam. Como você modela isso?

Como devo projetar um serviço reparador de um recurso de lista ordenada?

Em particular, como devo projetar liste itemmodelar um recurso repousante? O design mais comum que eu já vi é a itementidade que possui uma propriedade orderou position. Outra abordagem que ouvi é uma lista duplamente vinculada dos itens.

O que é uma abordagem que não grava muito no banco de dados e geralmente é rápida de atualizar e ler para os clientes? Como os pontos de extremidade devem ser expostos?

Rico Kahler
fonte
Por curiosidade, por que é importante retornar uma lista ordenada especificamente?
Adam Wells
Bem, acho que não estou procurando retornar um recurso de lista ordenada, mas apenas persistir a ordem / posição do recurso que pode fazer parte de um recurso de lista real ou apenas implicitamente parte de uma lista. Quero dizer que permita ao usuário alterar a ordem de um todo em uma lista de todos. Mas não importa o que ainda exista uma lista em que a ordem é importante. O que não consigo encontrar é uma boa maneira de projetar isso #
Rico Kahler

Respostas:

14

Representar uma lista ordenada é um dos problemas difíceis nos bancos de dados relacionais. Adicionar uma propriedade position à relação lista-associação é a maneira mais comum de fazer isso, pois você pode recuperar facilmente a lista ordenada adicionando ORDER BY positionà sua consulta SQL e porque é possível inserir itens facilmente no meio da lista calculando a média do valores do membro da lista anterior e subseqüente, assumindo que a posição é um ponto flutuante em vez de inteiro.

O uso de listas duplamente vinculadas deve ser evitado, pois é fácil acidentalmente tornar os links inconsistentes e terminar com um gráfico ou árvore cíclica.

No entanto, as APIs RESTful não sofrem com as restrições dos bancos de dados relacionais. Você pode simplesmente fazer algo que pareça natural, em vez de usar um hack como uma propriedade position.

Se você tiver apenas algumas centenas de elementos na lista, basta transferir a lista inteira em uma solicitação. Supondo que queremos reorganizar [1, 2, 3, 4]onde os membros da lista são IDs, poderíamos

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

[1, 2, 4, 3]

O back-end pode traduzi-lo para qualquer tecnologia de banco de dados que você estiver usando, mas o usuário da API não precisa considerar esses detalhes.

Se a lista for grande e os itens geralmente forem solicitados individualmente, você poderá permitir um índice no URL:

GET /page/7

Se você gosta do HATEOAS, a resposta pode incluir links anteriores / próximos para simplificar a navegação, se o recurso normalmente for consumido dessa maneira. No entanto, isso não significa que seu banco de dados também contenha essa lista duplamente vinculada.

Se a lista for muito grande, convém expor ArrayListoperações do tipo como insertou push/ append. Eu poderia imaginar uma ligação como

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

description of the item to insert

Se a reordenação for um caso de uso comum e a reordenação for confirmada imediatamente, você poderá oferecer um ponto de extremidade apropriado em sua API:

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

Se a lista reordenada deve ser confirmada explicitamente, será mais fácil transferir o novo pedido como um documento JSON, veja acima.

Agora não é bem verdade que você pode ver sua API completamente separada da tecnologia de banco de dados que está usando. No entanto, é melhor manter a API externa o mais livre possível dos detalhes da implementação. Se alguma reordenação tocar cerca de 30 linhas apenas para atualizar uma coluna de pedido inteiro, isso não é grande coisa. Basta fazer a coisa mais simples possível e sempre atualizar a lista inteira. Se sua escala exigir que o uso do banco de dados seja mais sofisticado, prefira capturar essa sofisticação no back-end, onde é mais fácil manter a consistência.

amon
fonte
1
Observe que a abordagem "substituir a lista inteira" pode se tornar problemática se houver vários clientes fazendo alterações. Se você não tomar nenhuma precaução, poderá substituir as alterações de outra pessoa ("a última a vencer ganha").
oefe 3/06/19
Lista-like operações não devem ter este problema, desde que os parâmetros (a, de, para) são ids, não lista índices
oefe
2

Eu acho que a viabilidade de diferentes abordagens depende muito do banco de dados subjacente que está sendo usado.

Essa abordagem sugeriu mover um item na ordem:

POST / url / do / list / reordenar item? De = 783; a = 1357

Isso pode estar sujeito a condições de corrida, a menos que você tenha transacionalidade SERIALIZÁVEL. O nível padrão de READ_COMMITTED na maioria dos bancos de dados não eliminará uma condição de corrida!

Na verdade, eu acho que, na maioria dos casos - desde que a lista seja relativamente pequena -, a abordagem de substituir a lista completa tem menos problemas com as condições de corrida e não pode corromper os dados. Se o conjunto de itens tiver sido alterado desde que o cliente fez a solicitação, você poderá retornar um 409 (Conflito).

Ele sofre de last-write-wins, mas isso é verdade para literalmente QUALQUER API. Independentemente da API que você está implementando, outro cliente pode ter atualizado a coisa enquanto você visualiza uma página.

Charles Capps
fonte