API RESTful basata sulla funzione di progettazione

8

Si prega di risolvere una discussione tra me e un amico.

Attualmente stiamo progettando un'API di prodotto. La nostra entità prodotto è simile a questa

{
    "Id": "",
    "ProductName": "",
    "StockQuantity": 0
}

Le vendite di prodotti sono gestite da una terza parte e sono obbligate a informarci della quantità acquistata in modo che il campo StockQuantity possa essere diminuito.

Il mio approccio:

PUT /api/Product/{Id}/ --data { "StockQuantity": "{NewStockQuantity}" }

La terza parte è responsabile dell'interrogazione del prodotto, esegue il calcolo basato sulla percentuale attuale diStockQuantity e della quantità acquistata e inviando una richiesta di PUT con il nuovo valore.

Il mio amico non vuole che la terza parte faccia il calcolo. Il suo approccio

PUT /api/Product/{Id}/DecreaseStock --data { "PurchasedQuantity": "{PurchasedQuantity}" }

Quindi possiamo eseguire il calcolo e aggiornare StockQuantity

Non voglio creare endpoint basati su funzioni e non vuole affidarsi a terze parti per effettuare i calcoli.

Quale sarebbe il modo corretto di affrontare questo problema?

    
posta Sefa Ümit Oray 13.06.2018 - 10:48
fonte

5 risposte

19

Potresti consentire alla terza parte di pubblicare vendite sul tuo prodotto. Ad esempio:

POST /product/{id}/sale { "Quantity": 3 }

Sono d'accordo sia con la tua che con la tua collega. Questa è una logica aziendale e non dovrebbe essere lasciata al client dell'API, ma dovresti anche evitare di avere "funzioni" come endpoint.

A volte risolvere questi problemi è facile come chiamarlo in modo diverso, certamente non sempre.

    
risposta data 13.06.2018 - 10:58
fonte
3

Non c'è motivo per cui tu non possa fare nessuno dei due; o entrambi.

In un contesto di vendita, il monitoraggio delle singole transazioni ha molto senso. Lì, la soluzione di Robert ha molto senso.

In un contesto magazzino / magazzino, non è necessario tenere traccia delle transazioni tanto quanto "fare l'inventario"; con un endpoint che consente al client di segnalare i livelli di scorte

I have 10 units I have 7 units I have 3 units I have 20 units

ha molto senso.

I livelli delle scorte cambiano per ragioni diverse dalle "vendite"; solo qualcosa da tenere a mente.

In teoria, il livello delle scorte dovrebbe essere calcolabile dai cambiamenti; ma in alcuni domini è proprio l'assunto che tu vuoi verificare . Dovresti essere in grado di calcolare il livello del grezzo in due modi diversi e controllare le discrepanze (ovvero "restringimento").

Quindi non penso che la semantica sia chiara, in base al contesto che hai fornito.

Come per la parte HTTP; PUT [target-uri] ha senso semanticamente quando si sostituisce una rappresentazione di un documento con un'altra. È un UPSERT - il secondo PUT a una risorsa chiede di sovrascrivere la rappresentazione esistente.

PUT /sales { Quantity = 5 }
PUT /sales { Quantity = 2 }
PUT /sales { Quantity = 3 }

indica che la quantità di unità vendute è 3 , non 10 .

PUT /sales/1 { Quantity = 5 }
PUT /sales/2 { Quantity = 2 }
PUT /sales/3 { Quantity = 3 }

Ecco come 10 assomiglia a

PUT /sales { Quantity : [5] }
PUT /sales { Quantity : [5,2] }
PUT /sales { Quantity : [5,2,3] }

Questo è un altro modo di ortografare 10 .

POST /sales { Quantity = 5 }
POST /sales { Quantity = 2 }
POST /sales { Quantity = 3 }

Per quanto riguarda HTTP, anche questo è accettabile. Tuttavia, non è una grande scelta su una rete inaffidabile perché i messaggi a volte duplicato.

POST /sales { Quantity = 5 }
POST /sales { Quantity = 2 }
POST /sales { Quantity = 3 }
POST /sales { Quantity = 3 }

È questo 13 ? o 10 ?

PUT /sales/1 { Quantity = 5 }
PUT /sales/2 { Quantity = 2 }
PUT /sales/3 { Quantity = 3 }
PUT /sales/3 { Quantity = 3 }

Questo è univocamente 10

PUT /sales { Quantity : [5,2,3] }
PUT /sales { Quantity : [5,2,3] }

Questo è univocamente 10

PUT /sales/1 { Quantity = 5 }
PUT /sales/2 { Quantity = 2 }
PUT /sales/3 { Quantity = 3 }
PUT /sales/4 { Quantity = 3 }

Questo è inequivocabilmente 13

PUT /sales { Quantity : [5,2,3] }
PUT /sales { Quantity : [5,2,3,3] }

Questo è inequivocabilmente 13

POST /sales { TransactionId = 1 , Quantity = 5 }
POST /sales { TransactionId = 2 , Quantity = 2 }
POST /sales { TransactionId = 3 , Quantity = 3 }
POST /sales { TransactionId = 3 , Quantity = 3 }

10

POST /sales { TransactionId = 1 , Quantity = 5 }
POST /sales { TransactionId = 2 , Quantity = 2 }
POST /sales { TransactionId = 3 , Quantity = 3 }
POST /sales { TransactionId = 4 , Quantity = 3 }

13

(Per essere onesti, HTTP ha il supporto per richieste condizionali ; puoi rimuovere alcuni dei metadati dal tuo dominio protocollo specifico nelle intestazioni agnostiche del dominio per eliminare alcune delle ambiguità - se riesci a convincere il cliente a giocare insieme.

Naturalmente, ci sono trade off: l'HTML non ha il supporto PUT nativo; se intendi che i client della tua API siano browser, allora hai bisogno di un protocollo basato su POST o hai bisogno di estensioni code-on-demand per convertire l'invio di moduli da un POST a un PUT.

    
risposta data 13.06.2018 - 14:34
fonte
2

Questo sembra un design davvero pessimo, non importa come lo si affetta. Non mi fiderei mai di una terza parte per informarmi sull'inventario attuale a meno che non li abbia assunti per gestire il mio magazzino.

Inoltre, l'approccio alla funzione non è affatto RESTful e destinato a creare costernazione tra i tuoi consumatori.

Infine, non riesco a immaginare uno scenario in cui l'unica cosa che ti interessa di una vendita è l'inventario che ti è rimasto dopo averlo fatto.

Stai molto meglio se la terza persona pubblica una vendita o una fattura per te (con informazioni quali prodotto, quantità, data, metodo di spedizione, informazioni sul cliente, ecc.). In questo modo realizziamo analisi e tracciamento reali di ciò che vendi, a chi, quando, ecc. In modo da poter attuare la tua attività.

Anche se la tua terza parte sta eseguendo l'evasione degli ordini, vorrai monitorare le vendite per ragioni di contabilità e demografia dei clienti se non altro.

    
risposta data 13.06.2018 - 22:43
fonte
1

PUT /api/Product/{Id}/ --data { "StockQuantity": "{NewStockQuantity}" }

Questo tipo di design ha un grosso problema in quanto se vuoi avere più di un thread client in esecuzione contro la tua API, sei soggetto a letture / scritture sporche. Cioè, tra il momento in cui il client abbassa la quantità corrente e calcola il nuovo valore, un altro cliente può ottenere lo stesso valore precedente e calcolare una risposta diversa. La quantità che si finisce sarà l'ultimo che si aggiorna, ma nessuno dei due è corretto. Ad esempio, diciamo che la tua quantità attuale è 10. Il cliente A vuole vendere 5 articoli e tira la quantità corrente. Allo stesso tempo, il client B vuole vendere 6 articoli e ottiene la quantità corrente. Entrambi vedono 10 articoli in magazzino. A calcola 5 elementi rimanenti. B calcola 4 rimanenti. Entrambi gli aggiornamenti. Ora visualizzi 4 o 5 elementi rimanenti a seconda dell'ultimo aggiornamento registrato. Tuttavia, hai effettivamente venduto più oggetti che effettivamente hai. Quel che è peggio è che non c'è un modo semplice per attraversare e vedere cosa è andato storto. Tutto quello che hai sono due% non correttiPUTs nei tuoi log da guardare.

In qualsiasi sistema di registrazione del mondo reale, il semplice avere un totale attuale non è adeguato. Considera se vai in un negozio e compri un numero di articoli. Chiedete una ricevuta e il cassiere vi consegnerà una ricevuta con un solo totale. Come dimostreresti che il totale è corretto da quella ricevuta? Come vorresti mostrare che hai acquistato un oggetto se volessi restituire qualcosa?

L'approccio del tuo amico è migliore ma suggerirei di aggiungere un ID transazione nel mix. Ciò affronta le vere preoccupazioni che VoiceOfUnreason menziona sulle transazioni duplicate. Un'opzione consiste nel fornire un'operazione POST per creare una nuova transazione e quindi PUT su quella transazione per confermarla. Al punto di conferma si riduce lo stock totale o si rifiuta la richiesta perché non è disponibile a sufficienza.

    
risposta data 13.06.2018 - 16:23
fonte
0

Poiché le vendite sono gestite da terze parti, devi avere il controllo sull'inventario dei tuoi prodotti non permettendo loro di aggiornare il conteggio delle scorte.

Per uso interno, ad es. Conteggio titoli, puoi avere il tuo approccio, ovvero PUT /api/Product/{Id}/ --data { "StockQuantity": "{NewStockQuantity}" } .

Per uso esterno, devi creare un'interfaccia separata, ad es. /api/SalesOrder/ che prende un elenco di prodotti e quantità, come:

POST /api/SalesOrder/ --data { [{"Id": 1, "Qty": 1}, {"Id": 2, "Qty": 3}] }

Basato su SalesOrder inviato da terze parti, la quantità di ciascun prodotto può essere aggiornata e assegnata all'ordine oppure puoi rifiutare l'ordine se non c'è abbastanza prodotto disponibile.

L'elaborazione e il conteggio delle scorte sono processi interni, la terza parte richiede solo l'interfaccia in modo che possano inoltrare gli ordini all'inventario. In sostanza, SalesOrder è il modo in cui le vendite, le finanze e il magazzino comunicano per completare una vendita.

    
risposta data 14.06.2018 - 08:13
fonte

Leggi altre domande sui tag