# REST pragmatique
# REST pragmatique pour le projet Mojaloop
Avec lâĂ©mergence de la stratĂ©gie API comme levier de montĂ©e en charge pour les services Internet, lâattention portĂ©e aux technologies dâinterconnexion a Ă©voluĂ©. Sâappuyant sur les principes qui ont permis au Web de se structurer et de scaler, REST (Representational State Transfer) est devenu un choix de conception privilĂ©giĂ© pour les API de services Internet. Mais si les principes REST, proposĂ©s dans la thĂšse de doctorat de Roy Fielding qui les a dĂ©finis, ont une valeur acadĂ©mique pour la recherche, un design REST pur nâest pas aujourdâhui praticable pour la plupart des applications. Nous prĂ©conisons une forme de REST pragmatique â un patron de conception qui adopte les Ă©lĂ©ments utiles du design RESTful sans exiger une puretĂ© acadĂ©mique stricte.
# Le modÚle de maturité de Richardson
Martin Fowler a citĂ© un modĂšle structurĂ© dâadoption RESTful Ă©laborĂ© par Leonard Richardson et exposĂ© (opens new window) lors dâune confĂ©rence QCon. Fowler lâappelle le modĂšle de maturitĂ© de Richardson du design RESTful.
Martin Fowler, en rĂ©fĂ©rence Ă Rest in Practice (opens new window)ÂČ, rĂ©sume ainsi la genĂšse du design RESTful :
utiliser des services web RESTful pour traiter une grande partie des problĂšmes dâintĂ©gration auxquels les entreprises sont confrontĂ©es. Au cĆur du propos se trouve lâidĂ©e que le Web est une preuve par lâexistence dâun systĂšme distribuĂ© massivement scalable qui fonctionne trĂšs bien, et que nous pouvons en tirer des idĂ©es pour construire des systĂšmes intĂ©grĂ©s plus facilement.
Une approche pragmatique du design RESTful emprunte les meilleures parties du cadre conceptuel de Fielding pour permettre aux dĂ©veloppeurs et intĂ©grateurs de comprendre aussi rapidement que possible ce quâils peuvent faire avec lâAPI, sans avoir Ă Ă©crire de code superflu.
Au plus fondamental, un design RESTful est centré sur les ressources et utilise les verbes HTTP. Au plus avancé, un design REST académique pur applique HATEOAS via des contrÎles hypermédia. Nous recommandons un design RESTful de niveau 2 pour Mojaloop.
# Pourquoi pas les contrÎles hypermédia ?
Bien que HATEOAS soit un principe fascinant â il prĂ©conise quâun serveur rĂ©ponde Ă chaque action client par une liste de toutes les actions possibles menant le client vers son prochain Ă©tat applicatif â et que les clients ne doivent pas sâappuyer sur des informations hors bande (comme une spĂ©cification API Ă©crite) pour savoir quelles actions sont possibles sur quelles ressources ni sur le format des URI.
Câest cette derniĂšre interdiction qui fait Ă©chouer le test du REST pragmatique : si HATEOAS est une approche thĂ©orique intĂ©ressante pour limiter le couplage, elle ne sâapplique pas facilement Ă Mojaloop (ni Ă tout autre design dâAPI contractuelle). Si lâon considĂšre le public des API dâinterconnexion, on trouve des acteurs commerciaux soumis Ă des rĂšgles de schĂ©ma trĂšs prĂ©cises. Les interactions entre participants, et entre participant et hub de services central, sont fortement spĂ©cifiĂ©es pour fixer un risque commercial acceptable, pour proposer aux utilisateurs finaux des transactions tarifĂ©es Ă trĂšs faible coĂ»t. Cela exige une prĂ©visibilitĂ© ex ante de lâAPI, incompatible avec le principe HATEOAS dĂ©fini par Fielding.
# Principes REST pragmatiques
# Les URI définissent les ressources
Un schĂ©ma dâURI bien conçu rend une API facile Ă consommer, Ă dĂ©couvrir et Ă Ă©tendre, comme une API soignĂ©e dans un langage classique. Le REST pur dĂ©daigne ce principe au profit de HATEOAS. Mais le REST pragmatique suit un schĂ©ma dâURI habituel pour faciliter la comprĂ©hension humaine, mĂȘme si des principes HATEOAS sont employĂ©s pour la dĂ©couverte.
Les chemins dâURI qui dĂ©signent une collection dâobjets doivent ĂȘtre un nom pluriel, p. ex. /customers, pour un ensemble de clients. Lorsquâune collection ne peut avoir quâune seule instance, on utilise le singulier pour Ă©viter la confusion. P. ex. GET /transfers/:id/fulfillment est correct, car il nây a quâun objet fulfillment par transfert identifiĂ©.
Les chemins dâURI qui dĂ©signent un objet unique doivent ĂȘtre un nom pluriel (la collection) suivi dâun identifiant unique prĂ©dĂ©fini. P. ex. /customers/123456 pour le client numĂ©ro 123456. Lâidentifiant doit ĂȘtre unique dans la collection et persister pendant la vie de lâobjet dans cette collection. Les identifiants ne doivent pas ĂȘtre des valeurs ordinales â un parcours ordinal dans une collection se fait via des paramĂštres de requĂȘte sur lâURI de collection.
Les chemins dâURI peuvent avoir un prĂ©fixe pour lâenvironnement, la version ou un autre contexte de la ressource. AprĂšs le chemin dâidentification, il ne doit suivre que des collections et des rĂ©fĂ©rences dâobjets.
Les identifiants de segments de chemin et de requĂȘte doivent ĂȘtre choisis dans lâensemble des caractĂšres romains [0-9A-Za-z]. Utiliser camelCase pour les Ă©lĂ©ments du chemin dâURI. Ne pas utiliser snake_case.
Pour lever toute ambiguĂŻtĂ©, le caractĂšre « _ » (tiret bas) et « - » (tiret) ne doivent pas ĂȘtre utilisĂ©s dans les identifiants de segments de chemin ou de requĂȘte.
Cela peut sembler un peu Ă©troit. Lâobjectif est dâavoir un format dâURI bien dĂ©fini, cohĂ©rent avec les usages rĂ©pandus, simple Ă dĂ©crire, prĂ©visible, et qui se mappe aux environnements et conventions natifs. Cela ne satisfera pas tout le monde. Voici la logique de cette contrainte :
CapitalCase et camelCase sont la norme de facto pour NodeJS et JavaScript et une contrainte courante pour les URI : les segments de chemin sont souvent mappés vers des ressources internes JS ; respecter les conventions JS est cohérent.
Les noms de champs en JSON et SQL doivent suivre la mĂȘme convention, car ils sont souvent mappĂ©s automatiquement vers lâespace de noms des variables et peuvent ĂȘtre rĂ©fĂ©rencĂ©s dans les URI comme segments de chemin ou de requĂȘte.
Il faut aussi Ă©viter « $ » sauf si une bibliothĂšque lâexige (p. ex. jQuery). Le JCL IBM est rĂ©volu ; laissons-le en paix. Il existe de meilleurs outils de portĂ©e pour sĂ©parer les espaces de noms que dâintroduire des symboles non romains.
Il faut Ă©viter « - » (tiret) dans les noms de segments de chemin et de paramĂštres de requĂȘte car cela ne se mappe pas aux noms de variables, SQL ou champs JSON.
Les caractĂšres tiret bas doivent ĂȘtre Ă©chappĂ©s dans le source Markdown en les prĂ©fixant par « \ ».
On a signalĂ© que snake_case serait lĂ©gĂšrement plus lisible que camelCase dans les noms de variables, mais cela nâamĂ©liore pas la lisibilitĂ© des URI : cela gĂȘne visuellement la lecture des dĂ©limiteurs de segments de chemin et de requĂȘte. Et lorsque les URI sont soulignĂ©es Ă lâaffichage, les tirets bas deviennent illisibles.
# ParamĂštres dâURI
Utiliser un ensemble standard et prévisible de paramÚtres optionnels de maniÚre cohérente.
Un ensemble standard de paramĂštres de requĂȘte doit servir pour les collections afin de laisser lâappelant contrĂŽler la portion de collection retournĂ©e. « count » pour dĂ©finir le nombre dâobjets Ă retourner, « start » pour dĂ©finir le point de dĂ©part dans le jeu de rĂ©sultats, et « q » comme requĂȘte de recherche libre. Nous dĂ©finirons lâensemble standard au fil du temps et lâappliquerons de façon uniforme.
# Verbes
Les objets singuliers doivent prendre en charge GET pour la lecture, PUT pour le remplacement complet (ou la création lorsque la clé primaire est fournie par le client et persistante, p. ex. un PAN de carte de paiement), et DELETE pour la suppression.
Les collections doivent prendre en charge GET pour lire tout ou partie dâune collection, et POST pour ajouter un nouvel objet Ă la collection.
Les objets singuliers peuvent prendre en charge POST pour modifier leur Ă©tat de façon dĂ©terminĂ©e. Poster un document JSON vers lâURI dâun objet singulier peut mettre Ă jour des champs sĂ©lectionnĂ©s ou dĂ©clencher un changement dâĂ©tat ou une action sans remplacer tout lâobjet.
GET doit ĂȘtre implĂ©mentĂ© de maniĂšre nullipotente â câest-Ă -dire que GET ne provoque jamais dâeffets de bord ni ne modifie lâĂ©tat visible par le client hors journalisation ou mise Ă jour des mĂ©triques dâinstrumentation.
PUT et DELETE doivent ĂȘtre implĂ©mentĂ©s de maniĂšre idempotente â les changements sâappliquent de façon cohĂ©rente aux donnĂ©es du systĂšme en ne dĂ©pendant que de lâĂ©tat de la ressource et des entrĂ©es, rien dâautre. Lâaction nâa pas dâeffet supplĂ©mentaire si elle est rĂ©pĂ©tĂ©e avec les mĂȘmes paramĂštres et ne dĂ©pend pas de lâordre dâautres opĂ©rations sur une collection ou dâautres ressources. Par exemple, retirer une ressource dâune collection peut ĂȘtre idempotent sur la collection. Utiliser PUT pour remplacer (ou crĂ©er) entiĂšrement une ressource identifiĂ©e de façon unique lorsque lâURI est entiĂšrement connue du client est aussi idempotent. Le systĂšme peut donc rĂ©ordonner les opĂ©rations pour gagner en efficacitĂ© ; le client nâa pas besoin de savoir si la ressource existe avant de tenter un remplacement.
POST et PATCH ne sont pas des opĂ©rations idempotentes. POST sert Ă crĂ©er des ressources dont lâidentifiant est assignĂ© par le serveur ou lorsquâune ressource interne unique est impliquĂ©e par lâURI cible (p. ex. POST /transfers, mais PUT /transfers/:id/fulfillment). Voir la note 3 (RFC 5789).
# Format des données
Nous privilégions les formats liés à JSON (opens new window) plutÎt que XML (voir note 4). Dans certains cas, les formats seront binaires ou XML, selon des normes préexistantes, et seront précisément spécifiés. Les formats binaires doivent avoir une syntaxe formelle pour éviter des ambiguïtés de représentation (jeux de caractÚres, représentations big-endian ou little-endian des valeurs numériques, etc.).
Les dates et heures utilisĂ©es dans les API doivent respecter la norme ISO 8601, avec le profil du document W3C sur les formats date et heure (voir note 5). Cette note W3C doit rĂ©duire la complexitĂ© et les erreurs lorsque des composants Ă©changent des dates et heures concrĂštes. Il existera des cas oĂč un format non ISO sera requis par une norme externe, p. ex. dates dâexpiration ISO 7813.
Les formats XML standard existants doivent disposer dâun schĂ©ma XSD pour le sous-ensemble de profil acceptable dans le projet. Pour des formats particuliĂšrement complexes, on peut utiliser un traducteur de profil commun pour mapper entre le sous-ensemble projet du format standard et le format fil utilisĂ© par un protocole normalisĂ©. Cela limite le couplage aux formats complexes de façon plus maintenable.
Lorsque lâaction PATCH est spĂ©cifiĂ©e pour une ressource, nous utiliserons un format de document de patch cohĂ©rent (p. ex. JSON Patch (opens new window), voir note 6).
# Codes de retour
Utiliser les codes HTTP de façon cohérente et conformément à leurs définitions standard. Les codes standard sont définis dans la RFC 2616 (voir note 7).
# Format dâerreur lisible par machine
LâAPI doit fournir un rĂ©sultat dâerreur lisible par machine dans un format JSON bien dĂ©fini. {Ă dĂ©finir : enveloppe de rĂ©ponse et format des erreurs, dĂ©fauts et enveloppes de succĂšs. Le design RESTful sâappuie sur les en-tĂȘtes pour les erreurs de protocole ; les infos de dĂ©bogage peuvent aussi transiter dans les en-tĂȘtes. Il faut ĂȘtre clair sur lâusage dâune enveloppe et comment elle soutient la communication production normale entre client et serveur.}
# Versionnement
Les URI dâAPI doivent inclure un identifiant de version au format vM comme premier segment de chemin (oĂč M est la composante majeure du numĂ©ro de version). LâAPI et son identifiant de version doivent respecter la spĂ©cification versionnement sĂ©mantique (opens new window) 2.0 pour le versionnement dâAPI (voir note 8).
Un client doit indiquer le numĂ©ro de version majeure dans chaque requĂȘte. Un client ne peut pas exprimer lâexigence dâune version mineure prĂ©cise.
Le numĂ©ro de version complet de lâAPI est indiquĂ© dans lâen-tĂȘte de rĂ©ponse (Ă dĂ©finir) pour toutes les rĂ©ponses rĂ©ussies et en erreur.
Bien que le contrat de version dâune API soit influencĂ© par les niveaux majeur, mineur et correctif, seul le numĂ©ro majeur lie lâAPI en production â un client de production ne peut pas demander une version mineure ou un niveau de correctif particulier, et un serveur de production nâaccepte pas une requĂȘte URI qui spĂ©cifierait ces Ă©lĂ©ments supplĂ©mentaires.
En revanche, dans les environnements de prĂ©production, on prĂ©voit quâune combinaison de suffixes mineur, correctif, prĂ©-release et mĂ©tadonnĂ©es puisse ĂȘtre prise en charge dans les requĂȘtes client (comme dĂ©fini dans semver [3]) et peut figurer dans les URI de prĂ©production pour faciliter le dĂ©veloppement et lâintĂ©gration.
# Il sera peut-ĂȘtre temps de mettre REST de cĂŽtĂ©
En concevant les API dâinterconnexion entre composants et systĂšmes participants, nous pourrions rencontrer des exigences qui ne correspondent pas exactement au modĂšle REST pragmatique dĂ©fini ici. Nous Ă©valuerons au cas par cas et choisirons ce qui sert le mieux les objectifs du projet.
# Exigences non fonctionnelles
En développant les API, nous ferons des choix cohérents sur les exigences non fonctionnelles pour renforcer les objectifs du projet.
1: http://martinfowler.com/articles/richardsonMaturityModel.html, consulté le 18 août 2016.
2: https://www.amazon.com/gp/product/0596805829 (opens new window), consulté le 18 août 2016.
3: RFC 5789, PATCH Method for HTTP, https://tools.ietf.org/html/rfc5789 (opens new window), consulté le 18 août 2016.
4: Introducing JSON, http://json.org/ (opens new window), consulté le 18 août 2016.
5: http://www.w3.org/TR/1998/NOTE-datetime-19980827 (opens new window), consulté le 22 août 2016.
6: JSON Patch, http://jsonpatch.com/ (opens new window), consulté le 18 août 2016.
7: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html (opens new window)
8: Semantic Versioning 2.0.0, http://semver.org/ (opens new window), consulté le 18 août 2016.
