API di versioning

9

Supponiamo che tu abbia un grande progetto supportato da una base API. Il progetto include anche un'API pubblica che può essere utilizzata dagli utenti finali (ish).

A volte è necessario apportare modifiche alla base dell'API che supporta il tuo progetto. Ad esempio, devi aggiungere una funzione che richiede una modifica dell'API, un nuovo metodo o richiede la modifica di uno degli oggetti o il formato di uno di questi oggetti, passati ao dall'API.

Supponendo che tu stia utilizzando questi oggetti anche nella tua API pubblica, anche gli oggetti pubblici cambieranno ogni volta che lo fai, il che è indesiderabile dato che i tuoi client potrebbero fare affidamento sugli oggetti API che restano identici per il loro codice di analisi. (tossire client C ++ WSDL ...)

Quindi una soluzione potenziale è la versione dell'API. Ma quando diciamo "versione" dell'API, sembra che questo debba significare anche la versione degli oggetti API oltre a fornire chiamate di metodo duplicate per ogni firma di metodo modificata. Quindi avrei quindi un semplice oggetto clr vecchio per ogni versione della mia api, che di nuovo sembra indesiderabile. E anche se lo faccio, sicuramente non costruirò ogni oggetto da zero in quanto finirebbe con grandi quantità di codice duplicato. Piuttosto, è probabile che l'API estenda gli oggetti privati che stiamo usando per la nostra API di base, ma poi ci imbattiamo nello stesso problema perché le proprietà aggiunte sarebbero anche disponibili nell'API pubblica quando non dovrebbero esserlo.

Quindi, qual è il buon senso che viene solitamente applicato a questa situazione? So che molti servizi pubblici come Git per Windows mantengono un'API con versione, ma ho difficoltà a immaginare un'architettura che supporti questo senza grandi quantità di codice duplicato che copre i vari metodi di versione e oggetti di input / output.

Sono a conoscenza del fatto che processi come la versione semantica tentano di mettere un po 'di equilibrio su quando interruzioni API pubbliche dovrebbero verificarsi. Il problema è che sembra che molte o molte modifiche richiedano la rottura dell'API pubblica se gli oggetti non sono più separati, ma non vedo un buon modo per farlo senza duplicare il codice.

    
posta Case 10.10.2012 - 20:59
fonte

1 risposta

6

Se si mantiene un'API utilizzata da terze parti, è inevitabile che sia necessario apportare modifiche. Il livello di complessità dipenderà dal tipo di cambiamento che si sta verificando. Questi sono gli scenari principali che emergono:

  1. Nuova funzionalità aggiunta all'API esistente
  2. Funzionalità obsoleta deprecata dall'API
  3. Funzionalità esistente nell'API che cambia in qualche modo

Nuova funzionalità aggiunta all'API esistente

Questo è lo scenario più semplice da supportare. L'aggiunta di nuovi metodi a un'API non dovrebbe richiedere alcuna modifica ai client esistenti. Questo è sicuro da implementare per i client che hanno bisogno della nuova funzionalità in quanto non ha aggiornamenti per qualsiasi client esistente.

Vecchia funzionalità deprecata dall'API

In questo scenario è necessario comunicare agli utenti esistenti della propria API che la funzionalità non sarà supportata a lungo termine. Finché non si rilascia il supporto per la vecchia funzionalità (o fino a quando tutti i client non sono passati alla nuova funzionalità) è necessario mantenere contemporaneamente la vecchia e la nuova funzionalità dell'API. Se si tratta di una libreria fornita dalla maggior parte delle lingue, è possibile contrassegnare i vecchi metodi obsoleti / deprecati. Se si tratta di un servizio di terze parti di solito è meglio avere endpoint diversi per la vecchia / nuova funzionalità.

Funzionalità esistente nell'API che cambia in qualche modo

Questo scenario dipenderà dal tipo di cambiamento. Se non è più necessario utilizzare i parametri di input, è sufficiente aggiornare il servizio / la libreria per ignorare i dati ora aggiuntivi. In una libreria, il metodo sovraccaricato chiama internamente il nuovo metodo che richiede un numero inferiore di parametri. In un servizio ospitato l'endpoint ignora i dati aggiuntivi e può servire entrambi i tipi di client ed eseguire la stessa logica.

Se la funzionalità esistente deve aggiungere nuovi elementi richiesti, è necessario disporre di due endpoint / metodi per il servizio / la libreria. Fino all'aggiornamento dei client devi supportare entrambe le versioni.

Altri pensieri

Rather, the API is likely to extend the private objects we are using for our base API, but then we run into the same problem because added properties would also be available in the public API when they are not supposed to be.

Non esporre oggetti privati interni attraverso la tua libreria / servizio. Crea i tuoi tipi e mappa l'implementazione interna. Ciò consentirà di apportare modifiche interne e ridurre al minimo la quantità di aggiornamenti che i client esterni devono eseguire.

The problem is more that it seems like many or most changes require breaking the public API if the objects aren't more separated, but I don't see a good way to do that without duplicating code.

L'API se si tratta di un servizio o di una libreria deve essere stabile nel punto di integrazione con i client. Più tempo impieghi per identificare quali input e output dovrebbero essere e tenerli come entità separate ti farà risparmiare un sacco di grattacapi lungo la strada. Rendi il contratto API una propria entità separata e mappalo alle classi che forniscono il lavoro reale. Il tempo risparmiato quando le implementazioni interne cambiano dovrebbe più che compensare i tempi supplementari necessari per definire l'interfaccia aggiuntiva.

Non visualizzare questo passaggio come "codice di duplicazione". Mentre simili, sono entità separate che valgono la pena creare. Mentre le modifiche alle API esterne richiedono quasi sempre una modifica corrispondente all'implementazione interna, le modifiche di implementazione interne non dovrebbero sempre modificare l'API esterna.

Esempio

Supponi di fornire una soluzione di elaborazione dei pagamenti. Stai utilizzando PaymentProviderA per effettuare transazioni con carta di credito. In seguito ottieni una tariffa migliore tramite il processore di pagamento di PaymentProviderB. Se la tua API ha esposto i campi Carta di credito / Indirizzo di fatturazione del tuo tipo invece della rappresentazione di PaymentProviderA, la modifica dell'API è 0 poiché l'interfaccia è rimasta invariata (si spera comunque, se PaymentProviderB richiede dati che non sono richiesti da PaymentProviderA allora devi scegliere supportare entrambi o mantenere la tariffa peggiore con PaymentProviderA).

    
risposta data 11.10.2012 - 21:24
fonte

Leggi altre domande sui tag