
{"id":2118,"date":"2025-02-09T16:33:55","date_gmt":"2025-02-09T16:33:55","guid":{"rendered":"https:\/\/avant-iconic.com\/?p=2118"},"modified":"2025-03-01T20:27:54","modified_gmt":"2025-03-01T20:27:54","slug":"rest-anti-pattern","status":"publish","type":"post","link":"https:\/\/avant-iconic.com\/en\/rest-anti-pattern\/","title":{"rendered":"Ist REST jetzt ein Anti-Pattern?"},"content":{"rendered":"<p class=\"wp-block-paragraph\">REST-APIs sind schon seit mehr als einem Jahrzehnt allgegenw\u00e4rtig. Doch nicht jeder wei\u00df, dass das, was heute meist als &#8222;REST&#8220; bezeichnet wird, gar nicht die urspr\u00fcngliche Idee war. REST wurde von Roy Fielding in seiner <a href=\"https:\/\/ics.uci.edu\/~fielding\/pubs\/dissertation\/fielding_dissertation.pdf\" target=\"_blank\" rel=\"noopener\">Dissertation<\/a> als &#8222;Representational State Transfer&#8220; entwickelt und eine wichtige Idee darin war HATEOAS (Hypertext as the Engine of Application State), was im Grunde bedeutet, dass jede API-Antwort Navigationslinks zu anderen parametrisierten REST-Endpunkten enth\u00e4lt, zum Beispiel Detail-Links bei einem Listen-Endpunkt. Man kann in der API also \u00e4hnlich navigieren, wie das auf einer HTML-Seite m\u00f6glich w\u00e4re.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das hat sich aber in der Praxis kaum durchgesetzt. Wenn wir im Folgenden von &#8222;REST in der Praxis&#8220; sprechen, meinen wir daher etwas anderes, n\u00e4mlich REST im <a href=\"https:\/\/blog.restcase.com\/4-maturity-levels-of-rest-api-design\/\" target=\"_blank\" rel=\"noopener\">Maturity Level 2<\/a> mit JSON \u00fcber HTTP, ohne HATEOAS, daf\u00fcr mit OpenAPI oder einer \u00e4hnlichen Dokumentation. Das ist zwar eine sehr enge Definition, die wenig mit dem Original zu tun hat, spiegelt jedoch den Industrie-Standard sehr gut wieder, wie wir finden. Und da Software in erster Linie dazu da ist, um benutzt zu werden, lohnt es sich in der Regel, Industrie-Standards auch in Erw\u00e4gung zu ziehen.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/avant-iconic.com\/wp-content\/uploads\/2025\/02\/pexels-liza-summer-6348126-683x1024.webp\" alt=\"\" class=\"wp-image-2336\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Industrie-Standard-Form von REST wird aber immer wieder <a href=\"https:\/\/youtu.be\/NsdnGAAJfDk?feature=shared&amp;t=146\" target=\"_blank\" rel=\"noopener\">kritisiert<\/a>. Dabei handle es sich lediglich um eine Form von CRUD, was ja sowieso ein Anti-Pattern sei. Diese Aussage meint aber nicht, wie man vielleicht denken k\u00f6nnte, dass CRUD-<em>Datenbanken <\/em>problematisch sind. Im <a href=\"https:\/\/youtu.be\/NsdnGAAJfDk?feature=shared&amp;t=146\" target=\"_blank\" rel=\"noopener\">verlinkten Video<\/a> wird lediglich problematisiert, dass die CRUD-Datenbanklogik auf die Backend-Ebene gehoben wird. Und, das ist f\u00fcr einige vielleicht \u00fcberraschend, finden wir in der Regel auch nicht gut!<\/p>\n\n\n\n<div class=\"wp-block-stackable-heading stk-block-heading stk-block-heading--v2 stk-block stk-b8a3007\" id=\"wie-hangt-rest-mit-crud-zusammen\" data-block-id=\"b8a3007\"><h3 class=\"stk-block-heading__text\">Wie h\u00e4ngt REST mit CRUD zusammen?<\/h3><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Die HTTP-Methoden <code>GET<\/code>, <code>POST<\/code>, <code>PUT<\/code>, <code>PATCH<\/code> und <code>DELETE<\/code>, die bei allen Formen von REST zum Einsatz kommen, sind tats\u00e4chlich sehr CRUD-nah. Aber: Niemand sagt, dass dieses CRUD auf derselben Abstraktionsebene wie die Datenbank sein muss. Ein POST-Endpoint, der einen Kunden erfasst, k\u00f6nnte im Hintergrund zehn verschiedene Datenbanktabellen bef\u00fcllen und einen Domain-Event ausl\u00f6sen und das alles sauber abstrahieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wer REST nur als CRUD-Interface in die Datenbank sieht, der l\u00e4sst REST potenziell hinter seinen M\u00f6glichkeiten zur\u00fcck und kreiert dar\u00fcber hinaus eine API, wo Datenfilterung und -verbindung, eigentlich eine St\u00e4rke von Datenbanken und Backends, im Frontend erledigt werden muss. Das ist \u00fcbrigens keine Wertung; genauso haben wir es in einem Projekt mit umfangreichen Offline-F\u00e4higkeiten schon gehandhabt. Der Regelfall ist es aber nicht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Tendenz, einer Datenbank einfach eine API \u00fcberzust\u00fclpen, gibt es tats\u00e4chlich, und diese ist nur in einem Fall eine gute Idee: Die Datenbank soll als eine Art DBaaS (Database as a Service) dienen. So war es auch in unserem Projekt. In allen anderen F\u00e4llen ist das einfach nur eine Fehlentscheidung, die man zwar kompensieren kann, aber in der Regel nur auf Kosten von hoher Komplexit\u00e4t und schlechter Performance.<\/p>\n\n\n\n<div class=\"wp-block-stackable-heading stk-block-heading stk-block-heading--v2 stk-block stk-db37638\" id=\"fachliches-rest\" data-block-id=\"db37638\"><h2 class=\"stk-block-heading__text\">Fachliches REST<\/h2><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Wir gehen daher mit Herrn Roden aus dem Video insofern \u00fcberein, dass eine API in der Regel fachliche Operationen anbieten sollte. Auch die Vorbereitung der Operationen auf CQRS, also eine Trennung von Schreib- und Leseoperationen, sehen wir als sinnvoll an. So muss man bei einer Skalierung, die eines Tages notwendig werden k\u00f6nnte, die externe API nicht umstellen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nun gibt es aber eine wichtige Nuance, die oft zu kurz kommt: Die meisten fachlichen Operationen einer API lassen sich sehr gut ins CRUD-Schema von GET, POST, PUT, PATCH und DELETE einreihen. Einige jedoch \u00fcberhaupt nicht. Was ist die pragmatische L\u00f6sung? Wir finden, alles, was ins Schema passt, sollte dort eingereiht werden, was nicht passt, sollte zu einem POST werden. Der Name der Operation wandert in die OpenAPI-<a href=\"https:\/\/swagger.io\/docs\/specification\/v3_0\/paths-and-operations\/#operationid\" target=\"_blank\" rel=\"noopener\">Operation-ID<\/a>. Dadurch ergeben sich etwa folgende Endpunkte:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>POST \/api\/user<\/code>, Operation-ID: <code>registerUser<\/code><\/li>\n\n\n\n<li><code>PATCH \/api\/user\/1<\/code> (oder <code>PATCH \/api\/user\/1\/profile<\/code>), Operation-ID: <code>editProfile<\/code><\/li>\n\n\n\n<li><code>DELETE \/api\/user\/1<\/code>, Operation-ID: <code>deleteAccount<\/code> (hier ist sogar eine CRUD-Style-Operation-ID die passendste, denn der Endbenutzer m\u00f6chte sein Benutzerkonto <em>l\u00f6schen<\/em>)<\/li>\n\n\n\n<li><code>POST \/api\/user\/1\/ban<\/code>, Operation-ID: <code>banUser<\/code> (ein fachlicher Vorgang, der nicht ins CRUD-Schema passt)<\/li>\n\n\n\n<li><code>GET \/api\/user\/1\/ban<\/code>, Operation-ID: <code>getUserBanStatus<\/code><\/li>\n\n\n\n<li><code>GET \/api\/user\/1\/profile<\/code>, Operation-ID: <code>getProfile<\/code><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">So lassen sich auch State-Machine-Logiken und \u00e4hnliches recht gut ausdr\u00fccken. Die API ist fachlich sprechend und dennoch REST-\u00fcblich strukturiert. Dadurch haben die Endpunkt-URLs und Methoden ebenso eine hohe Aussagekraft. Bei dieser L\u00f6sung sehen wir \u00fcbrigens auch fachlich orientierte Statuscodes als gerechtfertigt. Wird zum Beispiel ein gebannter User nochmals gebannt, geh\u00f6rt f\u00fcr uns ein <code>422 Unprocessable Content<\/code> gesendet, bei Zugriff auf einen gel\u00f6schten Account ein <code>410 Gone<\/code>, und so weiter. Eine Trennung zwischen fachlicher und technischer Ebene sehen wir hier als nahezu unm\u00f6glich.<\/p>\n\n\n\n<div class=\"wp-block-stackable-heading stk-block-heading stk-block-heading--v2 stk-block stk-1982cc7\" id=\"wo-bleiben-komplexe-ruckmeldungen-und-cqrs\" data-block-id=\"1982cc7\"><h2 class=\"stk-block-heading__text\">Wo bleiben komplexe R\u00fcckmeldungen und CQRS?<\/h2><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Lediglich beim R\u00fcckmelden von teilweise gegl\u00fcckten oder fehlgeschlagenen Operationen werden die Einschr\u00e4nkungen der Status-Codes doch deutlich. Hier kommt eine Technik zum Einsatz, auf der auch eine HATEOAS-Erweiterung aufbauen k\u00f6nnte: Die modifizierten Objekte werden direkt vom Endpunkt zur\u00fcckgegeben. Alles, was erfolgreich durchgelaufen ist, hat einen Eintrag (oder HATEOAS-Link) im Response-Content. Alles andere nicht. Beim Statuscode pl\u00e4dieren wir bei teilweise fehlgeschlagenen Operationen auf einen Fehler 500. Zumindest, wenn sich die fehlschlagende Komponente im oder hinter dem Backend befindet, um die korrekte HTTP-Semantik zu wahren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CQRS wird bei diesem System durch zwei Ma\u00dfnahmen unterst\u00fctzt:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Alle nicht-GET-Operationen geben einen Response-Body mit dem ge\u00e4nderten Objekt oder den ge\u00e4nderten Objekten zur\u00fcck (auch ein Grundstein f\u00fcr HATEOAS)<\/li>\n\n\n\n<li>Alle GET-Operationen sind unabh\u00e4ngig von den nicht-GET-Operationen implementiert.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Diese pragmatische und f\u00fcr viele Entwickler vertraute Option hat sich f\u00fcr uns bew\u00e4hrt und eignet sich sowohl f\u00fcr interne Anwendungen als auch f\u00fcr \u00f6ffentliche APIs.<\/p>\n\n\n\n<div class=\"wp-block-stackable-heading stk-block-heading stk-block-heading--v2 stk-block stk-5f19d9d\" id=\"praxis-rest-als-kompatibilitats-garant\" data-block-id=\"5f19d9d\"><h2 class=\"stk-block-heading__text\">Praxis-REST als Kompatibilit\u00e4ts-Garant<\/h2><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">REST im Maturity Level 2 reiht sich in die Reihen von ungemein praktischen, aber nicht immer glamour\u00f6sen Standards ein, derer da unter anderem w\u00e4ren, die C-<a href=\"https:\/\/en.wikipedia.org\/wiki\/Linkage_(software)\" target=\"_blank\" rel=\"noopener\">Linkage<\/a>, das <a href=\"https:\/\/de.wikipedia.org\/wiki\/CSV_(Dateiformat)\" target=\"_blank\" rel=\"noopener\">CSV<\/a>-Datenformat oder auch neuere Entwicklungen wie Web-Components.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nat\u00fcrlich gibt es auch die Option, eine Client-Bibliothek aus der OpenAPI-Definition generieren zu lassen. Das ist aber im Gegensatz zu GraphQL oder gRPC optional. Es ist zwar etwas m\u00fchsamer, ohne Client-Bibliothek zu arbeiten, ans Ziel kommt man aber allemal. Da <a href=\"https:\/\/avant-iconic.com\/en\/tcl-9-das-nischen-wunder-bekommt-eine-neuauflage\/\">homoikonische Sprachen<\/a>, wo man zur Laufzeit YAML in Code transformieren kann, leider Exoten sind, muss man sich hier in der Praxis f\u00fcr ein \u00dcbel entscheiden: Codegenerierung mit vorprogrammierten Konflikten und Versionsproblemen, oder repetitiven Code selbst schreiben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wer Microservices, Event-Busse, CQRS und so weiter nutzt, muss in der Regel trotzdem fr\u00fcher oder sp\u00e4ter eine REST-API implementieren, denn der Rest der Welt spricht REST (schlechtes Wortspiel, ich wei\u00df) und m\u00f6chte JSON-Endpunkte, API-Keys und\/oder OAuth.<\/p>\n\n\n\n<div class=\"wp-block-stackable-heading stk-block-heading stk-block-heading--v2 stk-block stk-50cd615\" id=\"eine-klare-absage\" data-block-id=\"50cd615\"><h2 class=\"stk-block-heading__text\">Eine klare Absage an den Purismus<\/h2><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Der vorgestellte API-Design-Ansatz zeigt, dass pragmatische L\u00f6sungen oft besser sind als dogmatische Reinheit. Die Kombination von CRUD-Konventionen f\u00fcr Standardf\u00e4lle mit explizit fachlichen Operationen dort, wo sie wirklich notwendig sind, schafft APIs, die sowohl verst\u00e4ndlich als auch dom\u00e4nengerecht sind.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Alternative &#8211; das strikte Vermeiden von CRUD-Patterns um jeden Preis &#8211; w\u00fcrde zu unn\u00f6tiger Komplexit\u00e4t f\u00fchren, ohne echten fachlichen Mehrwert zu bieten. Stattdessen nutzen wir die St\u00e4rken etablierter REST-Konventionen und erg\u00e4nzen sie gezielt um fachliche Semantik durch Operation-IDs und sprechende Ressourcen-URLs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Diese L\u00f6sung ist gut, aber nicht ideal &#8211; unsere Branche ist auch noch jung, und wir glauben, dass das Ende der Fahnenstange noch nicht erreicht ist &#8211; weder im Bereich Eleganz noch im Bereich Einfachheit. Einfachheit f\u00fcr Eleganz zu opfern halten wir aber nie f\u00fcr eine gute Idee.<\/p>","protected":false},"excerpt":{"rendered":"<p>REST-APIs sind schon seit mehr als einem Jahrzehnt allgegenw\u00e4rtig. Doch nicht jeder wei\u00df, dass das, was heute meist als &#8222;REST&#8220; bezeichnet wird, gar nicht die urspr\u00fcngliche Idee war. REST wurde von Roy Fielding in seiner Dissertation als &#8222;Representational State Transfer&#8220; entwickelt und eine wichtige Idee darin war HATEOAS (Hypertext as the Engine of Application State), [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":2336,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[39,42,38],"tags":[],"class_list":["post-2118","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software","category-api","category-nerd-talk"],"blocksy_meta":[],"acf":[],"_links":{"self":[{"href":"https:\/\/avant-iconic.com\/en\/wp-json\/wp\/v2\/posts\/2118","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/avant-iconic.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/avant-iconic.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/avant-iconic.com\/en\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/avant-iconic.com\/en\/wp-json\/wp\/v2\/comments?post=2118"}],"version-history":[{"count":1,"href":"https:\/\/avant-iconic.com\/en\/wp-json\/wp\/v2\/posts\/2118\/revisions"}],"predecessor-version":[{"id":2396,"href":"https:\/\/avant-iconic.com\/en\/wp-json\/wp\/v2\/posts\/2118\/revisions\/2396"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/avant-iconic.com\/en\/wp-json\/wp\/v2\/media\/2336"}],"wp:attachment":[{"href":"https:\/\/avant-iconic.com\/en\/wp-json\/wp\/v2\/media?parent=2118"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/avant-iconic.com\/en\/wp-json\/wp\/v2\/categories?post=2118"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/avant-iconic.com\/en\/wp-json\/wp\/v2\/tags?post=2118"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}