Wie soll ich eine geordnete Listenressource in einem erholsamen Service entwerfen?

11

Ich bin immer wieder auf dasselbe Problem gestoßen und habe keine Lösung gefunden, die ich wirklich für optimal hielt.

Angenommen, Sie haben in einer App eine geordnete Liste und lassen den Benutzer diese Reihenfolge per Drag & Drop oder so ändern. Sie möchten, dass die Änderungen in der Reihenfolge beibehalten werden. Wie modellierst du das?

Wie soll ich einen erholsamen Service für eine geordnete Listenressource entwerfen?

Wie soll ich insbesondere das Modell einer erholsamen Ressource entwerfen listund itemmodellieren? Das häufigste Design, das ich gesehen habe, ist die itemEntität mit einer orderoder positionEigenschaft. Ein anderer Ansatz, den ich gehört habe, ist eine doppelt verknüpfte Liste der Elemente.

Was ist ein Ansatz, der nicht zu viel in die Datenbank schreibt und für Clients im Allgemeinen schnell aktualisiert und gelesen werden kann? Wie sollen die Endpunkte angezeigt werden?

Rico Kahler
quelle
Warum ist es aus Neugier wichtig, eine geordnete Liste speziell zurückzugeben?
Adam Wells
Nun, ich denke, ich möchte nicht speziell eine geordnete Listenressource zurückgeben, sondern nur die Reihenfolge / Position der Ressource beibehalten, die Teil einer tatsächlichen Listenressource oder nur implizit Teil einer Liste sein kann. Ich möchte sagen, dass der Benutzer die Reihenfolge einer Aufgabe in einer Liste von Aufgaben ändern soll. Aber egal was passiert, es gibt immer noch eine Liste, in der die Reihenfolge wichtig ist. Was ich nicht finden kann, ist eine gute Möglichkeit, dies zu entwerfen
Rico Kahler

Antworten:

14

Die Darstellung einer geordneten Liste ist eines der schwierigen Probleme bei relationalen Datenbanken. Das Hinzufügen einer Positionseigenschaft zur Liste-Mitgliedschafts-Beziehung ist die häufigste Methode, da Sie die geordnete Liste einfach abrufen können, indem Sie sie ORDER BY positionzu Ihrer SQL-Abfrage hinzufügen , und weil Sie Elemente einfach in die Mitte der Liste einfügen können, indem Sie den Durchschnitt bilden Werte des vorherigen und nachfolgenden Listenmitglieds, vorausgesetzt, die Position ist eher ein Float als eine Ganzzahl.

Die Verwendung von doppelt verknüpften Listen sollte vermieden werden, da es leicht ist, die Verknüpfungen versehentlich inkonsistent zu machen und stattdessen ein zyklisches Diagramm oder einen zyklischen Baum zu erhalten.

RESTful-APIs leiden jedoch nicht unter den Einschränkungen relationaler Datenbanken. Sie können einfach etwas tun, das sich natürlich anfühlt, anstatt einen Hack wie eine Positionseigenschaft zu verwenden.

Wenn Sie nur bis zu ein paar hundert Elemente in der Liste haben, übertragen Sie einfach die gesamte Liste in einer Anfrage. Angenommen, wir möchten neu anordnen, [1, 2, 3, 4]wo die Listenmitglieder IDs sind, könnten wir

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

[1, 2, 4, 3]

Das Backend kann dies dann in die von Ihnen verwendete Datenbanktechnologie übersetzen, der API-Benutzer muss diese Details jedoch nicht berücksichtigen.

Wenn die Liste groß ist und Elemente normalerweise einzeln angefordert werden, können Sie einen Index in der URL zulassen:

GET /page/7

Wenn Sie sich für HATEOAS interessieren, kann die Antwort vorige / nächste Links enthalten, um die Navigation zu vereinfachen, wenn die Ressource normalerweise so verbraucht wird. Dies muss jedoch nicht bedeuten, dass Ihre Datenbank auch diese doppelt verknüpfte Liste enthält.

Wenn die Liste sehr groß ist, möchten Sie möglicherweise ArrayListähnliche Vorgänge wie insertoder push/ verfügbar machen append. Ich könnte mir einen Anruf wie vorstellen

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

description of the item to insert

Wenn die Neuordnung ein häufiger Anwendungsfall ist und die Neuordnung sofort festgeschrieben werden sollte, können Sie einen geeigneten Endpunkt in Ihrer API anbieten:

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

Wenn die neu geordnete Liste explizit festgeschrieben werden soll, ist es einfacher, die neue Bestellung als JSON-Dokument zu übertragen (siehe oben).

Jetzt ist es nicht ganz richtig, dass Sie Ihre API als völlig unabhängig von der von Ihnen verwendeten Datenbanktechnologie anzeigen können. Es ist jedoch am besten, die externe API so frei wie möglich von Implementierungsdetails zu halten. Wenn eine Neuordnung etwa 30 Zeilen berührt, nur um eine Ganzzahlreihenfolge zu aktualisieren, ist das keine große Sache. Tun Sie einfach das Einfachste und aktualisieren Sie immer die gesamte Liste. Wenn Ihre Skalierung eine komplexere Datenbanknutzung erfordert, ziehen Sie es vor, diese Raffinesse im Backend zu erfassen, wo es einfacher ist, die Konsistenz aufrechtzuerhalten.

amon
quelle
1
Beachten Sie, dass der Ansatz "Gesamte Liste ersetzen" problematisch werden kann, wenn mehrere Clients Änderungen vornehmen. Wenn Sie keine Vorsichtsmaßnahmen treffen, überschreiben Sie möglicherweise die Änderungen eines anderen ("Letzte, die Gewinne schreiben").
Oefe
Listenähnliche
2

Ich denke, die Realisierbarkeit verschiedener Ansätze hängt stark von der zugrunde liegenden Datenbank ab, die verwendet wird.

Dieser Ansatz schlug vor, einen Artikel in der Bestellung zu verschieben:

POST / url / of / the / list / reorder-item? Von = 783; bis = 1357

Dies kann unter Rennbedingungen erfolgen, es sei denn, Sie verfügen über SERIALIZABLE-Transaktionen. Die Standardstufe von READ_COMMITTED in den meisten DBs beseitigt keine Racebedingung!

Ich denke tatsächlich, dass in den meisten Fällen - solange die Liste relativ klein ist - der Ansatz, die vollständige Liste zu ersetzen, weniger Probleme mit den Rennbedingungen hat und die Daten nicht beschädigen kann. Wenn sich der Satz von Elementen geändert hat, seit der Client die Anforderung gestellt hat, können Sie einen 409 (Konflikt) zurückgeben.

Es leidet unter Last-Write-Wins, aber das gilt buchstäblich für JEDE API. Unabhängig davon, welche API Sie implementieren, hat möglicherweise ein anderer Client das Objekt aktualisiert, während Sie eine Seite anzeigen.

Charles Capps
quelle