API RESTful et Booléen de comportement

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

Bonjour,

Imaginons que vous travailliez sur un système de contrats avec un montant (€) pouvant subir une opération, devenant un autre montant. Le dev front vous demande à ce que chaque contrat affiche et le montant originel, et le montant après exécution de ladite opération - sans que ce dernier soit enregistré en base. Précisions qu’à un moment donné, dans l’application, ce montant sera cependant bien enregistré en base.

Vous créez donc un contrôleur, CalculerNouveauMontant, permettant d’enregistrer en base ce montant sur tel ou tel contrat (fourni en entrée), mais de plus : vous prenez soin d’avoir recours à un booléen "simulation" (fourni en entrée). S’il vaut vrai, aucun enregistrement en base n’a lieu (et inversement s’il vaut faux). Ce contrat et ce booléen sont bien sûr envoyés par le front.

Dans la ressource HTTP "ContratResource", c’est-à-dire dans l’"objet" JSON "contrat" que vous retournez au front, vous définissez bien sûr les deux champs "montant" et "montant_apres_operation" : le premier est directement valué grâce à l’ORM. Mais "montant_apres_operation", vous l’avez écrit en tant que champ calculé : c’est vous qui l’avez défini de sorte que vous appelez votre contrôleur en passant en entrée le booléen "simulation" à vrai (et vous fournissez en entrée le contrat actuel - rappel : vous êtes dans la ressource ContratResource, donc vous y avez bien sûr accès via $this).

Question

Cela va-t-il à l’encontre des principes RESTful ? On pourrait penser qu’un contrôleur = 1 seule responsabilité et que vous violez ce principe. (Bien que d’après moi, le principe est ici de calculer un nouveau montant - seul l’enregistrement ou non en base est le point de divergence).

Question + ouverte encore : Est-ce une mauvaise pratique dans le cadre d’un développement professionnel dans une startup web ?

Alternatives

Je suggérais de mettre en place une classe parente abstraite réalisant le calcul du nouveau montant, et de définir deux contrôleurs (un pour la simulation, un autre pour l’enregistrement). Tous deux en héritant.

On m’a refusé ça pour mettre à la place :

  • un contrôleur réalisant la simulation

  • un contrôleur mettant à jour tout contrat, au sens large du terme : on pourrait y passer n’importe quel couple (clé ; valeur), même en-dehors de ce calcul de nouveau montant. On pourrait alors l’utiliser pour mettre à jour le nom du contrat par exemple. (Alors que cela a lieu au sein de certaines "grandes" fonctionnalités du projet, comme le fait de pouvoir mettre à jour un contrat via une modale en front : un contrôleur est dédié à ça. Dès lors, le fait de créer ce nouveau contrôleur de mise à jour de tout contrat "au sens large du terme" me paraît être une toute nouvelle approche de développement, et un peu bizarre car pas liée à une fonctionnalité en particulier).

Qu’en pensez-vous ? Quelle est la meilleure solution entre ces 2 voire d’autres ?

Merci et bonne journée.

Très honnêtement : je ne sais pas. Ta présentation est tellement alambiquée et mélange tellement de concepts (tu vas de la conception d’API aux spécificités du langage – tu parles de $this – en passant par des considérations sur des détails d’implémentation qu’on a pas) que je n’arrive pas à comprendre quel est le problème que tu essaies de résoudre.

Peux-tu reformuler en étant synthétique et sans partir dans des détails superflus ? Par exemple, si ton problème est un problème de conception d’API REST, on a pas besoin de savoir comment elle va être implémentée derrière (c’est un autre problème) ; si c’est un problème d’implémentation d’API REST imposée, on a juste besoin de connaitre son contrat d’interface, pas moult détails à son sujet ; etc.

Question + ouverte encore : Est-ce une mauvaise pratique dans le cadre d’un développement professionnel dans une startup web ?

Dans la vraie vie, que ce soit dans une startup Web ou une entreprise du CAC40, on veut dans l’idéal appliquer le meilleur compromis possible entre :

  • la difficulté d’implémentation ;
  • à quel point le code reste évolutif (en prenant en compte les limites de la compétence disponible et le nombre de bras disponibles et à venir) ;
  • la performance, quand c’est un problème ;
  • sécurité, quand c’est un problème ;
  • à quel point ça se fond bien dans l’existant.

Si une solution qui se conforme parfaitement aux préceptes de la thèse de Roy Fielding ne présente pas un compromis qui me semble acceptable, alors je préfère autant l’écarter au profit d’une solution moins pure et moins académique, mais qui présentera ses axes de bénéfice dans notre organisation — car oui, c’est aussi l’organisation et sa structure qui induit les lignes directrices de la solution retenue. Le même cahier des charges bien implémenté dans deux entreprise différentes ne donne pas nécessairement la même architecture à la fin.

Je te donne un exemple concret. Il y a 7–8 ans je travaillais sur une base de code classique PHP+MySQL. De nouvelles fonctionnalités nécessitaient d’avoir une queue pour traiter des events de façon asynchrone. La solution orthodoxe aurait été de mettre en place un outil spécialisé, ça aurait pu être RabbitMQ par exemple. Notre solution retenue, c’était plutôt de faire une table dans MySQL avec les events dedans, et un endpoint pour piocher dedans. Pour quel bénéfice ? Juste maintenir notre stack petite sans rajouter une brique, c’était important car nous déployions cette solution on-premise chez les clients, donc une brique en plus à déployer c’est un risque en plus, c’est de la complexité en plus sur des machines dont on a pas le contrôle à 100%, et c’est des coûts en plus. Est-ce que notre système était aussi robuste qu’un vrai système de queuing ? Non, absolument pas. Et ce n’était pas grave en vertu des règles métier qu’on voulait. On savait ce qu’on pouvait sacrifier volontiers.

Cette solution est le résultat d’une réflexion qui prend en compte des détails ultra-spécifiques à l’organisation : ses règles métier, son fonctionnement, le mode de distribution de son logiciel, et le faible effectif de ses employés qui doivent maximiser leur temps à travailler sur des choses plus importantes que de passer des heures à déployer des stacks de plus de deux-trois briques.

Si la direction technique de ton entreprise ne peut pas comprendra cela et impose de de suivre toujours la voie académique, c’est fort dommage.

Comme on dit parfois : il n’y a pas les bonnes solutions techniques, mais il y a les bons compromis.

Je retiens une seule chose importante pour moi de ton explication : le calcul de la simulation et le calcul "vrai" doivent être fait par le même code. Sinon, tu risques de ne pas enregistrer la même chose que ce que tu as montré en simulation lors d’une évolution mal coordonnée du code. Le reste, c’est un peu du détail à côté.

D’ailleurs, les choix exacts peuvent dépendre de tes outils et de la tête globale de l’appli. En fonction de comment sont faits ton framework, ton ORM, etc., le choix le plus judicieux peut être différent. Selon les fonctionnalités de l’appli aussi.

le fait de créer ce nouveau contrôleur de mise à jour de tout contrat "au sens large du terme" me paraît être une toute nouvelle approche de développement, et un peu bizarre car pas liée à une fonctionnalité en particulier

Par exemple ça, on ne peut pas savoir si c’est bizarre à ta place. Peut-être qu’il y a du code à mettre en commun, peut-être qu’il faut faire des vérifications communes sur ces différentes mises à jour qui valent la peine de mettre du code en commun pour être réutilisé par d’autres fonctionnalités ailleurs, etc.

+4 -0

Bonjour tout le monde, et merci pour vos réponses.

Désolé du retard, j’essaie de réduire le temps passé devant les écrans…


Je déduis de vos réponses que "ça dépend" en est un bon résumé.

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