Perché è una cattiva idea condividere un'interfaccia tra server e client?


12

Stavo leggendo la documentazione di Spring Cloud Netflix quando ho scoperto un modo per condividere un'interfaccia tra un server HTTP e il suo client. Usano questo esempio per i microservizi, anche se non c'è motivo per cui non possa estendersi alla comunicazione HTTP generica:

// The shared interface, in a common library
public interface UserService {
    @RequestMapping(method = GET, value = "/users/{id}")
    User getUser(@PathVariable long id);
}

// The controller, on the server
@RestController
public class UserResource implements UserService {
}

// The same interface used for the client
@FeignClient("users")
public interface UserClient extends UserService {
}

Questo definisce un'interfaccia che viene utilizzata sia come server (The Spring lo @RestControllertrasforma in un server HTTP) sia come client (The Feign @FeignClientlo configura per l'uso del client HTTP). Le implementazioni della classe client e server possono essere utilizzate in progetti separati ma utilizzano comunque la stessa interfaccia per garantire che i tipi corrispondano.

Tuttavia, sotto l'esempio hanno messo il seguente avvertimento:

Nota: in genere non è consigliabile condividere un'interfaccia tra un server e un client. Presenta un accoppiamento stretto e in realtà non funziona con Spring MVC nella sua forma attuale (la mappatura dei parametri del metodo non viene ereditata).

OK, quindi non è ben integrato in questo momento ... ma quella parte viene dopo l'avvertimento contro la condivisione del codice e l'introduzione dell'accoppiamento tra il server e il client, che ritengono sia più importante. Perché pensano che sia una cattiva idea condividere un'interfaccia in questo modo?

Senza di essa, si perde la capacità di garantire che il server e il client si scambino reciprocamente dati comprensibili a entrambi. È possibile aggiungere un campo a uno ma non all'altro e scoprire solo la mancata corrispondenza fino al runtime. A mio avviso, non si tratta di introdurre un accoppiamento, ma semplicemente di rivelare un accoppiamento già esistente. La necessità di rendere i server completamente indipendenti è maggiore della necessità di far loro sapere quali tipi di dati riceveranno?


1
L'accoppiamento esistente, in termini di dati / formattazione gestiti da client / server, è determinato dal protocollo , un documento che può essere utilizzato come convenzione . L'accoppiamento introdotto dalla condivisione di un'interfaccia è un accoppiamento in fase di compilazione: considerare cosa succede quando l'interfaccia viene modificata (ad esempio in modo incompatibile con le versioni precedenti), ma il codice client / server che utilizza quell'interfaccia viene distribuito in momenti diversi. Tale accoppiamento al tempo di implementazione può essere più difficile da gestire, specialmente su scala di Netflix.
Castaglia,

1
Sono abbastanza sicuro di non operare su scala di Netflix :) ma se le interfacce sono cambiate in modo incompatibile con le versioni precedenti , allora questo non sposta semplicemente l'errore dal trovarsi al momento della compilazione al trovarsi al momento dell'esecuzione? Considerano OK lasciare che alcune chiamate di funzioni falliscano mentre aggiornano lentamente tutti i server?
Ben S,

1
Possibilmente; dipende dal codice client. Considera anche l'altro caso: i server vengono prima aggiornati e ora i client devono gestire (inaspettatamente) chiamate non
riuscite

1
Solo curioso, condividendo questa interfaccia, limita da quali lingue / stack puoi costruire il client?
JeffO,

Sì, è un file Java, quindi dovrai usare Java. Si può essere in grado di utilizzare un altro linguaggio JVM, ma io non l'ho provato.
Ben S,

Risposte:


6

Il motivo, come indicato nei commenti, è che si traduce in un accoppiamento stretto tra la piattaforma client e la piattaforma server. In questo caso, ciò significa che al cliente è richiesto di utilizzare la lingua / piattaforma che si sta utilizzando sul server per comprendere il contratto previsto del server. Si noti che esiste una differenza tra la condivisione dello stesso codice (un artefatto di una lingua / piattaforma specifica) e l'accordo su un contratto specifico.

Molti progetti utilizzano invece la documentazione per i loro contratti. Richieste e risposte di esempio in un formato neutro (ad es. JSON) su protocolli standard (ad es. REST). (Vedi Documenti API Stripe , per esempio). Perché non è pratico scrivere un contratto basato su codice per ogni possibile piattaforma client che potresti voler utilizzare o consentire. Altri ancora usano strumenti di gestione API per definire contratti neutrali .

Il tuo esempio di aggiunta di un campo è una preoccupazione separata, un esempio del perché è importante per i contratti API versione. Consentire ai client di utilizzare la versione per cui sono progettati. Una nuova versione API incompatibile con le versioni precedenti esiste accanto alla vecchia. Il client per la versione precedente continua a funzionare fino a quando il suo team non riesce ad aggiornarlo o fino a quando non si ritira la versione precedente (dopo un periodo di deprecazione / migrazione). Vedi Modifica parallela .

Seguire l'avvertimento (avviso implicito nel) aiuta il client e il server a evolversi in modi e ritmi che hanno senso per ciascuno. Se puoi ragionevolmente garantire che il tuo server e client condivideranno sempre la stessa lingua / piattaforma E si evolveranno allo stesso ritmo, allora usando un artefatto di codice specifico per la lingua e la piattaforma, il tuo contratto probabilmente andrà bene. Tuttavia, questa probabilmente non è un'aspettativa ragionevole, soprattutto per i progetti destinati a Netflix OSS (qualcosa specificamente orientato alla scalabilità e alle prestazioni del cloud, con tutta la complessità necessaria).


2
Ci si aspetta davvero che il client usi l'interfaccia? Ho sempre visto tali costrutti come un modo per facilitare la scrittura di un cliente. Dopo tutto, puoi ancora scrivere un client REST in una lingua diversa.
Jimmy T.,

1
Esatto, ancora esisterà api, con le sue definizioni di route, nulla impedisce la creazione di un client in un'altra lingua, ma finché usi java potresti usare l'interfaccia
Leonardo Villela
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.