Was ist die beste Praxis eine Seife Service (generic vs bestimmte Operation) bei der Definition?
-
05-07-2019 - |
Frage
Meine Situation ist wie folgt:
Ich habe eine normalisierte Datenbank, in der ich geografische Informationen zu Flughäfen halten. Die Struktur ist:
airport --is in--> city --is in--> country --is in--> continent
Jetzt möchte ich Anwender diese Daten verwalten lassen, ohne dass sie den direkten Zugriff auf die Datenbank zu geben. Wir brauchen diese Administrationsoberfläche über einen Web-Service zu bieten.
Nun, wenn es um die Gestaltung der Service geht, liefen wir in die Diskussion darüber, wie die Vorgänge zu definieren. Wir kamen mit verschiedenen Lösungen:
Lösung A: spezifische Operationen
Für jede der vier Tabellen (Flughafen, Stadt, Land, Kontinent) definieren wir drei Operationen:
- Einsatz
- get
- update
Das zu 12 Operationen führen würde mit 2 Request / Response-Objekten = 24 Objekte
Um ein alle neuen Flughafen mit allen Abhängigkeiten, mindestens 4-Anforderungen notwendig wäre zu erstellen.
Die Lösung B: generic
Es gibt nur eine Operation, die über die Parameter gesteuert wird. Dieser Vorgang ist in der Lage, alles zu schaffen benötigt, um die Datenbank zu verwalten.
Die Operation würde entscheiden, was getan werden muss, und führt sie aus. Wenn ein Fehler occures, wird es alles rückgängig zu machen.
==> 1 Betrieb = 2 hoch komplexes Request / Response-Objekte
Die Lösung C: Treffen in der Mitte 1
Eine generische Operation pro Tabelle, die get ausführen kann, einfügen, aktualisieren, genau wie die Lösung B, sondern konzentrierte sich auf eine Tabelle jeweils.
==> 4 Operationen = 8 komplexe Anforderung / Antwort-Objekte
Die Lösung D: Treffen in der Mitte 2
Eine generische Operation pro Aktion (get, einfügen, löschen), die auf jedem Tisch arbeiten können und lösen Abhängigkeiten.
==> 3 Operationen = 6 etwas komplexere Request / Response-Objekte
Beispiel:
Da dies eher abstrakt, hier ein vereinfachtes Beispiel für Anfrage-Objekte für die Erstellung von (JFK / New York / USA / Nordamerika):
Lösung A:
Anfrage 1/4:
<insertContinent>North America</insertContinent>
Anfrage 2/4:
<insertCountry continent="North America">USA</insertCountry>
Anfrage 3/4:
<insertCity country="USA">New York</insertCity>
Anfrage 4/4:
<insertAirport city="New York">JFK</insertAirport>
Lösung B:
Anfrage 1/1:
<action type="insertCountry" parent="North America">USA</action>
<action type="insertAirport" parent="New York">JFK</action>
<action type="insertContinent" parent="">North America</action>
<action type="insertCity" parent="USA">New York</action>
Lösung C:
Anfrage 1/4:
<countryAction type="insert" parent="North America">USA</countryAction>
Anfrage 2/4:
<airportAction type="insert" parent="New York">JFK</airportAction>
Anfrage 3/4:
<continentAction type="insert" parent="">North America</continentAction >
Anfrage 4/4:
<cityAction type="insert" parent="USA">New York</cityAction >
Lösung D: Anfrage 1/1:
<insert airport="JFK" city="New York" country="USA" continent="North America" />
Die Lösung D für mich eher elegant scheint daher habe ich versucht, dies in XSD zu setzen:
Code:
<complexType name="NewContinent">
<sequence>
<element name="NAME" type="string"></element>
</sequence>
</complexType>
<complexType name="NewCountry">
<sequence>
<element name="ISOCODE" type="string"></element>
<element name="NAME" type="string"></element>
<choice>
<element name="newCONTINENT" type="tns:NewContinent"></element>
<element name="CONTINENT" type="string"></element>
</choice>
</sequence>
</complexType>
<complexType name="NewCity">
<sequence>
<element name="IATA" type="string"></element>
<element name="NAME" type="string"></element>
<choice>
<element name="COUNTRY" type="string"></element>
<element name="newCOUNTRY" type="tns:NewCountry"></element>
</choice>
</sequence>
</complexType>
<complexType name="NewAirport">
<sequence>
<element name="IATA" type="string"></element>
<element name="NAME" type="string"></element>
<choice>
<element name="CITY" type="string"></element>
<element name="newCITY" type="tns:NewCity"></element>
</choice>
</sequence>
</complexType>
Eine entsprechende Anfrage würde dann folgendermaßen aussehen folgt:
<complexType name="Request">
<choice>
<element name="AIRPORT" type="tns:NewAirport"></element>
<element name="CITY" type="tns:NewCity"></element>
<element name="COUNTRY" type="tns:NewCountry"></element>
<element name="CONTINENT" type="tns:NewContinent"></element>
</choice>
</complexType>
Jetzt meine Frage: Ist das wirklich die beste Lösung? Ist das XSD genug zu verstehen, was los ist?
Lösung
Vermutlich schreiben Sie eine Protokollschicht, die Ihre verschiedenen Nachrichtentypen verstehen. Sie werden auch eine Anwendungsschicht benötigen Sie den Inhalt der Nachricht zu analysieren. Die verschiedenen Ansätze, die Sie erwähnen, die Last der Parsen zwischen diesen beiden Schichten verschieben. So zum Beispiel:
Lösung A : Die Protokollschicht macht die ganze Analyse und liefert die Daten und Befehl. Die Anwendungsschicht kann nur die Daten verwenden. Dies wird auch als RPC-Muster bekannt.
Vorteile: Sie können Ihre Nachrichten validieren. Sie können Nachrichten direkt an Anwendungsaufrufe Karte.
Nachteile:. Wenn Sie eine Änderung an der Schnittstelle, das Protokoll Änderungen vornehmen
Die Lösung B : Die Protokollschicht gibt zwei Werte und einen Befehl aus. Die Anwendungsschicht muss den Befehl verwenden, um die Werte in Typen zu analysieren.
Pros:. Das Protokoll ändert sich nie
Nachteile: Sie können keine Nachrichten validieren. Ihr Anwendungscode ist komplizierter.
Die Lösung C : Die Protokollschicht gibt zwei bekannte Arten und einen Befehl, die analysiert werden müssen. Die Anwendungsschicht kann nur den Befehl analysieren und die Daten verwenden.
Pros:. Ich kann nicht von jedem denkt, scheint nicht sehr guter Kompromiss
Nachteile:. Verläßt das Parsen nur teilweise getan
Die Lösung D : Die Protokollschicht kehrt bekannten Typen (wie Sie implementiert es) und ein allgemeiner Befehl. Die Anwendungsschicht muss an den Daten schauen sie empfangen, und den generischen Befehl in einen bestimmten Befehl umwandeln. Dies ist vergleichbar mit der REST-Architektur.
Vorteile: Die Anrufe sind verschiedene Operationen, so dass Sie zum Beispiel Cache könnte Antworten bekommen
.Nachteile: Komplexität in der Anwendungsschicht
Das REST-Modell ist in der Regel anders umgesetzt als Sie skizziert haben. Es verwendet HTTP GET, POST, PUT, DELETE Nachrichten beliebige Dokumente zu kommunizieren. Die Parameter werden als Teil der URL angegeben. So zum Beispiel:
<insert airport="JFK" city="New York" country="USA" continent="North America" />
wird
<insert URL="airport?city=Chicago">ORD</insert>
Oder wenn Sie HTTP verwenden, wird es eine POST-Anforderung an einen Flughafen URL mit einem param der Stadt mit den Inhalten Informationen über den Flughafen. Beachten Sie, dass einige dieser wird klarer mit mehr compliated Daten, bei denen Sie mehrere Elemente und Mischtypen haben. Zum Beispiel, wenn Sie wollen die Flughafen Abkürzung, lange Namen und Höhe senden.
Ich denke, die REST-Architektur ganz gut für die Schnittstelle arbeiten könnten Sie beschreiben. Solange alles, was Sie tun müssen, ist die CRUD-Operationen unterstützen. Das sind viele Websites, die Sie die Vor- und Nachteile des REST architektonischen Stil geben wird.
Persönlich ziehe ich den RPC-Stil (Lösung A) mit einigen REST-ful Attributen. Ich mag das Protokoll die Parsing-Arbeit zu tun, und die Nachrichten zu validieren. Dies ist in der Regel, wie die Menschen SOAP Webservice-Schnittstellen implementieren.
Ihre Schnittstelle kann einfach heute aussieht, aber morgen einer Ihrer Kunden wird Sie für einen neuen Anruf stellen, die nicht das REST-Modell so gut passen, und Sie werden ihr es in die bestehenden vier Nachrichten finden verkeilen.
Andere Tipps
Dies ist eine alte Frage, und ich bin sicher, dass der Dienst vor langer Zeit geschrieben wurde, aber ich wollte sowieso eine Antwort beitragen.
Der RESTful Ansatz wäre, eine Flughafen-Ressource zu definieren, wie folgt aus:
<airport href="/airports/JFK">
<name>JFK</name>
<city>New York</city>
<country>USA</country>
<continent>North America</continent>
</airport>
Oder, wenn Sie einen Browser-kompatiblen Mikroformat verwenden:
<div class="object airport" href="/airports/JFK">
<ul class="attributes">
<li class="name">JFK</li>
<li class="city">New York</li>
<li class="country">USA</li>
<li class="continent">North America</li>
</ul>
</div>
Diese Ressource bei einer URI wie /airports/JFK
befinden würde, die mit einem GET
Verfahren abgerufen werden würden, mit einem PUT
Verfahren aktualisiert, und mit einem DELETE
Verfahren gelöscht.
In einem Entwurf so würde der URI /airports/
einen Container Ressource darstellt für alle von den Flughäfen in der Datenbank, und URIs wie /airports/?city=New+York
und /airports/?country=USA
wäre Filter auf den Behälter eine Teilmenge der Flughäfen zurückzukehren. Beide würden GET
Methoden sein, und die Mittel würden eine Liste der Flughafen Ressourcen enthalten, wie oben definiert, die entweder vollständig (da sie klein ist) oder mit einem paar nützlichen Eigenschaften und dem href
, die für jeden Flughafen der vollen Ressource Punkte .
Schließlich könnte eine neue Ressource hinzugefügt entweder eine PUT
Methode auf dem vollständigen URI des Flughafens oder eine POST
Methode auf /airports/
. In beiden Fällen ist der Körper des Antrags ist der Flughafen Ressource wie oben gezeigt. Der Unterschied zwischen den Methoden ist, der die endgültige URI für den Flughafen zu entscheiden, bekommt: Der Kunde entscheidet sich für PUT
und der Service entscheidet sich für POST
. Welche Sie verwenden, hängt davon ab, ob oder nicht Ihre Kunden die richtige URI vernünftigerweise bestimmen kann. Normalerweise entscheidet der Service, da die URIs eine numerische eindeutige Kennung enthält und der Service muss, dass wählen.
Jetzt natürlich Ihre ursprüngliche Frage war über SOAP, nicht zur Ruhe. Ich würde gehen Sie vor und legen Sie ein RESTful-Design, wie ich beschrieben habe, dann meine Ressourcen als komplexe Typen beschreiben mit XSD und einen SOAP-Dienst mit Aktionen, die die GET
, PUT
, DELETE
und POST
Operationen des RESTful Service dupliziert. Dadurch erhalten Sie die RPC-Äquivalent:
class Airport
has String name
has String city
has String country
has String continent
method void update(name, city, country, continent)
method void delete()
class AirportList
method Airport[] get(opt name, opt city, opt country, opt continent)
method void add(name, city, country, continent)