Warum REST Api nicht dem Fassadenmuster folgen

9

Beim Vergleich der REST [api] -Struktur mit einem OO-Modell sehe ich folgende Ähnlichkeiten:

Beide:

  • Sind datenorientiert

    • REST = Ressourcen
    • OO = Objekte
  • Surround-Betrieb um Daten

    • REST = Surround-VERBS (Get, Post, ...) um Ressourcen herum
    • OO = Förderung des Betriebs um Objekte durch Kapselung

Gute OO-Praktiken stehen jedoch nicht immer auf REST-APIs, wenn Sie beispielsweise versuchen, das Fassadenmuster anzuwenden: In REST verfügen Sie nicht über einen Controller, der alle Anforderungen verarbeitet, UND Sie verbergen die Komplexität interner Objekte nicht.

Einfache Objektbeziehung zwischen 2 Konzepten

Analogie zwischen OO und REST

Im Gegenteil, REST fördert die Veröffentlichung von Ressourcen aller Beziehungen zu einer Ressource und anderen in mindestens zwei Formen:

  1. über Ressourcenhierarchiebeziehungen (Ein Kontakt mit der ID 43 besteht aus einer Adresse 453): /api/contacts/43/addresses/453

  2. über Links in einer REST-JSON-Antwort:

>> GET /api/contacts/43
<< HTTP Response {
   id: 43, ...
   addresses: [{
      id: 453, ...
   }],
   links: [{
      favoriteAddress: {
          id: 453
      }
   }]
}

Grundlegende Komplexität, die von objectA verborgen wird

Zurück zu OO: Das Fassadenentwurfsmuster berücksichtigt a Low Couplingzwischen einem ObjektA und seinem ' ObjektB-Client ' sowie High Cohesionfür dieses ObjektA und seine interne Objektzusammensetzung ( ObjektC , ObjektD ). Mit dem Objecta Schnittstelle, erlauben diese einen Entwickler zu begrenzen Auswirkungen auf ObjectB der Objecta internen Veränderungen (in ObjectC und ObjectD ), solange die Objecta api (Operationen) sind nach wie vor eingehalten werden .

In REST werden die Daten (Ressource), die Beziehungen (Links) und das Verhalten (Verben) in verschiedenen Elementen aufgelöst und stehen dem Web zur Verfügung.

Wenn ich mit REST spiele, habe ich immer Auswirkungen auf Codeänderungen zwischen meinem Client und meinem Server: Weil ich High Couplingzwischen meinen Backbone.jsAnforderungen und Low Cohesionzwischen Ressourcen habe.

Ich habe nie herausgefunden, wie ich mich Backbone.js javascript applicationmit der Erkennung von " REST-Ressourcen und -Funktionen " befassen kann , die durch REST-Links gefördert wird. Ich verstehe, dass das WWW von mehreren Servern bedient werden soll und dass die OO-Elemente explodiert werden mussten, um von vielen Hosts dort bedient zu werden, aber für ein einfaches Szenario wie das "Speichern" einer Seite, die einen Kontakt mit ihren Adressen zeigt, Am Ende habe ich:

GET /api/contacts/43?embed=(addresses)
[save button pressed]
PUT /api/contacts/43
PUT /api/contacts/43/addresses/453

Dies führte dazu, dass ich die atomare Transaktionsverantwortung für die Speicheraktion auf die Browseranwendungen verlagerte (da zwei Ressourcen separat angesprochen werden können).

In diesem Sinne, wenn ich meine Entwicklung nicht vereinfachen kann (Fassadenentwurfsmuster nicht anwendbar) und wenn ich meinem Kunden mehr Komplexität (Umgang mit atomarer Transaktionsspeicherung) bringe, wo ist der Vorteil von RESTful?

Alain
quelle
1
Lass mich verstehen. Sie sagen, dass Sie einen Kontakt mit einer "eingebetteten" (zusammengesetzten) verknüpften Adresse mithilfe von zwei REST-Aufrufen aktualisieren müssen, einen für den Kontakt und einen für seine Adresse. Sie haben eine Fassade, um Kontakte zu aktualisieren. Was ist das Problem, PUT /api/contacts/43wenn die Aktualisierungen innerer Objekte kaskadiert werden? Ich hatte viele APIs wie diese entworfen (Master-URL liest / erstellt / aktualisiert das "Ganze" und Sub-URLs aktualisieren die Teile). Stellen Sie einfach sicher, dass Sie die Adresse nicht aktualisieren, wenn keine Änderungen erforderlich sind (aus Leistungsgründen).
Anthony Accioly
@AnthonyAccioly, du hast richtig verstanden. Ich habe versucht, meine Frage durch Hinzufügen einiger Bilder zu klären. Ihr Vorschlag ist gut und das ist auch die Schlussfolgerung, zu der ich gekommen bin: Manuelles Steuern meiner Anfrage und Verwenden eines eingebetteten Objekts, um nur eine Anfrage zu senden, um mein Update atomar zu halten. Dennoch: Warum alles in REST mich von OO-Qualitäten oder Durchsetzungsmaßnahmen (Kapselung, ...) abhält, indem mein Modell abgeflacht wird (was viele Controller impliziert). Wenn Sie Ihre Lösung verwenden, erhalten Sie 1 atomares Update. Wenn Sie Ihre Lösung nicht verwenden, hat der Entwickler eine neue Verantwortung und keine Regeln für die API, die ihn daran hindern könnten.
Alain
Nur eine Anmerkung: Bei den von Ihnen erwähnten "Ressourcenhierarchie-Beziehungen" geht es darum, wie man sich entscheiden könnte, Beziehungsinformationen in einem Bezeichner (in diesem Fall einer URL) zu codieren. Ich bin mir nicht sicher, ob diese Darstellung von Informationen Teil von REST ist oder etwas, das sie fördert, sondern nur eine Entscheidung, die man selbst trifft, wenn man die URLs eines Systems erstellt. Wenn Sie etwas anderes glauben, haben Sie Referenzen von Roy Fielding, der diese Angelegenheit im Rahmen von REST diskutiert?
Thiago Silva

Antworten:

7

Ich denke, Objekte basieren nur korrekt auf kohärenten Verhaltensweisen und nicht auf Daten. Ich werde provozieren und sagen, dass Daten in der objektorientierten Welt fast irrelevant sind. Tatsächlich ist es möglich und manchmal üblich, Objekte zu haben, die niemals Daten zurückgeben, z. B. "Protokollsenken", oder Objekte, die niemals die Daten zurückgeben, an die sie übergeben werden, beispielsweise wenn sie statistische Eigenschaften berechnen.

Lassen Sie uns die PODS (die kaum mehr als Strukturen sind) und die realen Objekte, die Verhaltensweisen aufweisen (wie die ContactsKlasse in Ihrem Beispiel) , nicht verwechseln 1 .

PODS sind im Grunde genommen eine Annehmlichkeit, um mit Repositorys und Geschäftsobjekten zu kommunizieren. Sie ermöglichen, dass der Code typsicher ist. Nicht mehr und nicht weniger. Geschäftsobjekte hingegen bieten konkrete Verhaltensweisen wie die Validierung oder Speicherung Ihrer Daten oder die Verwendung zur Berechnung.

Wir verwenden also Verhaltensweisen, um die "Kohäsion" 2 zu messen , und es ist leicht zu erkennen, dass in Ihrem Objektbeispiel eine gewisse Kohäsion besteht, obwohl Sie nur Methoden zum Bearbeiten von Kontakten der obersten Ebene und keine Methoden zum Bearbeiten von Adressen anzeigen.

In Bezug auf REST können Sie REST-Services als Datenrepositorys anzeigen. Der große Unterschied zum objektorientierten Design besteht darin, dass es (fast) nur eine Designauswahl gibt: Sie haben vier grundlegende Methoden (mehr, wenn Sie HEADzum Beispiel zählen) und natürlich haben Sie viel Spielraum mit den URIs, sodass Sie geschickt arbeiten können Sachen wie viele IDs weitergeben und eine größere Struktur zurückbekommen. Verwechseln Sie die übergebenen Daten nicht mit den von ihnen ausgeführten Vorgängen . Bei Kohäsion und Kopplung geht es um Code und nicht um Daten .

Es ist klar, dass REST-Services eine hohe Kohäsion (jede Art der Interaktion mit einer Ressource befindet sich am selben Ort) und eine geringe Kopplung aufweisen (jedes Ressourcen-Repository erfordert keine Kenntnis der anderen).

Die grundlegende Tatsache bleibt jedoch, dass REST im Wesentlichen ein einzelnes Repository-Muster für Ihre Daten ist. Dies hat Konsequenzen, da es sich um ein Paradigma handelt, das auf einer einfachen Zugänglichkeit über ein langsames Medium basiert, bei dem hohe Kosten für "Chattiness" anfallen: Clients möchten normalerweise so wenig Vorgänge wie möglich ausführen, erhalten aber gleichzeitig nur die Daten, die sie benötigen . Dies bestimmt, wie tief ein Datenbaum ist, den Sie zurücksenden möchten.

Bei (korrektem) objektorientiertem Design führt jede nicht triviale Anwendung viel komplexere Operationen aus, beispielsweise durch Komposition. Sie könnten Methoden haben, um spezialisiertere Operationen mit den Daten durchzuführen - was auch so sein muss, denn während REST ein API-Protokoll ist, wird OOD verwendet, um ganze Anwendungen für Benutzer zu erstellen! Aus diesem Grund ist die Messung von Kohäsion und Kopplung bei OOD von grundlegender Bedeutung, bei REST jedoch nahezu unbedeutend.

Inzwischen sollte klar sein, dass die Analyse des Datenentwurfs mit OO-Konzepten keine zuverlässige Methode zur Messung ist: Es ist wie der Vergleich von Äpfeln und Orangen!

Tatsächlich stellt sich heraus, dass die Vorteile von RESTful (meistens) die oben beschriebenen sind: Es ist ein gutes Muster für einfache APIs über ein langsames Medium. Es ist sehr zwischenspeicherbar und zerbrechbar. Es hat eine feinkörnige Kontrolle über das Geschwätz usw.

Ich hoffe das beantwortet deine (ziemlich facettenreiche) Frage :-)


1 Dieses Problem ist Teil einer größeren Reihe von Problemen, die als objektrelationale Impedanzfehlanpassung bekannt sind . Befürworter von ORMs sind im Allgemeinen in dem Lager, das die Ähnlichkeiten zwischen Datenanalyse und Verhaltensanalyse untersucht, aber ORMs wurden in letzter Zeit kritisiert, weil sie die Impedanzfehlanpassung nicht wirklich zu beheben scheinen und als undichte Abstraktionen gelten .

2 http://en.wikipedia.org/wiki/Cohesion_(computer_science)

Sklivvz
quelle
Sie haben Recht, es fiel mir schwer, meine Frage in vielen Aspekten zu explodieren und einen bestimmten Punkt festzunageln, da die Frage eine "falsche" Schlussfolgerung enthält, die auf der Anhäufung dieser Aspekte beruht. Ich werde jetzt versuchen, Ihre Punkte in vielen Kommentaren zu beantworten.
Alain
[Text 1] Ich habe das Wort "Daten" verwendet, um von der OO- und REST-Welt zu abstrahieren. Mit welchem ​​Wort würden Sie Eigenschaften in OO und Datenstruktur in REST abstrahieren?
Alain
@Alain "Daten" sind in Ordnung, aber es geht mir nicht darum, PODS und Geschäftsobjekte zu verwechseln. Wenn wir über OOD sprechen, sprechen wir im Allgemeinen über die zweite. Die ersten sind eine Annehmlichkeit, und man könnte sich fast genauso leicht ein Wörterbuch, eine Struktur oder ein Tupel vorstellen.
Sklivvz
Ja, ich stimme zu, ich verwende Backbone.js, wo das Speichern eines Modells eine einfache JSON-Struktur verwendet. Hier spiegelt der Text meine tatsächliche Codierungserfahrung wider.
Alain
[Text 3] Das ist neu für mich. Ich dachte, dass der Zusammenhalt anhand der Anzahl der Zeitmethoden gemessen wurde, bei denen eine bestimmte Beziehung verwendet wurde. Ich bevorzuge Ihre Sichtweise.
Alain
1

In diesem Sinne, wenn ich meine Entwicklung nicht vereinfachen kann (Fassadenentwurfsmuster nicht anwendbar) und wenn ich meinem Kunden mehr Komplexität (Umgang mit atomarer Transaktionsspeicherung) bringe, wo ist der Vorteil von RESTful?

Die Antwort auf "Wo ist der Vorteil von RESTful?" wird hier gründlich analysiert und erklärt: http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

Die Verwirrung in dieser Frage ist jedoch, dass es nicht um Merkmale von REST und wie man damit umgeht, sondern um die Annahme, dass das Design der URLs, die Sie für Ihr Beispielsystem erstellt haben, etwas mit RESTful zu tun hat. Schließlich gibt REST an, dass es Dinge gibt, die als Ressourcen bezeichnet werden, und dass eine Kennung für diejenigen bereitgestellt werden sollte, auf die verwiesen werden muss. Dies schreibt jedoch nicht vor, dass beispielsweise Entitäten in Ihrem ER-Modell eine 1-1-Entsprechung mit den von Ihnen erstellten URLs haben sollten (Weder dass die URLs die Kardinalität der ER-Beziehungen im Modell codieren sollten).

Bei Kontakten und Adressen könnten Sie eine Ressource definiert haben, die diese Informationen gemeinsam als eine Einheit darstellt, obwohl Sie diese Informationen möglicherweise extrahieren und in beispielsweise verschiedenen relationalen DB-Tabellen speichern möchten, wenn sie PUT oder POST sind .

Thiago Silva
quelle
1

Das ist, weil Fassaden ein "Kludge" sind; Sie sollten sich 'API-Abstraktion' und 'API-Verkettung' ansehen. Die API ist eine Kombination aus zwei Funktionssätzen: E / A und Ressourcenverwaltung. Lokal ist die E / A in Ordnung, aber innerhalb einer verteilten Architektur (z. B. Proxy, API-Gate, Nachrichtenwarteschlange usw.) wird die E / A gemeinsam genutzt, und somit werden die Daten und Funktionen dupliziert und verwickelt. Dies führt zu architektonischen Querschnittsproblemen. Dies plagt ALLE existierenden Apis.

Die einzige Möglichkeit, dies zu beheben, besteht darin, die E / A-Funktionalität für die API auf einen Pre / Post-Handler (wie einen handlerIntercepter in Spring / Grails oder einen Filter in Rails) zu abstrahieren, damit die Funktionalität als Monade verwendet und von Instanzen und externen Benutzern gemeinsam genutzt werden kann Werkzeuge. Daten für die Anforderung / Antwort müssen auch in einem Objekt externalisiert werden, damit sie auch gemeinsam genutzt und neu geladen werden können.

http://www.slideshare.net/bobdobbes/api-abstraction-api-chaining

Orubel
quelle
0

Wenn Sie Ihren REST-Service oder allgemein jede Art von API als zusätzliche Schnittstelle verstehen , die Clients zur Verfügung steht, damit diese Ihre Controller darüber programmieren können, wird es plötzlich einfach. Der Service ist nichts weiter als eine zusätzliche Ebene über Ihrer Geschäftslogik.

Mit anderen Worten, Sie müssen die Geschäftslogik nicht wie in Ihrem Bild oben auf mehrere Controller aufteilen, und was noch wichtiger ist, Sie sollten es nicht tun. Die Datenstrukturen, die zum Datenaustausch verwendet werden, müssen nicht mit den Datenstrukturen übereinstimmen, die Sie intern verwenden. Sie können sehr unterschiedlich sein.

Es ist Stand der Technik und allgemein anerkannt, dass es eine schlechte Idee ist, eine Geschäftslogik in den UI-Code zu integrieren. Aber jede Benutzeroberfläche ist nur eine Art Schnittstelle (das I in der Benutzeroberfläche), um die dahinter stehende Geschäftslogik zu steuern. Folglich scheint es offensichtlich, dass es auch eine schlechte Idee ist, eine Geschäftslogik in die REST-Serviceschicht oder eine andere API-Schicht zu integrieren.

Konzeptionell gibt es keinen so großen Unterschied zwischen Benutzeroberfläche und Service-API.

JensG
quelle
Ich stimme dem Layer-Begriff zu, aber was meinst du mit "Programmiere deinen Controller damit"?
Alain
1
Ich möchte die Tatsache betonen, dass der Controller selbst der eigentliche Service ist. Die Schnittstelle, die um das Ganze gewickelt ist, ist lediglich ein Mittel, um etwas zu erreichen. Es gibt jede Schnittstelle, um auf die eine oder andere Weise den Zugriff auf die umschlossenen Funktionen zu erleichtern. Eine GUI erledigt dies für menschliche Benutzer, eine Service-API wird von Clients verwendet. Beide Zielgruppen möchten mit den Dingen, die sich hinter der Benutzeroberfläche befinden, etwas erreichen. Ich bin damit einverstanden, dass "Programm" vielleicht nicht der beste Wortlaut dafür ist, aber "Steuerung der Controller" klingt auch unangenehm
;-)