Qual è la migliore pratica nella definizione di un servizio di sapone (operazione generica o specifica)?

StackOverflow https://stackoverflow.com/questions/180572

  •  05-07-2019
  •  | 
  •  

Domanda

La mia situazione è la seguente:

Ho un database normalizzato, in cui detengo informazioni geografiche sugli aeroporti. La struttura è:

airport --is in--> city --is in--> country --is in--> continent

Ora voglio consentire agli utenti di amministrare questi dati, senza dare loro l'accesso diretto al database. Dobbiamo offrire questa interfaccia di amministrazione tramite un servizio Web.

Ora, quando si tratta di progettare il servizio, ci siamo imbattuti nella discussione su come definire le operazioni. Abbiamo trovato diverse soluzioni:

Soluzione A: operazioni specifiche

Per ciascuna delle quattro tabelle (aeroporto, città, paese, continente) definiamo 3 operazioni:

  • Inserisci
  • get
  • update

Questo porterebbe a 12 operazioni con 2 oggetti richiesta / risposta = 24 oggetti

Per creare un aeroporto tutto nuovo con tutte le dipendenze, sarebbero necessarie almeno 4 richieste.

Soluzione B: generica

Esiste una sola operazione, controllata tramite parametri. Questa operazione è in grado di creare tutto il necessario per amministrare il database.

L'operazione dovrebbe decidere cosa deve essere fatto ed eseguirla. Se si verifica un errore, eseguirà il rollback di tutto.

== > 1 Operazione = 2 oggetti richiesta / risposta molto complessi

Soluzione C: Meet in the middle 1

Un'operazione generica per tabella, che è in grado di eseguire get, insert, update, proprio come la soluzione B, ma focalizzata su una tabella ciascuno.

== > 4 operazioni = 8 oggetti richiesta / risposta complessi

Soluzione D: Meet in the middle 2

Un'operazione generica per azione (get, insert, delete), che può funzionare su ogni tabella e risolvere dipendenze.

== > 3 operazioni = 6 oggetti richiesta / risposta leggermente più complessi

Esempio

Poiché questo era piuttosto astratto, è un esempio semplificato di oggetti richiesta per la creazione (JFK / New York / USA / Nord America):

Soluzione A:

Richiesta 1/4:

<insertContinent>North America</insertContinent>

Richiesta 2/4:

<insertCountry continent="North America">USA</insertCountry>

Richiesta 3/4:

<insertCity country="USA">New York</insertCity>

Richiesta 4/4:

<insertAirport city="New York">JFK</insertAirport>

Soluzione B:

Richiesta 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>

Soluzione C:

Richiesta 1/4:

<countryAction type="insert" parent="North America">USA</countryAction>

Richiesta 2/4:

<airportAction type="insert" parent="New York">JFK</airportAction>

Richiesta 3/4:

<continentAction type="insert" parent="">North America</continentAction >

Richiesta 4/4:

<cityAction type="insert" parent="USA">New York</cityAction >

Soluzione D: Richiesta 1/1:

<insert airport="JFK" city="New York" country="USA" continent="North America" />

La soluzione D mi sembra piuttosto elegante, quindi ho cercato di inserirlo in XSD:

Codice:

<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>

Una richiesta corrispondente dovrebbe quindi apparire come segue:

<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>

Ora la mia domanda: È davvero la migliore soluzione disponibile? L'XSD è abbastanza per capire cosa sta succedendo?

È stato utile?

Soluzione

Presumibilmente stai scrivendo un livello di protocollo in grado di comprendere i diversi tipi di messaggi. Sarà inoltre necessario un livello applicazione per analizzare il contenuto del messaggio. I diversi approcci che menzionerai spostano l'onere dell'analisi tra questi due livelli. Quindi ad esempio:

Soluzione A : il livello del protocollo esegue tutte le analisi e restituisce i dati e il comando. Il livello applicazione può semplicemente usare i dati. Questo è anche noto come modello RPC.

Pro: puoi convalidare i tuoi messaggi. Puoi mappare i messaggi direttamente alle chiamate dell'applicazione.

Contro: se è necessario apportare una modifica all'interfaccia, il protocollo cambia.

Soluzione B : il livello protocollo restituisce due valori e un comando. Il livello applicazione deve utilizzare il comando per analizzare i valori in tipi.

Pro: il protocollo non cambia mai.

Contro: non è possibile convalidare i messaggi. Il codice dell'applicazione è più complicato.

Soluzione C : il livello protocollo restituisce due tipi noti e un comando che deve essere analizzato. Il livello applicazione può semplicemente analizzare il comando e utilizzare i dati.

Pro: non riesco a pensare a nessuno, sembra non essere un ottimo compromesso.

Contro: lascia l'analisi eseguita solo parzialmente.

Soluzione D : il livello protocollo restituisce tipi noti (il modo in cui è stato implementato) e un comando generico. Il livello dell'applicazione deve guardare i dati che riceve e convertire il comando generico in un comando specifico. Questo è simile all'architettura REST.

Pro: le chiamate sono operazioni distinte in modo che tu possa ad esempio ricevere risposte dalla cache.

Contro: complessità nel livello applicazione

Il modello REST è di solito implementato in modo diverso da come è stato delineato. Utilizza i messaggi HTTP GET, POST, PUT, DELETE per comunicare documenti arbitrari. I parametri sono indicati come parte dell'URL. Quindi ad esempio:

<insert airport="JFK" city="New York" country="USA" continent="North America" />

diventa

<insert URL="airport?city=Chicago">ORD</insert>

O se si utilizza HTTP diventa una richiesta POST a un URL di aeroporto con un parametro della città con i contenuti che sono informazioni sull'aeroporto. Nota che alcuni di questi diventano più chiari con dati più calcolati in cui hai più elementi e tipi misti. Ad esempio, se si desidera inviare l'abbreviazione, il nome lungo e l'altitudine dell'aeroporto.

Penso che l'architettura REST potrebbe funzionare abbastanza bene per l'interfaccia che descrivi. Finché tutto ciò che devi fare è supportare le operazioni CRUD. Ci sono molti siti che ti daranno i pro e i contro dello stile architettonico REST.

Personalmente preferisco lo stile RPC (Soluzione A) con alcuni attributi REST-ful. Voglio che il protocollo esegua il lavoro di analisi e convalidi i messaggi. Questo è in genere il modo in cui le persone implementano le interfacce dei servizi Web SOAP.

La tua interfaccia potrebbe sembrare semplice oggi, ma domani uno dei tuoi clienti ti chiederà una nuova chiamata che non si adatta così bene al modello REST e ti ritroverai a inserirla nei quattro messaggi esistenti.

Altri suggerimenti

Questa è una vecchia domanda, e sono sicuro che il servizio è stato scritto molto tempo fa, ma volevo comunque dare una risposta.

L'approccio RESTful sarebbe quello di definire una risorsa aeroportuale, come questa:

<airport href="/airports/JFK">
    <name>JFK</name>
    <city>New York</city>
    <country>USA</country>
    <continent>North America</continent>
</airport>

Oppure, se si desidera utilizzare un microformato compatibile con browser:

<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>

Questa risorsa dovrebbe trovarsi in un URI come / airport / JFK , che verrebbe recuperato con un metodo GET , aggiornato con un PUT ed eliminato con un metodo DELETE .

In un progetto come questo, l'URI / airport / rappresenterebbe una risorsa container per tutti gli aeroporti nel database e gli URI come / airport /? city = New + York e / airport /? country = USA sarebbero filtri sul container per restituire un sottoinsieme degli aeroporti. Entrambi questi sarebbero i metodi GET e le risorse conterrebbero un elenco di risorse aeroportuali come sopra definito, o per intero (poiché sono piccole) o con alcuni attributi utili e il href che punta alla risorsa completa per ciascun aeroporto.

Infine, l'aggiunta di una nuova risorsa potrebbe essere un metodo PUT sull'URI completo dell'aeroporto o un metodo POST su / airport / . In entrambi i casi il corpo della richiesta è la risorsa aeroportuale come mostrato sopra. La differenza tra i metodi è chi decide l'URI finale per l'aeroporto: il cliente decide per PUT e il servizio decide per POST . Quale utilizzi dipende dal fatto che i tuoi clienti possano ragionevolmente determinare l'URI giusto. Di solito il servizio decide perché gli URI contengono un identificatore univoco numerico e il servizio deve sceglierlo.

Ora, naturalmente, la tua domanda originale riguardava SOAP, non REST. Vorrei andare avanti e impostare un design RESTful come ho descritto, quindi descrivere le mie risorse come tipi complessi usando XSD e un servizio SOAP con azioni che duplicano GET , PUT , DELETE e POST del servizio RESTful. Questo ti darà l'equivalente RPC di:

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)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top