les appels systèmes Linux pour aiguiller le système de stockage : il s’agit d’une spécialisation des comportements par défaut.
Le problème est quand une abstraction ment, c’est-à-dire que le modèle qu’elle expose ne correspond pas à la réalité sous-jacente. Quand ce type d’abstraction se met à fuir, vous ne retrouvez pas alors avec quelque chose de plus complexe, mais avec quelque chose de complètement différent. Vous devez alors jongler avec deux modèles mentaux : celui qui est mis en avant par l’abstraction et le « vrai » qui correspond à l’implémentation.
Le cas des bases NoSQL qui exposent une surcouche SQL est un exemple connu de ce biais. Souvent cette couche n’expose qu’une sous-partie limitée du SQL, et les développeur·euse·s doivent donc apprendre ou découvrir quelles requêtes SQL fonctionnent et quelles requêtes ne fonctionnent pas, et avec quelles performances. Ensuite cette surcouche ne permet souvent pas d’utiliser toute les fonctionnalités promises par l’outil. Suivant les cas, il faut donc utiliser du SQL, mais pas tout le SQL, ou le langage natif d’interrogation de la base.
En anglais ce problème porte le nom d’impedance mismatch, c’est-à-dire d’inadaptation d’impédance, ce qui signifie que le lien entre le fonctionnement interne et l’abstraction externe n’est pas efficace.
Réutiliser des frontières connues peut donc être intéressant, mais peut donner lieu à des mauvaises surprises.
Combiner deux systèmes ou bâtir un système sur un autre d’un type différent est une opération au résultat complexe : vais-je obtenir un système avec les avantages des deux sans aucun des inconvénients, ou au contraire les inconvénients de l’un vont-ils annuler les avantages de l’autre ? Et au final, serai-je en mesure d’exposer le résultat sous forme d’une frontière riche, cohérente et minimale ?
Par exemple : bâtir un système synchrone sur un système asynchrone. L’approche naïve est simple à mettre en œuvre : simuler un appel synchrone en appelant régulièrement le système asynchrone jusqu’à avoir une réponse. Mais comment faire pour pouvoir interrompre le traitement en cours de route, ce qu’on s’attend à pouvoir faire dans le cas d’un « vrai » système synchrone ? Faut-il dans ce cas demander une annulation ? Mais que se passe-t-il si le traitement s’est terminé pendant ce temps ? …
On atteint alors la limite de la solution naïve.
Quand une combinaison de deux systèmes fonctionne bien, elle peut avoir deux résultats :
Prenons le cas d’un outil magique permettant de distribuer des traitements.
Si on l’ajoute à un système de calcul sans stockage de données, il peut permettre d’exécuter un nombre plus important de traitements en parallèle sans supprimer de cas d’usage. Du point de vue de la frontière, le résultat est donc mieux que l’ancien sans effet secondaire : il s’agit d’une amélioration.
Si on l’ajoute à un système de base de données, il peut permettre d’augmenter la vitesse de traitement en sacrifiant la transactionnalité et/ou la cohérence des données. Le nouvel outil n’a donc plus les mêmes usages que l’ancien : il s’agit d’un hybride.
Ainsi quand un éditeur logiciel vous promet un produit miracle qui fait quelque chose que personne d’autre ne propose, la première chose à faire et de vérifier s’il s’agit d’un cas d’amélioration ou d’un cas d’hybridation. Et s’il s’agit d’une hybridation, répond-il bien à votre besoin ou allez-vous atterrir dans un cas limite qui aura l’air de fonctionner mais pas tout à fait ?
La combinaison de deux systèmes conduit souvent à déléguer certaines choses à l’extérieur, car le système résultant ne saura plus prendre seul certaines décisions.
Par exemple, dans un système distribué, garantir l’unicité d’un message est très compliqué, car cela nécessite une forme de centralisation. Il est donc souvent plus simple que le système appelant s’en occupe car il dispose d’informations supplémentaires qui lui permettent de le faire plus facilement.
Cela risque de créer des incohérences dans la frontière, et des fuites d’abstractions. Il s’agit d’un arbitrage à faire : vaut-il mieux quelque chose de plus riche mais de plus difficile à comprendre, voire de plus difficile à opérer ?
Au final, assembler des systèmes de type différent peut donc être risqué. Pour maîtriser le résultat, le mieux est de choisir des systèmes avec des frontières cohérentes.
L’art subtil de combiner les systèmes
Parfois, pour obtenir un résultat intéressant en combinant deux systèmes, la meilleure approche est de commencer par décaper la frontière d’un des deux afin de reconstruire une autre frontière plus propice.
Dans un SI, il y a de nombreuses briques logicielles, chacune avec ses besoins propres. De nombreuses fonctionnalités nécessitent de s’appuyer sur d’autres briques.
Il s’agit donc du problème d’assemblage mais à grande échelle. Plus il y a de frontières et plus elles sont communes à de nombreuses applications, plus le problème est compliqué, c’est le couplage.
Il faut donc maîtriser les frontières qui sont exposées. Cela ne signifie pas interdire les échanges entre systèmes, mais faire des choix en fonction de votre contexte.
En plus du nombre de frontières, il faut aussi piloter le nombre de types de frontières.
Par exemple, si vous avez cinq types de bases de données qui ont des garanties différentes, vous aurez peut-être cinq types de services avec des SLAs différents : certains seront transactionnels, certains auront des risques d’incohérences… Et les services qui auront besoin de composer ces services ne sauront pas faire : que se passe-t-il quand j’ai une moitié de donnée pas cohérente mais synchrone et une autre moitié cohérente mais asynchrone ? À l’inverse, si tout le monde expose un même type de frontière, comme des services REST, combiner les services et les SLAs est très simple. Le problème est d’autant plus compliqué que ceux·celles qui paient le prix de la complexité ne sont pas ceux·celles qui développent le système qui expose une frontière, mais ceux qui l’utilisent.
Dans ce cas, l’approche est plus directe : il faut limiter les types de frontières, et donc les types d’outils et/ou de technologies. Comme vu plus haut, cela veut dire qu’en contrepartie certaines choses seront plus difficiles, voire impossibles, mais c’est le prix à payer pour limiter la complexité de votre SI. Cela ne veut pas dire "un seul type d’outil", mais essayer d’en avoir le minimum viable pour vous permettre de répondre à vos besoins.
Pour cela, il faut prendre les choses sous l’angle du·de la client·e : définir quels sont ses besoins et déterminer la frontière qui y répond le mieux.