WCF / SOA - Warum sollte ich Parameterobjekte für einfache Anforderungen erstellen?

12

Unser Unternehmen startet eine ziemlich große SOA-Initiative. Wir machen eine Menge richtig: Es gibt gute Kommunikation; gegebenenfalls Geld für Werkzeuge; und wir haben einige gute Fachkenntnisse mitgebracht, um uns bei der Umstellung zu helfen.

Wir versuchen Standards zu entwickeln, denen wir als Gruppe folgen können, und einer der vorgeschlagenen Standards stört mich ziemlich:

Wir haben das Muster standardisiert, bei dem jede Operation ein Anforderungsobjekt entgegennimmt und ein Antwortobjekt zurückgibt.

Mir ist klar, dass dies für viele mehr oder weniger ein Standardansatz ist, aber ich frage mich, warum ich mich darum kümmern soll. (Ich bin nicht so gut mit erhaltener Weisheit, ich brauche einige Gründe).

Die meisten der Dienste, die ich bereitstellen werde, sind das einfache Abrufen von Organisationsmetadaten. Suchen Sie beispielsweise die Sicherheitsrichtlinie für einen bestimmten Benutzer. Dies erfordert eine Benutzer-ID und sonst nichts. Der Standard sagt mir, dass ich diese Anforderung in ein Objekt und die zurückgegebene Richtlinie in ein Antwortobjekt einschließen soll.

Mein Unbehagen wird durch einen Blick auf die WSDL verstärkt, die sich aus unseren Verträgen ergibt. WCF generiert automatisch Anforderungs- und Antwortnachrichten und umschließt sogar das Anforderungs- / Antwortobjekt.

Ich verstehe voll und ganz, dass ein komplexes Eingabeobjekt gerechtfertigt ist, wenn Sie eine komplexe Anfrage stellen. Das ist es, was Sie tun würden, selbst wenn die Dienste nicht involviert wären.

Meine Frage ist, warum ich Anfragen und Antworten automatisch verpacken soll, wenn:

  • Es macht einfache Dienste weniger aussagekräftig
  • Sie würden es trotzdem für einen komplexen Service tun
  • WCF erstellt trotzdem eine Anfrage- / Antwortnachricht

Ich habe die folgenden Argumente für diesen Ansatz gefunden:

Die Versionierung wird unterstützt, indem optionale Parameter in das Anforderungsobjekt eingefügt werden.

Früher habe ich einiges an COM gemacht, und ich würde dies fast als Anti-Pattern für die Versionierung betrachten. Für einige Dinge würde es wahrscheinlich helfen, aber ich gehe davon aus, dass Sie dort, wo es helfen würde, sowieso bereits ein Parameterobjekt haben.

Dadurch können allgemeine Daten und Verhaltensweisen für eine Basisklasse isoliert werden

Dieser trägt etwas Gewicht bei mir.

Es lenkt Menschen von einem Verhalten im RPC-Stil weg und hin zu einem Messaging-Verhalten

Ich habe dies auf der Microsoft-Website gelesen und es von unserem Guru gehört, aber ich habe immer noch keine klare Vorstellung davon, was sie bedeuten oder warum es wertvoll ist. Ist es so, dass natürlich aussehende Benutzeroberflächen dazu neigen, zu vergessen, dass sie einen Remote-Service anrufen?

Ich bin gerade dabei, die Signaturen von vielleicht 300 Methoden zu überarbeiten, das ist also eine nicht unbedeutende Menge an Schmerz. Ich bin ein großer Fan von Konsequenz bei der Implementierung, daher bin ich bereit, den Schmerz in Kauf zu nehmen. Es hilft nur zu wissen, dass sich alles am Ende lohnt.

Andy Davis
quelle

Antworten:

3

Ich denke, Versionierung ist wahrscheinlich das beste Argument. Wenn Sie einen bestehenden Betriebsvertrag haben wie

int GetPersons(int countryId);

die Sie zB später durch einen anderen Filter erweitern möchten

int GetPersons(int countryId, int age);

Sie müssten einen neuen Betriebsvertrag mit einem neuen Namen schreiben, da dieser eindeutig sein muss. Oder Sie behalten den Namen bei und veröffentlichen eine neue Version Ihres Dienstes, wobei die alte Version 1 aus Gründen der Abwärtskompatibilität weiterhin verfügbar ist.

Wenn Sie den Parameter in ein Objekt einschließen, können Sie ihn jederzeit mit Standard- / optionalen Parametern erweitern. Alle vorhandenen Clients bleiben davon unberührt, wenn Sie denselben Operationsvertrag wiederverwenden.

Ich möchte Sie jedoch auch bitten, Ihre Objekte entsprechend zu benennen. Auch wenn es nur ein int umschließt, wenn Sie mit IntMessageoder ähnlichem beginnen, tun Sie sich selbst keinen Gefallen daran, es zu erweitern. Sie müssten es zB PersonFiltervon Anfang an benennen, was bedeutet, dass Sie sich ein wenig überlegen müssen, was dieser Serviceabruf als Parameter semantisch erwarten soll und was er daher tun soll. Vielleicht (und das ist vielleicht sehr vage) hilft dies bei der Entwicklung der richtigen Dienste und der Pflege einer API mit angemessener Größe.

Dadurch können allgemeine Daten und Verhaltensweisen für eine Basisklasse isoliert werden

Das ist etwas, mit dem man vorsichtig sein sollte. Vererbung und Datenverträge passen nicht so gut zusammen. Es funktioniert, aber Sie müssen alle bekannten Subtypen des Vertrags angeben, die über das Kabel gehen könnten. Andernfalls beschwert sich der Serializer für Datenverträge nicht über unbekannte Typen.

Aber was Sie tun könnten (aber wahrscheinlich immer noch nicht tun sollten, ich bin immer noch unentschlossen), ist, die gleichen Nachrichten unter verschiedenen Diensten wiederzuverwenden. Wenn Sie die Datenverträge in einer separaten DLL ablegen, können Sie diese zwischen Client und Service teilen und müssen nicht zwischen Typen konvertieren, wenn Sie verschiedene Services aufrufen, die im Grunde die gleiche Nachricht erwarten. Sie erstellen beispielsweise einen Personenfilter und senden diesen an einen Dienst, um eine Filterliste von Personen abzurufen. Anschließend senden Sie ihn an einen anderen Dienst und haben dieselben Objekte auf dem Client. Ich kann jedoch kein gutes reales Beispiel dafür finden, und die Gefahr besteht immer darin, dass eine Erweiterung der Datenverträge nicht für alle Dienste, die diesen Vertrag nutzen, allgemein genug ist.

Insgesamt kann ich, abgesehen von der Versionierung, auch nicht wirklich den tödlichen Grund dafür finden.

Andreas
quelle
Ich denke der Grund warum ich den
Andy Davis
(Entschuldigung, das Kommentar-Interface macht mir Sorgen.) Vielen Dank für Ihre Antwort. Ich denke, der Grund, warum ich das Versionsargument mit einem solchen Salzkorn betrachte, ist, dass Sie wahrscheinlich die Semantik geändert haben, wenn Sie ein neues Argument hinzugefügt haben. Wenn Sie (wie in Ihrem Beispiel) gerade ein Alter hinzugefügt haben, war die Semantik wahrscheinlich für die Suche und Sie hatten ein "Suchobjekt" darin, um mit zu beginnen.
Andy Davis
Betrachten Sie nun mein Beispiel für Sicherheitsrichtlinien. Um eine Sicherheitsrichtlinie ordnungsgemäß bereitzustellen, müssen wir möglicherweise nicht nur den Benutzer kennen, sondern auch die Einrichtung, in der er arbeitet. Dies ist viel mehr als das Hinzufügen eines Arguments. Wir haben die Semantik des Aufrufs geändert. Unter der Annahme, dass wir diese Informationen für einen Anrufer der vorherigen Version irgendwie bereitstellen können, erscheint es mir sinnvoller, den Servicevertrag zu versionieren. Dann kann die Implementierung für die alte Version isoliert werden und Sie mischen nicht die alte Semantik mit der neuen.
Andy Davis
0

Ich denke, Martin Fowlers Anmerkungen zum Datenübertragungsobjekt sind hier angebracht.

Wenn Sie mit einer Remote-Schnittstelle wie Remote Facade (388) arbeiten, ist jeder Anruf teuer. Infolgedessen müssen Sie die Anzahl der Anrufe reduzieren, und das bedeutet, dass Sie mit jedem Anruf mehr Daten übertragen müssen. Eine Möglichkeit, dies zu tun, besteht darin, viele Parameter zu verwenden. Dies ist jedoch oft umständlich zu programmieren - in der Tat ist es mit Sprachen wie Java, die nur einen einzigen Wert zurückgeben, oft unmöglich.

Die Lösung besteht darin, ein Datenübertragungsobjekt zu erstellen, das alle Daten für den Anruf enthalten kann. Es muss serialisierbar sein, um über die Verbindung zu gehen. Normalerweise wird ein Assembler auf der Serverseite verwendet, um Daten zwischen dem DTO und beliebigen Domänenobjekten zu übertragen.

Gleiches kann für Anfragen gelten. Was RPC betrifft, und warum ist es schlecht:

Ist es so, dass natürlich aussehende Benutzeroberflächen dazu neigen, zu vergessen, dass sie einen Remote-Service anrufen?

Ich denke das ist wahr, aber nicht der Hauptgrund. Ein weiterer Grund, RPC zu vermeiden, besteht darin, dass eng gekoppelte Clients und Dienste gefördert werden können.

Kyle Hodgson
quelle
Vielen Dank für den Input, aber ich denke nicht, dass die DTO-Diskussion so relevant ist. Es ist die gleiche Anzahl von Aufrufen, und wenn Sie sich die WSDL ansehen, wird alles automatisch in ein Anforderungs- und Antwortobjekt gepackt. Die Konvention befasst sich mit sichtbarer Semantik, dh, die Leute sollten dies als eine Anforderung und Antwort im Gegensatz zu einem Remote-Methodenaufruf betrachten.
Andy Davis
Möchten Sie näher auf RPCs eingehen, die eng verbundene Kunden und Services fördern? Ich kann nicht sehen, dass dies den Service überhaupt beeinträchtigt. Für den Kunden sehe ich auch nicht wirklich, wie sich etwas wirklich ändert. In beiden Fällen hat der Client einen Proxy, der eine Reihe von Vorgängen beschreibt, die von einem Dienst bereitgestellt werden. Wer die andere Seite des Services tatsächlich implementiert, ist in keinem Fall Sache des Kunden.
Andy Davis