REST Kritik #4: APIs leiden am CRUD Antipattern

2018-01-14 von Thomas Bayer

Die Eigenschaften der REST Architektur führen zwangsläufig zu datengetriebenen Schnittstellen anstatt passende Abstraktionen anzubieten. Dieser Blog Beitrag zeigt auf, dass REST APIs zum CRUD Antipattern neigen.

Für die Abbildung aller Funktionalitäten einer Schnittstelle müssen die wenigen Methoden, die REST bietet, ausreichen. Meist werden nur die CRUD Methoden POST, GET, PUT und DELETE verwendet, die den Datenbankoperationen Create, Read, Update und Delete entsprechen.

Die Kunst beim API Design besteht darin, aussagekräftige Hauptwörter, die URIs zu finden, auf die die wenigen HTTP Methoden angewendet werden.

Ein Beispiel soll das erläutern. Der folgende Funktionsaufruf einer Nicht-REST Schnittstelle dient zum Stornieren von Bestellungen:

storniereBestellung( 74)

Eine Möglichkeit diesen Funktionsaufruf mit REST abzubilden wäre ein DELETE Aufruf an die zu stornierende Ressource:

DELETE /bestellung/74

Für einfache Anwendungen ist das eine elegante Lösung. Bei einem Storno wird einfach die Ressource gelöscht. Was ist aber, wenn die Daten später noch benötigt werden? Alternativ könnte man anstatt eine Bestellung zu löschen, ein Storno anlegen:

POST /stornos/ { “bestellung“: “/bestellung/74“ }

Eine dritte Variante wäre das Überschreiben der Bestellung mit einer neuen Repräsentation, bei der der Status aktualisiert würde:

PATCH /bestellung/74 { “status“: “storniert“ }

Alle drei Varianten bilden den Aufruf storniereBestellung() über eine Manipulation der Daten ab. Die Verwendung von REST begünstigt Schnittstellen, bei denen die Daten im Vordergrund stehen. REST Schnittstellen bilden oft nur die darunterliegende Datenschicht ab, ohne ausreichend zu abstrahieren und die Geschäftslogik zu kapseln. Die Logik wird meist in den Client verschoben.

REST ist keine Abstraktion für den Datenbankzugriff. Wer Daten manipulieren möchte, kann SQL oder einen ORM Mapper verwenden.

Eine weitere Variante des Antipatterns ist eine flache CRUD Schnittstelle, bei der jede Tabelle auf zwei URI Templates abgebildet wird: eines für einzelne Objekt und eines für Listen. Das Beispiel unten zeigt die URIs für Rechnungen und die zugehörigen Positionen:

/rechnungen/ /rechnungen/{rid} /positionen/ /positionen/{pid}

Eine Schnittstelle wie diese ist nichts anderes als „CRUD over HTTP“!

Mit Unterressourcen ist Umsetzung möglich, die die Fachlichkeit besser abbildet. Eine Position ist immer einer Rechnung zugeordnet. Eine Position alleine macht keinen Sinn. Mit Subressourcen könnten abhängige Objekte zuordnet werden:

/rechnungen/ /rechnungen/{rid} /rechnungen/{rid}/positionen/ /rechnungen/{rid}/positionen/{pid}

Eine neue Position könnte dann über die zugehörige Rechnung erzeugt werden:

POST /rechnungen/{rid}/positionen/

Beim Löschen einer Position sollten alle zugehörigen Positionen gelöscht werden. Dieses Verhalten könnte die Implementierung übernehmen. Das API übernimmt Verantwortung und kann Geschäftslogik kapseln. Diese Realisierung ist etwas besser aber immer noch stark Daten orientiert. Selbstverständlich sollte in den Repräsentationen auf Ressourcen verwiesen werden. Beispielsweise von der Rechnung auf die Liste der Positionen und von der Position auf die zugehörige Ware.

Wie das obige Beispiel zeigt, können mit REST Schnittstellen modelliert werden, die passende Abstraktionen bieten und Verhalten kapseln. Die Verwendung von Hypermedia verringert die Gefahr des CRUD Antipatters. Der Punkt ist nicht, dass REST zwangsweise zum CRUD Antipattern führt. REST hat den Ruf, einfach zu sein. Das Design von guten REST Schnittstellen ist alles andere als einfach und führt schnell zu datenorientierten Schnittstellen.