Perché REST Api non segue il modello di progettazione della facciata


9

Nel confrontare la struttura REST [api] con un modello OO, vedo queste somiglianze:

Tutti e due:

  • Sono orientati ai dati

    • REST = Risorse
    • OO = Oggetti
  • Operazione surround attorno ai dati

    • REST = circondare VERBI (Ottieni, Posta, ...) attorno alle risorse
    • OO = promuove l'operazione attorno agli oggetti mediante incapsulamento

Tuttavia, le buone pratiche di OO non si basano sempre sulle API REST quando si tenta di applicare il modello di facciata ad esempio: in REST, non si dispone di 1 controller per gestire tutte le richieste E non si nasconde la complessità degli oggetti interni.

Relazione dell'oggetto semplice tra 2 concetti

Analogia tra OO e REST

Al contrario, REST promuove la pubblicazione di risorse di tutte le relazioni con una risorsa e altre su almeno due forme:

  1. tramite le relazioni della gerarchia delle risorse (un contatto di id 43 è composto da un indirizzo 453): /api/contacts/43/addresses/453

  2. tramite collegamenti in una risposta json REST:

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

Complessità di base nascosta dall'oggettoA

Tornando a OO, il modello di progettazione della facciata rispetta a Low Couplingtra un oggetto A e il suo " client oggetto B " e High Cohesionper questo oggetto A e la sua composizione interna dell'oggetto (oggetto C , oggetto D ). Con l' interfaccia objectA , ciò consente a uno sviluppatore di limitare l'impatto sull'oggetto B delle modifiche interne dell'oggetto A (in oggetto C e oggetto D ), purché l' oggetto Api (operazioni) sia ancora rispettato.

In REST, i dati (risorsa), le relazioni (collegamenti) e il comportamento (verbi) sono esplosi in diversi elementi e disponibili per il web.

Giocando con REST, ho sempre un impatto sulle modifiche del codice tra il mio client e il server: perché ho High Couplingtra le mie Backbone.jsrichieste e Low Cohesiontra le risorse.

Non ho mai capito come lasciare che il mio Backbone.js javascript applicationpatto con la scoperta di " risorse e funzionalità REST " sia promosso dai link REST. Comprendo che il WWW è pensato per essere servito da più server e che gli elementi OO dovevano essere sfruttati per essere serviti da molti host, ma per uno scenario semplice come "salvare" una pagina che mostra un contatto con i suoi indirizzi, Finisco con:

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

che mi ha portato a spostare l'azione di salvataggio della responsabilità transazionale atomica sulle applicazioni del browser (poiché due risorse possono essere indirizzate separatamente).

Con questo in mente, se non riesco a semplificare il mio sviluppo (modelli di progettazione della facciata non applicabili) e se porto più complessità al mio cliente (gestendo il salvataggio atomico transazionale), qual è il vantaggio di essere RESTful?


1
Fammi capire Stai dicendo che devi aggiornare un contatto con un indirizzo "incorporato" (composizione) collegato usando due chiamate REST, una per il contatto e un'altra per il suo indirizzo. Hai una facciata per gestire i contatti di aggiornamento. Qual è il problema con l'esecuzione in PUT /api/contacts/43cascata degli aggiornamenti agli oggetti interni? Ho avuto molte API progettate in questo modo (l'URL principale legge / crea / aggiorna il "tutto" e gli URL secondari aggiornano i pezzi). Assicurati solo di non aggiornare l'indirizzo quando non sono necessarie modifiche (per motivi di prestazioni).
Anthony Accioly,

@AnthonyAccioly, hai capito bene. Ho cercato di chiarire la mia domanda aggiungendo alcune immagini. Il tuo suggerimento è valido e questa è anche la conclusione con cui sono arrivato: controllo manuale della mia richiesta e utilizzo di un oggetto incorporato per inviare una sola richiesta per mantenere atomico il mio aggiornamento. Ancora: perché tutto in REST mi allontana dalle qualità o dalle forzature OO (incapsulamento, ...) appiattendo il mio modello (implicando molti controller). L'uso della soluzione fornisce 1 aggiornamento atomico. Non utilizzare la soluzione comporta una nuova responsabilità per gli sviluppatori e nessuna regola sull'API per impedirgli di farlo.
Alain,

Solo una nota: le "relazioni della gerarchia delle risorse" che hai citato sono una questione di come si potrebbe decidere di codificare le informazioni sulle relazioni in un identificatore (in questo caso, un URL). Non sono sicuro che questa esposizione di informazioni sia qualcosa che fa parte di REST o qualcosa che promuove, solo una decisione che uno prende da solo quando si presentano gli URL di un sistema. Se credi diversamente, hai qualche riferimento a Roy Fielding che discute la questione come parte di REST?
Thiago Silva,

Risposte:


7

Penso che gli oggetti siano costruiti correttamente solo attorno a comportamenti coerenti e non attorno ai dati. Provocherò e dirò che i dati sono quasi irrilevanti nel mondo orientato agli oggetti. In effetti, è possibile e talvolta comune avere oggetti che non restituiscono mai dati, ad esempio "sink di registro" o oggetti che non restituiscono mai i dati che vengono passati, ad esempio se calcolano proprietà statistiche.

Non confondiamo i PODS (che sono poco più che strutture) e gli oggetti reali che hanno comportamenti (come la Contactsclasse nel tuo esempio) 1 .

I POD sono sostanzialmente una comodità utilizzata per parlare con repository e oggetti business. Consentono al codice di essere sicuro. Ne più ne meno. Gli oggetti business, d'altra parte, forniscono comportamenti concreti , come la convalida dei dati, la loro memorizzazione o il loro utilizzo per eseguire un calcolo.

Quindi, i comportamenti sono ciò che usiamo per misurare la "coesione" 2 , ed è abbastanza facile vedere che nel tuo esempio di oggetto c'è una certa coesione, anche se mostri solo metodi per manipolare i contatti di livello superiore e nessun metodo per manipolare gli indirizzi.

Per quanto riguarda REST, puoi vedere i servizi REST come repository di dati. La grande differenza con il design orientato agli oggetti è che esiste (quasi) una sola scelta di design: hai quattro metodi di base (di più se conti HEAD, ad esempio) e ovviamente hai un grande margine di manovra con gli URI in modo da poter fare la nifty cose come passare molti id e ottenere indietro una struttura più grande. Non confondere i dati che passano con le operazioni che eseguono. La coesione e l'accoppiamento riguardano il codice e non i dati .

Chiaramente, i servizi REST hanno un'elevata coesione (ogni modo di interagire con una risorsa è nello stesso posto) e un basso accoppiamento (ogni archivio di risorse non richiede la conoscenza degli altri).

Resta il fatto fondamentale, tuttavia, REST è essenzialmente un modello di repository singolo per i tuoi dati. Ciò ha conseguenze, perché si tratta di un paradigma basato sulla facile accessibilità su un mezzo lento, in cui vi è un costo elevato per "chattiness": i clienti in genere vogliono eseguire il minor numero possibile di operazioni, ma allo stesso tempo ricevono solo i dati di cui hanno bisogno . Ciò determina la profondità con cui un albero di dati verrà restituito.

Nella progettazione (corretta) orientata agli oggetti, qualsiasi applicazione non banale farà operazioni molto più complesse, ad esempio attraverso la composizione. Potresti avere metodi per eseguire operazioni più specializzate con i dati, il che deve essere così, perché mentre REST è un protocollo API, OOD viene utilizzato per creare applicazioni rivolte all'intero utente! Questo è il motivo per cui misurare la coesione e l'accoppiamento è fondamentale in OOD, ma quasi insignificante in REST.

Ormai dovrebbe essere ovvio che analizzare la progettazione dei dati con i concetti di OO non è un modo affidabile per misurarlo: è come confrontare mele e arance!

In effetti, si scopre che i vantaggi di essere RESTful sono (principalmente) quelli descritti sopra: è un buon modello per API semplici su un supporto lento. È molto memorizzabile nella cache e frammentabile. Ha un ottimo controllo della chattiness, ecc.

Spero che questo risponda alla tua (piuttosto poliedrica) domanda :-)


1 Questo problema fa parte di una più ampia serie di problemi noti come mancata corrispondenza dell'impedenza oggetto-relazione . I sostenitori degli ORM sono generalmente nel campo che esplora le somiglianze tra analisi dei dati e analisi del comportamento, ma gli ORM sono stati criticati di recente perché sembrano non risolvere realmente il disadattamento dell'impedenza e sono considerati astrazioni che perdono .

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


Hai ragione, ho avuto difficoltà a far esplodere la mia domanda in molti aspetti, a definire un punto specifico, poiché la domanda affronta una conclusione "sbagliata" basata sull'accumulo di questi aspetti. Proverò ora a rispondere ai tuoi punti in molti commenti.
Alain,

[testo 1] Ho usato la parola "dati" per astrarre dal mondo OO e REST. Quale parola useresti per astrarre le proprietà in OO e la struttura dei dati in REST?
Alain,

@Alain "dati" va bene, ma il mio punto non è quello di confondere PODS e oggetti business. Quando parliamo di OOD generalmente parliamo del secondo. I primi sono una comodità, e potrebbe essere quasi altrettanto facile pensare a un dizionario, a una struttura o a una tupla.
Sklivvz,

Sì, sono d'accordo, utilizzo Backbone.js in cui il salvataggio di un modello utilizza una struttura json semplice. Qui è dove il testo riflette la mia esperienza di programmazione reale.
Alain,

[testo 3] Questo è nuovo per me. Pensavo che la coesione fosse misurata dal numero di volte in cui i metodi usano una relazione specifica ... Preferisco il tuo modo di vederlo.
Alain,

1

Con questo in mente, se non riesco a semplificare il mio sviluppo (modelli di progettazione della facciata non applicabili) e se porto più complessità al mio cliente (gestendo il salvataggio atomico transazionale), qual è il vantaggio di essere RESTful?

La risposta a "dov'è il vantaggio di essere RESTful?" viene accuratamente analizzato e spiegato qui: http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

La confusione in questa domanda però è che non si tratta delle caratteristiche di REST e di come affrontarle, ma supponendo che il design degli URL che hai creato per il tuo sistema di esempio abbia qualcosa a che fare con l'essere RESTful. Dopotutto, REST afferma che ci sono cose chiamate risorse e un identificatore dovrebbe essere fornito per quelli che devono essere referenziati, ma non impone che, diciamo, le entità nel tuo modello ER dovrebbero avere una corrispondenza 1-1 con gli URL che hai creato (né che gli URL dovrebbero codificare la cardinalità delle relazioni ER nel modello).

Nel caso di contatti e indirizzi, avresti potuto definire una risorsa che rappresenta congiuntamente queste informazioni come una singola unità, anche se potresti voler estrarre e salvare queste informazioni, per esempio, in diverse tabelle DB relazionali, ogni volta che sono PUT o POSTed .


1

Questo perché le facciate sono un "kludge"; dovresti dare un'occhiata a 'api astrazione' e 'api concatenamento'. L'API è una combinazione di due set di funzionalità: I / O e gestione delle risorse. A livello locale, l'I / O va bene ma all'interno di un'architettura distribuita (ovvero proxy, api gate, coda messaggi, ecc.) L'I / O è condiviso e quindi i dati e la funzionalità vengono duplicati e impigliati. Ciò porta a preoccupazioni trasversali a livello architettonico. Questo affligge TUTTE le API esistenti.

L'unico modo per risolvere questo problema è quello di astrarre la funzionalità I / O per l'API su un gestore pre / post (come un gestore Interterter in Spring / Grails o un filtro in Rails) in modo che la funzionalità possa essere utilizzata come monade e condivisa tra istanze ed esterne utensili. Anche i dati per richiesta / risposta devono essere esternalizzati in un oggetto in modo che possano essere condivisi e ricaricati.

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


0

Se capisci il tuo servizio REST, o in generale qualsiasi tipo di API, proprio come un'interfaccia aggiuntiva esposta ai client in modo che possano programmare i tuoi controller attraverso di essa, improvvisamente diventa facile. Il servizio non è altro che un livello aggiuntivo in aggiunta alla tua logica biz.

In altre parole, non devi dividere la logica biz tra più controller, come hai fatto nella tua foto sopra, e, cosa più importante, non dovresti. Le strutture di dati utilizzate per scambiare i dati non devono necessariamente corrispondere alle strutture di dati utilizzate internamente, possono essere piuttosto diverse.

È all'avanguardia e ampiamente accettato, che è una cattiva idea inserire qualsiasi logica biz nel codice dell'interfaccia utente. Ma ogni interfaccia utente è solo un tipo di interfaccia (l'I in UI) per controllare la logica biz dietro. Di conseguenza, sembra ovvio che sia anche una cattiva idea inserire qualsiasi logica biz nel livello di servizio REST o in qualsiasi altro livello API.

Concettualmente parlando, non c'è molta differenza tra l'interfaccia utente e l'API di servizio.


Sono d'accordo sulla nozione di livello, ma cosa intendi con "programma il controller tramite esso"?
Alain,

1
Voglio sottolineare il fatto che il controller stesso è il vero servizio. L'interfaccia racchiusa nell'insieme è semplicemente un mezzo per ottenere qualcosa. Esiste un'interfaccia per facilitare l'accesso alla funzionalità "wrapped", in un modo o nell'altro. Una GUI lo fa per gli utenti umani, un'API di servizio viene utilizzata dai client. Entrambi i destinatari vogliono ottenere qualcosa con le cose avvolte dietro l'interfaccia. Concordo sul fatto che "programma" potrebbe non essere la migliore espressione, ma "controllo i controller" suona anche imbarazzante ;-)
JensG
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.