Cercare di progettare un'API per applicazioni esterne con previsione per il cambiamento non è facile, ma un po 'di riflessione può rendere la vita più facile in seguito. Sto provando a stabilire uno schema che supporterà le modifiche future rimanendo compatibile con le versioni precedenti lasciando in sospeso i gestori delle versioni precedenti.
La preoccupazione principale di questo articolo riguarda il modello da seguire per tutti gli endpoint definiti per un determinato prodotto / azienda.
Schema base
Dato un modello di URL di base di https://rest.product.com/
ho ideato che tutti i servizi risiedono sotto /api
insieme a /auth
e altri endpoint non rimanenti come /doc
. Pertanto posso stabilire gli endpoint di base come segue:
https://rest.product.com/api/...
https://rest.product.com/auth/login
https://rest.product.com/auth/logout
https://rest.product.com/doc/...
Endpoint del servizio
Ora per gli endpoint stessi. La preoccupazione per POST
, GET
, DELETE
non è l'obiettivo principale di questo articolo ed è la preoccupazione per quelle azioni stesse.
Gli endpoint possono essere suddivisi in spazi dei nomi e azioni. Ogni azione deve anche presentarsi in modo tale da supportare cambiamenti fondamentali nel tipo di ritorno o nei parametri richiesti.
Prendendo un ipotetico servizio di chat in cui gli utenti registrati possono inviare messaggi potremmo avere i seguenti endpoint:
https://rest.product.com/api/messages/list/{user}
https://rest.product.com/api/messages/send
Ora per aggiungere il supporto alla versione per le future modifiche API che potrebbero essere in fase di interruzione. Potremmo aggiungere la firma della versione dopo /api/
o dopo /messages/
. Dato l'endpoint send
, potremmo avere il seguente per v1.
https://rest.product.com/api/v1/messages/send
https://rest.product.com/api/messages/v1/send
Quindi la mia prima domanda è, qual è il posto consigliato per l'identificativo della versione?
Gestione del codice controller
Quindi ora abbiamo stabilito che abbiamo bisogno di supportare versioni precedenti di cui abbiamo bisogno per gestire in qualche modo il codice per ognuna delle nuove versioni che potrebbero deprecare nel tempo. Supponendo che stiamo scrivendo endpoint in Java, potremmo gestirlo attraverso i pacchetti.
package com.product.messages.v1;
public interface MessageController {
void send();
Message[] list();
}
Questo ha il vantaggio che tutto il codice è stato separato attraverso spazi dei nomi in cui qualsiasi cambiamento di rottura significherebbe una nuova copia degli endpoint del servizio. Il danno è che tutto il codice deve essere copiato e le correzioni dei bug che si desidera applicare a nuove e versioni precedenti devono essere applicate / testate per ogni copia.
Un altro approccio consiste nel creare gestori per ciascun endpoint.
package com.product.messages;
public class MessageServiceImpl {
public void send(String version) {
getMessageSender(version).send();
}
// Assume we have a List of senders in order of newest to oldest.
private MessageSender getMessageSender(String version) {
for (MessageSender s : senders) {
if (s.supportsVersion(version)) {
return s;
}
}
}
}
Questo ora isola il controllo delle versioni su ciascun endpoint stesso e rende le correzioni dei bachi sulla porta compatibile nella maggior parte dei casi che devono essere applicate una sola volta, ma ciò significa che dobbiamo fare un po 'più di lavoro su ogni singolo endpoint per supportare questo .
Quindi c'è la mia seconda domanda "Qual è il modo migliore per progettare il codice di servizio REST per supportare versioni precedenti."