Come modellare per un'API RESTful che funziona con diversi fornitori di servizi

1

Sto cercando di sviluppare un'API che cerchi di integrare le funzionalità di vari fornitori di servizi. Sono nella fase di sviluppo di un modello (struttura di classe che si identifica con il contesto del dominio) che aiuta a unificare i campi per i dati che vengono inviati a diversi fornitori di servizi.

Il problema che sto cercando di risolvere è scegliere se consolidare i campi di diversi fornitori di servizi per creare un modello completamente generico o avere un modello con campi comuni accanto a campi specifici del provider.

Ad esempio: ho un'API per noleggio auto che cerca di interfacciarsi con più fornitori. Ogni venditore ha un'API che mi consente di cercare auto in un'area con-in una categoria di auto. Ma ogni vendor-api ha le proprie capacità uniche che li separano dalla concorrenza.

Quando partecipo all'integrazione con diversi fornitori, vorrei che la mia API eseguisse solo la ricerca delle funzioni di base, per la quale sono in grado di identificare i campi richiesti per il modello comune. Ma mentre la mia API si evolve, voglio esporre i parametri di ricerca unici del venditore attraverso la mia API. Quando sono in fase di espansione delle funzionalità, separo i campi ad esse correlati in classi diverse o li compongo in modelli esistenti oppure li aggiungo al modello comune e interpreto campi univoci in base al contesto disponibile dai dati.

Approccio 1:

CarReservation{

  String userName;    
  String id;
  // in millis
  long startDate;
  long endDate;

  CarCategory carCategory;

   //--end common model
   // only vendor 1 supports discounts on referrals 
   Referral referral;

   // only vendor 2 supports delivering and pick up from home
   Address homeDeliveryAddress; 
    ...    
 }

Approccio 2:

CarReservation{

  String userName;    
  String id;
  // in millis
  long startDate;    
  long endDate;

  CarCategory carCategory;

  //--end common model

  vendor_1_name : {
  // only vendor 1 supports discounts on referrals
    Referral referral;      
  }

  vendor_2_name : {  
    // only vendor 2 supports delivering and pick up from home
    Address homeDeliveryAddress;        
  }
..    
}

Il vantaggio dell'utilizzo di un singolo modello comune (Approach 1) è che è più pulito, ma con il passare del tempo con l'aggiunta di più fornitori con funzionalità molto specifiche, il modello si riempie di troppi campi non raggruppati. Capisco che possiamo organizzare meglio il modello con una gerarchia anche con molti campi.

Il vantaggio dell'utilizzo di classi di modelli compositi separati (Approach 2) per diversi fornitori è che ci aiuterà a raggruppare meglio i campi e consentirà di isolare le modifiche richieste nell'interpretazione dei dati. Ma il lato negativo è che espone i campi specifici del venditore.

Per favore, fammi sapere se la direzione in cui sto andando è quella giusta o se ci sono altri / molti altri approcci che dovrei considerare che potrebbero aiutarti ad aggiungere più funzionalità all'API. Fammi sapere se hai bisogno di ulteriori dettagli / informazioni relative alla domanda.

PS: il modello di esempio (CarReservation) che ho menzionato nell'esempio è qualcosa a cui non ho pensato troppo. In realtà sto modellando in un dominio diverso, dettagli di cui non posso rivelare. :)

    
posta pavan kumar chaitanya 22.07.2017 - 01:32
fonte

2 risposte

1

Possiamo invertire la composizione.

In questo momento, come hai commentato, la nostra attività è fornire un'interfaccia unificata. Per brevità, chiamiamo la nostra azienda CarReservation .

Il nostro CarReservation è il concetto di crosscutting tra le terze parti API. Quindi, invece di collegare il nostro modello al modello, accoppiamo il modello al nostro. 1

Al momento, CarReservation è un hub di caratteristiche casuali che, nella maggior parte dei casi, rimarranno vuoti un numero elevato di esse. Ci condurrà a verificare continuamente queste funzionalità per determinare se sono state implementate o meno ( obj.getReferral()!=null ) e per cambiare il modello 1 ogni volta che un è stata adottata una nuova funzionalità. Nelle prime fasi dello sviluppo, potrebbe sembrare irrilevante, ma una volta in produzione, cambiare il CarReservation potrebbe portarci a prendere decisioni difficili. Ad esempio, come tenerlo retrocompatibile. Questo rischio è presente indipendentemente da ciò che cambiamo. Tuttavia, possiamo limitare l'impatto delle modifiche.

Invertendo la composizione, otteniamo qualcosa di simile

class Referral{
   ...
   private CarReservation reservation;
} 

Quindi, ogni volta che viene adottata una nuova funzione, non è necessario modificare CarReservation .

class Delivery{
   private DeliveryAddress address;
   private CarReservation reservation;
 }

Sia Delivery 2 che Referral 2 possono contenere attributi specifici del venditore. Due venditori potrebbero capire Referral in modi diversi. Quindi se Referral differisce da un fornitore all'altro, possiamo implementare le differenze all'interno di uno specifico modello 1 . Più o meno, ogni funzionalità potrebbe essere un contesto limitato, con la sua complessità. Ci vorrà un esercizio di astrazione ma il nostro core ( CarReservation ) rimarrà lo stesso.

Successivamente, possiamo associare le funzioni correlate con CarReservation all'interno di un DTO in cui CarReservation e Referral si trovano allo stesso livello, invece di nidificare.

Ovviamente, non ci impedisce di mantenere il codice. Più funzioni vogliamo adottare, più componenti otteniamo. Ma quanti di loro adottiamo fa parte della nostra strategia. Probabilmente, non adotteremo ogni singola funzionalità da ogni singolo fornitore, adotteremo quelli che aggiungono valore alla nostra attività.

Come al solito, un approccio più dinamico è possibile. Ad esempio, implementando la riflessione (se il nostro linguaggio supporta la riflessione), ma considero questo approccio molto più astratto e difficile da gestire.

Un altro approccio possibile sta implementando EAV che rende modello molto estendibile, ma difficile da interrogare. Tuttavia, vale la pena menzionarlo.

In ogni caso, il nostro sistema funziona come facciata. Dovrebbe nascondere i dettagli dell'API di terze parti per il bene della facciata. Quindi, se dobbiamo esporre questi dettagli, dovremmo farlo nei nostri termini e condizioni, altrimenti le API esterne vincoleranno -eventualmente- il modo in cui il nostro sistema si evolve.

1: Qui model , dominio o business sono intercambiabili. Per semplicità, ho scelto model

2: Non presti troppa attenzione ai nomi. Non ci ho pensato troppo.

    
risposta data 22.07.2017 - 18:29
fonte
0

Se segui l'approccio n. 1, puoi aggiungere nuovi fornitori senza che i client debbano aggiornare. Se gestisci internamente la mappatura da campi comuni a campi specifici del fornitore, puoi facilmente modificare tali mappature internamente senza modificare l'interfaccia per la tua API.

Se segui l'approccio n. 2, non ottieni gli stessi vantaggi di cui sopra. Ogni nuovo venditore ti farà continuare a sbattere la tua versione dell'API e i tuoi clienti dovranno tenere il passo. Inoltre, se un fornitore modifica drasticamente la propria API, ciò potrebbe causare modifiche simili all'API, il che è un problema da affrontare e un segno di design non molto buono.

Personalmente ritengo che un'API sia un livello di astrazione che dovrebbe rendere la vita semplice e facile. Se stai solo creando un modo indiretto per accedere a un'altra API, qual è il punto?

    
risposta data 22.07.2017 - 01:54
fonte

Leggi altre domande sui tag