Come devo progettare una risorsa elenco ordinata in un servizio riposante?


11

Ho riscontrato questo stesso problema ancora e ancora e non ho trovato una soluzione che ritenevo davvero ottimale.

Dì in un'app, hai un elenco ordinato e permetti all'utente di cambiare quell'ordine trascinandolo o qualcosa del genere. Volete che le modifiche nell'ordine persistano. Come lo modelli?

Come devo progettare un servizio riposante di una risorsa dell'elenco ordinato?

In particolare, come devo progettare il modello liste itemdi una risorsa riposante? Il design più comune che ho visto è il itemsoggetto che abbia in ordero di positionproprietà. Un altro approccio che ho sentito è un elenco doppiamente collegato sugli articoli.

Qual è un approccio che non scrive troppo nel database ed è generalmente veloce da aggiornare e leggere per i client? Come devono essere esposti gli endpoint?


Per curiosità, perché è importante restituire un elenco ordinato in particolare?
Adam Wells,

Beh, suppongo che non sto cercando in particolare di restituire una risorsa elenco ordinata, ma persisto solo l'ordine / la posizione della risorsa che può far parte di una risorsa elenco effettiva o solo implicitamente parte di un elenco. Voglio dire, lascia che l'utente cambi l'ordine di un todo in un elenco di todos. Ma non importa quale sia ancora un elenco in cui l'ordine conta. Quello che non riesco a trovare è un buon modo per progettare questo
Rico Kahler,

Risposte:


14

La rappresentazione di un elenco ordinato è uno dei problemi difficili con i database relazionali. L'aggiunta di una proprietà position alla relazione elenco-appartenenza è il modo più comune per farlo, poiché è possibile recuperare facilmente l'elenco ordinato aggiungendo ORDER BY positionalla query SQL e poiché è possibile inserire facilmente elementi al centro dell'elenco facendo la media del valori del membro di elenco precedente e successivo, supponendo che la posizione sia un float anziché un numero intero.

L'uso di elenchi doppiamente collegati dovrebbe essere evitato, poiché è facile rendere inavvertitamente i collegamenti incoerenti e finire invece con un grafico o un albero ciclico.

Tuttavia, le API RESTful non soffrono delle restrizioni dei database relazionali. Puoi semplicemente fare qualcosa che sembra naturale, piuttosto che usare un hack come una proprietà position.

Se nell'elenco sono presenti solo poche centinaia di elementi, è sufficiente trasferire l'intero elenco in una richiesta. Supponendo di voler riordinare [1, 2, 3, 4]dove i membri dell'elenco sono ID, potremmo

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

[1, 2, 4, 3]

Il back-end potrebbe quindi tradurlo in qualsiasi tecnologia di database in uso, ma l'utente dell'API non deve considerare questi dettagli.

Se l'elenco è grande e gli articoli vengono normalmente richiesti singolarmente, puoi consentire un indice nell'URL:

GET /page/7

Se ti piacciono gli HATEOAS, la risposta può includere collegamenti precedente / successivo per semplificare la navigazione, se la risorsa viene normalmente consumata in questo modo. Tuttavia, ciò non implica che il database contenga anche questo elenco doppiamente collegato.

Se l'elenco è molto grande, potresti voler esporre ArrayListoperazioni simili a come inserto push/ append. Potrei immaginare una chiamata come

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

description of the item to insert

Se il riordino è un caso d'uso comune e il riordino deve essere eseguito immediatamente, è possibile offrire un endpoint appropriato nell'API:

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

Se l'elenco riordinato deve essere eseguito in modo esplicito, sarà più semplice trasferire il nuovo ordine come documento JSON, vedere sopra.

Ora non è del tutto vero che è possibile visualizzare l'API come completamente separata dalla tecnologia di database che si sta utilizzando. Tuttavia, è meglio mantenere l'API esterna il più libera possibile dai dettagli di implementazione. Se un riordino tocca circa 30 righe solo per aggiornare una colonna di ordine intero, non è un grosso problema. Fai solo la cosa più semplice possibile e aggiorna sempre l'intero elenco. Se la tua bilancia richiede che il tuo database sia più sofisticato, preferisci catturare questa raffinatezza nel backend, dove è più facile mantenere la coerenza.


1
Si noti che l'approccio "Sostituisci l'intero elenco" può diventare problematico se ci sono più client che apportano modifiche. Se non prendi alcuna precauzione, potresti finire per sovrascrivere le modifiche di qualcun altro ("l'ultimo a scrivere vince").
oefe,

Le operazioni di tipo elenco non dovrebbero presentare questo problema, a condizione che i parametri (da, da, a) siano id, non indici di elenco
oefe

2

Penso che la fattibilità di approcci diversi dipenda fortemente dal database sottostante utilizzato.

Questo approccio ha suggerito di spostare un articolo nell'ordine:

POST / url / of / the / list / reorder-item? Da = 783; a = 1357

Ciò può essere soggetto alle condizioni di gara, a meno che non si abbia una transitabilità SERIALIZZABILE. Il livello predefinito di READ_COMMITTED nella maggior parte dei DB non eliminerà una condizione di competizione!

In realtà penso che nella maggior parte dei casi, purché l'elenco sia relativamente piccolo, l'approccio di sostituzione dell'elenco completo ha meno problemi con le condizioni di gara e non può corrompere i dati. Se l'insieme di elementi è cambiato da quando il client ha effettuato la richiesta, è possibile restituire un 409 (Conflitto).

Soffre delle vittorie dell'ultima scrittura, ma questo vale letteralmente per QUALSIASI API. Indipendentemente dall'API che stai implementando, un altro client potrebbe aver aggiornato la cosa mentre stai visualizzando una pagina.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.