Pourquoi REST Api ne suit pas le modèle de conception de façade


9

En comparant la structure REST [api] avec un modèle OO, je vois ces similitudes:

Tous les deux:

  • Sont orientés données

    • REST = Ressources
    • OO = Objets
  • Fonctionnement surround autour des données

    • REST = entourer les VERBES (Get, Post, ...) autour des ressources
    • OO = promouvoir le fonctionnement autour des objets par encapsulation

Cependant, les bonnes pratiques OO ne se tiennent pas toujours sur les API REST lorsque vous essayez d'appliquer le motif de façade par exemple: dans REST, vous n'avez pas 1 contrôleur pour gérer toutes les demandes ET vous ne cachez pas la complexité des objets internes.

Relation d'objet simple entre 2 concepts

Analogie entre OO et REST

Au contraire, REST favorise la publication des ressources de toutes les relations avec une ressource et autres sous au moins deux formes:

  1. via les relations de hiérarchie des ressources (Un contact de l'id 43 est composé d'une adresse 453): /api/contacts/43/addresses/453

  2. via des liens dans une réponse json REST:

>> GET /api/contacts/43
<< HTTP Response {
   id: 43, ...
   addresses: [{
      id: 453, ...
   }],
   links: [{
      favoriteAddress: {
          id: 453
      }
   }]
}

Complexité de base cachée par objectA

Pour en revenir à OO, le motif de conception de façade respecte un Low Couplingentre un objetA et son « client objectB » et High Cohesionpour cet objetA et sa composition d'objet interne ( objectC , objectD ). Avec l' objectA interface, cela permet à un développeur d'impact limite objectB des ObjectA changements internes (en ObjectC et objectD ), tant que le objectA api (opérations) sont toujours respectées.

Dans REST, les données (ressource), les relations (liens) et le comportement (verbes) sont éclatés en différents éléments et disponibles sur le Web.

En jouant avec REST, j'ai toujours un impact sur les changements de code entre mon client et mon serveur: parce que j'ai High Couplingentre mes Backbone.jsdemandes et Low Cohesionentre les ressources.

Je n'ai jamais compris comment laisser mon Backbone.js javascript applicationaccord avec la découverte de " ressources et fonctionnalités REST " promu par les liens REST. Je comprends que le WWW est destiné à être servi par plusieurs serveurs et que les éléments OO ont dû être explosés pour être servis par de nombreux hôtes, mais pour un scénario simple comme "enregistrer" une page montrant un contact avec ses adresses, Je me retrouve avec:

GET /api/contacts/43?embed=(addresses)
[save button pressed]
PUT /api/contacts/43
PUT /api/contacts/43/addresses/453

ce qui m'a amené à déplacer l'action d'économie de responsabilité transactionnelle atomique sur les applications des navigateurs (puisque deux ressources peuvent être adressées séparément).

Dans cet esprit, si je ne peux pas simplifier mon développement (modèles de conception de façade non applicables), et si j'apporte plus de complexité à mon client (gestion de la sauvegarde atomique transactionnelle), quel est l'avantage d'être RESTful?


1
Laisse-moi comprendre. Vous dites que vous devez mettre à jour un contact avec une adresse liée "intégrée" (composition) à l'aide de deux appels REST, un pour le contact et un autre pour son adresse. Vous avez une façade pour gérer la mise à jour des contacts. Quel est le problème avec la mise en PUT /api/contacts/43cascade des mises à jour des objets internes? J'ai eu beaucoup d'API conçues comme ça (l'URL principale lit / crée / met à jour le "tout" et les sous-URL mettent à jour les morceaux). Assurez-vous simplement de ne pas mettre à jour l'adresse lorsqu'aucune modification n'est requise (pour des raisons de performances).
Anthony Accioly

@AnthonyAccioly, vous avez bien compris. J'ai essayé de clarifier ma question en ajoutant quelques images. Votre suggestion est bonne et c'est aussi la conclusion que j'ai tirée: contrôler manuellement ma demande et utiliser un objet incorporé pour envoyer une seule demande pour garder atomique ma mise à jour. Pourtant: Pourquoi tout dans REST me pousse loin des qualités OO ou des renforcements (encapsulation, ...) en aplatissant mon modèle (impliquant de nombreux contrôleurs). L'utilisation de votre solution donne 1 mise à jour atomique. Ne pas utiliser votre solution apporte au développeur une nouvelle responsabilité et aucune règle sur l'API pour l'empêcher de le faire.
Alain

Juste une note: les "relations de hiérarchie des ressources" que vous avez mentionnées dépendent de la façon dont on peut décider de coder les informations de relation dans un identifiant (dans ce cas, une URL). Je ne suis pas sûr que cette exposition d'informations soit quelque chose qui fait partie de REST ou quelque chose qu'elle promeut, juste une décision que l'on prend de son propre chef quand on arrive avec les URL d'un système. Si vous pensez le contraire, avez-vous des références de Roy Fielding discutant de cette question dans le cadre de REST?
Thiago Silva

Réponses:


7

Je pense que les objets ne sont construits correctement que sur des comportements cohérents et non sur des données. Je vais provoquer et dire que les données sont presque hors de propos dans le monde orienté objet. En fait, il est possible et parfois courant d'avoir des objets qui ne renvoient jamais de données, par exemple des "puits de journal", ou des objets qui ne renvoient jamais les données qui leur sont transmises, par exemple s'ils calculent des propriétés statistiques.

Ne confondons pas les PODS (qui ne sont guère plus que des structures) et les objets réels qui ont des comportements (comme la Contactsclasse dans votre exemple) 1 .

Les PODS sont essentiellement une commodité utilisée pour parler aux référentiels et aux objets métier. Ils permettent au code d'être sûr de type. Ni plus ni moins. Les objets métier, en revanche, fournissent des comportements concrets , comme valider vos données, les stocker ou les utiliser pour effectuer un calcul.

Ainsi, les comportements sont ce que nous utilisons pour mesurer la "cohésion" 2 , et il est assez facile de voir que dans votre exemple d'objet, il y a une certaine cohésion, même si vous ne montrez que des méthodes pour manipuler des contacts de haut niveau et aucune méthode pour manipuler des adresses.

Concernant REST, vous pouvez voir les services REST comme des référentiels de données. La grande différence avec la conception orientée objet est qu'il n'y a (presque) qu'un seul choix de conception: vous avez quatre méthodes de base (plus si vous comptez HEAD, par exemple) et bien sûr, vous avez beaucoup de latitude avec les URI afin que vous puissiez faire des astuces des trucs comme passer de nombreux identifiants et récupérer une structure plus grande. Ne confondez pas les données qu'ils transmettent avec les opérations qu'ils effectuent. La cohésion et le couplage concernent le code et non les données .

De toute évidence, les services REST ont une cohésion élevée (chaque façon d'interagir avec une ressource est au même endroit) et un faible couplage (chaque référentiel de ressources ne nécessite pas la connaissance des autres).

Le fait fondamental reste cependant que REST est essentiellement un modèle de référentiel unique pour vos données. Cela a des conséquences, car il s'agit d'un paradigme construit autour d'une accessibilité facile sur un support lent, où le coût de la «conversation» est élevé: les clients souhaitent généralement effectuer le moins d'opérations possible, mais en même temps, ils ne reçoivent que les données dont ils ont besoin. . Cela détermine la profondeur d'une arborescence de données que vous allez renvoyer.

Dans une conception orientée objet (correcte), toute application non triviale effectuera des opérations beaucoup plus complexes, par exemple par la composition. Vous pourriez avoir des méthodes pour effectuer des opérations plus spécialisées avec les données - ce qui doit être le cas, car bien que REST soit un protocole API, OOD est utilisé pour créer des applications complètes destinées aux utilisateurs! C'est pourquoi la mesure de la cohésion et du couplage est fondamentale dans OOD, mais presque insignifiante dans REST.

Il devrait être évident maintenant que l'analyse de la conception de données avec des concepts OO n'est pas un moyen fiable de le mesurer: c'est comme comparer des pommes et des oranges!

En fait, il s'avère que les avantages d'être RESTful sont (principalement) ceux décrits ci-dessus: c'est un bon modèle pour les API simples sur un support lent. Il est très cacheable et partageable. Il a un contrôle fin sur le bavardage, etc.

J'espère que cela répond à votre question (assez multiforme) :-)


1 Ce problème fait partie d'un ensemble plus large de problèmes connus sous le nom de non -concordance d'impédance objet-relationnelle . Les partisans des ORM sont généralement dans le camp qui explore les similitudes entre l'analyse des données et l'analyse du comportement, mais les ORM ont récemment fait l'objet de critiques car ils ne semblent pas vraiment résoudre le problème d'impédance et sont considérés comme des abstractions qui fuient .

2 http://en.wikipedia.org/wiki/Cohesion_(computer_science)


Vous avez raison, j'ai eu du mal à faire exploser ma question à bien des égards, à clouer un point précis, car la question aborde une "mauvaise" conclusion basée sur l'accumulation de ces aspects. J'essaierai maintenant de répondre à vos points dans de nombreux commentaires.
Alain

[texte 1] J'ai utilisé le mot "données" pour faire abstraction du monde OO et REST. Quel mot utiliseriez-vous pour résumer les propriétés dans OO et la structure des données dans REST?
Alain

@Alain "data" va bien, mais je ne veux pas confondre PODS et objets métier. Lorsque nous parlons d'OOD, nous parlons généralement de la seconde. Les premiers sont une commodité, et on pourrait presque aussi facilement penser à un dictionnaire ou une structure ou un tuple.
Sklivvz

Oui, je suis d'accord, j'utilise Backbone.js où l'enregistrement d'un modèle utilise une structure json simple. C'est là que le texte reflète mon expérience de codage réelle.
Alain

[texte 3] C'est nouveau pour moi. Je pensais que la cohésion était mesurée par le nombre de fois où les méthodes utilisent une relation spécifique ... Je préfère votre façon de voir les choses.
Alain

1

Dans cet esprit, si je ne peux pas simplifier mon développement (modèles de conception de façade non applicables), et si j'apporte plus de complexité à mon client (gestion de la sauvegarde atomique transactionnelle), quel est l'avantage d'être RESTful?

La réponse à "où est l'avantage d'être RESTful?" est analysé et expliqué en détail ici: http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

Cependant, la confusion dans cette question est qu'il ne s'agit pas des caractéristiques de REST et de la manière de les gérer, mais en supposant que la conception des URL que vous avez créées pour votre système d'exemple a quelque chose à voir avec RESTful. Après tout, REST indique qu'il existe des choses appelées ressources et un identifiant doit être fourni pour celles qui doivent être référencées, mais cela ne signifie pas que, par exemple, les entités de votre modèle ER doivent avoir une correspondance 1-1 avec les URL que vous avez créées (ni que les URL doivent coder la cardinalité des relations ER dans le modèle).

Dans le cas des contacts et des adresses, vous auriez pu définir une ressource qui représente conjointement ces informations comme une seule unité, même si vous souhaitez extraire et enregistrer ces informations dans, disons, différentes tables de bases de données relationnelles, chaque fois qu'elles sont PUT ou POSTed .


1

C'est parce que les façades sont un «kludge»; vous devriez jeter un oeil à «abstraction api» et «chaînage api». L'API est une combinaison de deux ensembles de fonctionnalités: les E / S et la gestion des ressources. Localement, les E / S sont correctes mais dans une architecture distribuée (c.-à-d. Proxy, porte api, file d'attente de messages, etc.) les E / S sont partagées et ainsi les données et les fonctionnalités deviennent dupliquées et enchevêtrées. Cela conduit à une préoccupation architecturale transversale. Cela affecte tous les API existants.

La seule façon de résoudre ce problème est d'abstraire la fonctionnalité d'E / S de l'API à un gestionnaire pré / post (comme un gestionnaire HandlerIntercepter dans Spring / Grails ou un filtre dans Rails) afin que la fonctionnalité puisse être utilisée comme une monade et partagée entre des instances et des externes. outillage. Les données de demande / réponse doivent également être externalisées dans un objet afin de pouvoir être partagées et rechargées également.

http://www.slideshare.net/bobdobbes/api-abstraction-api-chaining


0

Si vous comprenez votre service REST, ou en général tout type d'API, tout comme une interface supplémentaire exposée aux clients afin qu'ils puissent programmer votre ou vos contrôleurs à travers lui, cela devient soudainement facile. Le service n'est rien de plus qu'une couche supplémentaire au-dessus de votre logique biz.

En d'autres termes, vous n'avez pas à diviser la logique biz entre plusieurs contrôleurs, comme vous l'avez fait dans votre image ci-dessus, et plus important encore, vous ne devriez pas. Les structures de données utilisées pour échanger des données n'ont pas besoin de correspondre aux structures de données que vous utilisez en interne, elles peuvent être très différentes.

C'est l'état de l'art, et largement accepté, que c'est une mauvaise idée de mettre une logique biz dans le code de l'interface utilisateur. Mais chaque interface utilisateur n'est qu'une sorte d'interface (le I dans l'interface utilisateur) pour contrôler la logique biz derrière. Par conséquent, il semble évident que c'est également une mauvaise idée de mettre une logique biz dans la couche de service REST, ou toute autre couche d'API.

Sur le plan conceptuel, il n'y a pas beaucoup de différence entre l'interface utilisateur et l'API de service.


Je suis d'accord sur la notion de couche, mais que voulez-vous dire par "programmer votre contrôleur à travers elle"?
Alain

1
Je tiens à souligner le fait que le contrôleur lui-même est le vrai service. L'interface enroulée autour du tout n'est qu'un moyen de réaliser quelque chose. Toute interface existe pour faciliter l'accès à la fonctionnalité encapsulée, d'une manière ou d'une autre. Une interface graphique fait cela pour les utilisateurs humains, une API de service est utilisée par les clients. Les deux publics cibles veulent réaliser quelque chose avec les choses enveloppées derrière l'interface. Je suis d'accord pour dire que "programme" n'est peut-être pas la meilleure formulation pour cela, mais "contrôler les contrôleurs" semble maladroit non plus ;-)
JensG
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.