Pourquoi est-ce une si mauvaise idée de partager une interface entre le serveur et le client?

12

Je lisais la documentation de Spring Cloud Netflix lorsque j'ai découvert un moyen de partager une interface entre un serveur HTTP et son client. Ils utilisent cet exemple pour les microservices, bien qu'il n'y ait aucune raison pour qu'il ne puisse pas s'étendre à la communication HTTP générique:

// 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 {
}

Cela définit une interface qui est utilisée à la fois comme serveur (The Spring le @RestControllertransforme en serveur HTTP) et comme client (The Feign la @FeignClientconfigure pour une utilisation client HTTP). Les implémentations de classe serveur et client peuvent être utilisées dans des projets distincts mais utilisent toujours la même interface pour garantir la correspondance des types.

Cependant, sous l'exemple, ils ont mis la mise en garde suivante:

Remarque: Il n'est généralement pas conseillé de partager une interface entre un serveur et un client. Il introduit un couplage étroit et ne fonctionne pas réellement avec Spring MVC dans sa forme actuelle (le mappage des paramètres de méthode n'est pas hérité).

OK, donc ce n'est pas bien intégré en ce moment ... mais cette partie vient après l'avertissement contre le partage de code et l'introduction du couplage entre le serveur et le client, ce qu'ils pensent être plus important. Pourquoi pensent-ils que c'est une si mauvaise idée de partager une interface de cette façon?

Sans cela, vous perdez la possibilité de garantir que le serveur et le client s'envoient mutuellement des données qu'ils peuvent tous deux comprendre. Vous pouvez ajouter un champ à l'un mais pas à l'autre et découvrir uniquement la non-concordance jusqu'à l'exécution. À mon avis, il ne s'agit pas d' introduire un couplage, mais simplement de révéler un couplage qui existe déjà. La nécessité de rendre les serveurs complètement indépendants est-elle supérieure à la nécessité de leur faire savoir quels types de données ils recevront?

Ben S
la source
1
Le couplage qui existe, en termes de données / formatage géré par les clients / serveurs, est déterminé par le protocole - une documentation qui peut être utilisée comme convention . Le couplage introduit par le partage d'une interface est un couplage au moment de la compilation - considérez ce qui se passe lorsque l'interface est modifiée (d'une manière incompatible en amont, par exemple), mais le code client / serveur utilisant cette interface est déployé à des moments différents. Ce couplage temps de déploiement peut être plus difficile à gérer, en particulier à l'échelle de Netflix.
Castaglia
1
Je suis à peu près sûr que je ne fonctionne pas à l'échelle de Netflix :) mais si les interfaces sont modifiées d'une manière incompatible en amont, cela ne fait-il pas simplement passer l'erreur d'être trouvée au moment de la compilation à la trouver au moment de l'exécution à la place? Estiment-ils acceptable de laisser quelques appels de fonction échouer alors qu'ils mettent lentement à niveau tous les serveurs?
Ben S
1
Peut-être; dépend du code client. Considérez également l'autre cas: les serveurs sont mis à niveau en premier, et les clients doivent maintenant faire face (de manière inattendue) aux appels défaillants ...
Castaglia
1
Juste curieux, en partageant cette interface, cela limite-t-il les langues / pile à partir desquelles vous pouvez construire le client?
JeffO
Oui - c'est un fichier Java, vous devrez donc utiliser Java. Vous pourrez peut- être utiliser un autre langage JVM mais je ne l'ai pas essayé.
Ben S

Réponses:

6

La raison, comme indiqué dans les commentaires, est qu'il en résulte un couplage étroit de votre plate-forme client à votre plate-forme serveur. Ici, cela signifie que votre client doit utiliser la langue / la plate-forme que vous utilisez sur le serveur afin de comprendre le contrat attendu de votre serveur. Notez qu'il y a une différence entre partager le même code (un artefact d'une langue / plate-forme spécifique) et convenir d'un contrat spécifique.

De nombreux projets utilisent à la place de la documentation pour leurs contrats. Exemples de demandes et de réponses dans un format neutre (par exemple JSON) sur des protocoles standard (par exemple REST). (Voir la documentation de l'API Stripe , par exemple). Parce qu'il n'est pas pratique d'écrire un contrat basé sur du code pour chaque plateforme client possible que vous voudrez peut-être utiliser ou autoriser. D'autres encore utilisent des outils de gestion d'API pour définir des contrats neutres .

Votre exemple d'ajout d'un champ est une préoccupation distincte - un exemple de l'importance de la version des contrats d'API. Laissez les clients utiliser la version pour laquelle ils sont conçus. Une nouvelle version d'API incompatible en amont existe aux côtés de l'ancienne. Le client de l'ancienne version continue de fonctionner jusqu'à ce que son équipe se mette à le mettre à jour ou jusqu'à ce que vous retiriez l'ancienne version (après une période de dépréciation / migration). Voir Changement parallèle .

Suivre l'avertissement (conseils implicites dans) aide le client et le serveur à évoluer d'une manière et d'un rythme qui ont du sens pour chacun. Si vous pouvez raisonnablement garantir que votre serveur et votre client partageront toujours la même langue / plate-forme ET évolueront au même rythme, alors utiliser un artefact de code spécifique à la langue et à la plate-forme que votre contrat sera probablement correct. Cependant, ce n'est probablement pas une attente raisonnable, en particulier pour les projets ciblant Netflix OSS (quelque chose spécifiquement conçu pour l'évolutivité et les performances du cloud, avec toute cette complexité requise).

Kasey Speakman
la source
2
Le client est-il vraiment censé utiliser l'interface? J'ai toujours vu de telles constructions comme un moyen de faciliter l'écriture d'un client. Après tout, vous pouvez toujours écrire un client REST dans une langue différente.
Jimmy T.
1
Exactement, l'api existera toujours, avec ses définitions de routes, rien n'empêche la création d'un client dans une autre langue, mais tant que vous utilisez java pourrait utiliser l'interface
Leonardo Villela