Usine de clients REST

Le problème exposé dans ce sujet a été résolu.

Bonjour tout le monde !  :)

Aujourd’hui, je créé ce sujet pour qu’on m’aide à raisonner sur un cas concret d’utilisation du patron de développement factory appliqué à des services Symfony 4.

Je planche sur une application qui devra se connecter à diverses sources pour récupérer des données. Ces sources sont hétéroclites, je ne pourrai pas utiliser un même client pour toutes, il me faudra des spécialisations — et à terme, je ne suis même pas sûr que les sources soient toutes des APIs REST, on pourrait en venir à devoir traiter du HTML, mais justement, je pense que si c’est bien réfléchi au départ, ce ne sera pas un souci.

Je vais récupérer des informations qui entrent dans ma base de données, donc je sais assez précisément ce dont j’ai besoin de récupérer comme informations sur ces sources, et en conséquence, j’ai un nombre défini de méthodes. Les choses qui vont donc varier, ce sont

  • l’URL de la source ;
  • la manière de traiter les informations pour les faire correspondre au format de mon application.

Je pensais d’abord créer une classe client par source, et leur passer des objets spécialisés pour effectuer le mapping des informations récupérées (je vais appeler ces objets des mappers).
Mon souci est en fait au niveau technique : si ces mappers vivent dans des espaces de noms propres à leur source, comment les lier à mon client ? Je pourrais évidemment les instancier directement dans la classe spécialisée, mais j’ai l’impression que c’est trop fortement lié comme architecture — ce qui n’empêcherait pas mon impression d’être fausse.  ^^

Au final, je me demande comment lier mes éléments qui sont :

  • mon URL vers la source (qui pourrait être directement encapsulé dans un client GuzzleHttp mais du coup il me faudrait une fabrique pour en construire depuis mes URLs de base) ;
  • mes mappers, dont leur nombre est défini au moment de l’utilisation, mais qui peut évidemment varier en fonction des évolutions.

J’ai déjà de la peine à expliquer tout ça par écrit, et même si ça m’a aidé à défricher, je pense que je dois manquer quelque chose…

Une bonne âme pourrait-elle m’aider à y voir plus clair ?

Merci d’avance  :)

+0 -0

Salut !

Quel est l’avantage d’avoir une classe générique et une sous-classe par "source" ? Qu’est-ce que ça t’apporte, qu’est-ce que tu y gagnes ?

(Si t’as de la peine à répondre à ces questions, compare à une autre approche : une bête classe par source, pas d’héritage ou de pattern truc. T’y injectes l’API qui permet de persister les données.)

+0 -0

Disons que je voulais surtout qu’il soit facile et rapide d’ajouter une source ou de corriger une correspondance — ce qui risque d’arriver bien plus souvent que le fait de devoir ajouter une entité complète, donc une nouvelle structure de données à traiter.
Quant à cette histoire de mappers, je voulais surtout avoir les informations de correspondance dans des fichiers séparés, dans le même genre des mappings XML plutôt qu’avec les annotations. Avoir dans un même fichier la définition de la source et comment uniformiser les différents types de données, ça me paraît un peu lourd.

Donc en fait, je réfléchissais pour tenter d’avoir ce que je tiendrais pour une architecture modulaire. Je me disais que, avec l’"autowiring", je pouvais jouer sur l’injection automatique des dépendances, mais en fait, plus j’y pense, moins ça me paraît compatible avec ce que je souhaite faire.

Et si j’ai pensé à tout ça, c’est parce que j’ai déjà créé des classes spécialisées pour deux sources sur quatre, et à part les structures de données à "traduire" et les URLs des sources, j’ai l’impression qu’il y a vraiment beaucoup de code dupliqué, ce que j’aimerais pouvoir éviter — j’ai eu des mauvaises expériences avec du code multiplié, et on oublie de modifier partout…

Après, je ne suis pas sûr d’avoir bien expliqué : j’imaginais une classe "conteneur générique" avec une interface définie, qui utiliserait les composants propres à la source traitée, me permettant en un appel de méthode de taper dans la source et de récupérer un objet prêt à être validé et, le cas échéant, persisté. Donc pas vraiment d’héritage, même si très probablement qu’un interface sera bienvenue pour s’assurer d’avoir les méthodes nécessaires. Mais du coup, pourquoi ne pas avoir quelque chose d’à la fois plus concret et générique ?

Voilà voilà  ^^

+0 -0

Je sais pas, je suis pas fan d’OO. Je sais pas trop ce que t’avais comme code dupliqué. Une chose intéressante ce serait aussi de savoir comment tu comptes lancer ces job ?

Sans trop comprendre les problèmes que tu vois avec l’approche ’naïve’, je ferais vraiment une classe par "type" de job et j’injecterais des trucs. Au final, le truc où tu dis quels choses traiter, ça ferait en gros :

1
2
3
4
5
jobs = [
  new JSONScraper('url1', db1),
  new JSONScraper('url2', db1),
  new RESTScraper('url3', db1),
]

db1 serait un objet d’une classe qui permet de valider/persister.

+0 -0

En fait, c’est l’architecture sur laquelle je suis parti (avec un namespace par source) qui me fiche dedans aussi. Et là, je me retrouve avec des classes de deux namespaces (et même certaines dans le même dossier) qui ont le même code, les URLs des sources étant les seules différences… L’endpoint de l’API représentant le namespace racine, chaque objet y possède sa classe de récupération, juste pour varier sur l’URN…

Bref, il faut que j’y planche correctement, actuellement, ça ne me plaît pas.

Pour être plus précis, maintenant que je prends un peu de recul (je ne vais pas faire remonter le sujet pour ça), j’avais :

  1. une classe Client qui encapsule le client GuzzleHttp, en proposant des méthodes pour chaque verbe ;
  2. une autre classe qui hérite de mon Client, spécifique à une source, en cela qu’elle spécifie l’URL de base de l’API. C’est le service que j’appelle à la base.
  3. d’autres clients générés par celui au point 2. Ces autres clients, en plus d’être spécialisés pour la source, héritent de celui au point 1, et sont spécialisés pour une ressource de la source… et tout ce qu’ils font, c’est redéfinir les mêmes méthodes afin qu’on n’ait pas à donner les URLs exacts…
+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte