dimanche 1 mars 2009

L'excès de structure

Y en a mare des grands principes d'architectures qui imposent des couches et couches sans valeurs ajoutées. Où est perdu le sens commun ?

« Il faut séparer la couche métier, de la couche processus, de la couche présentation, de la couche de persistance. Chaque couche doit être hermétique. »

Super. J'ai participé à des projets basés sur ce type d'approche.

  • Comment gérer les exceptions ?

  • Et bien, dans chaque couche, on capture l'exception, on l'encapsule dans une autre et on la propage.

  • On ne doit pas capturer les exceptions par leurs types ? Si je l'encapsule, je n'ai plus le moyen de différencier la gestion d'erreur suivant son type !

  • Si, si. Fait un switch sur le instanceof getCause() !

  • Ce n'est le rôle de l'instruction catch ?

  • Heu, oui mais autrement cela viole les principes architecturaux.

Donc, pour écrire un simple traitement métier pouvant par mégarde avoir un problème (violation d'une règle d'intégrité de la base de donnée, paramètres invalides, etc.) je dois écrire beaucoup de traitements, sources d'erreurs.

J'écris le traitement de la couche métier qui traite les paramètres et retourne un objet métier. Un nouveau traitement, normal.

Dans la couche métier une capture d'exception de la couche de persistance. Transformer l'exception et la propager. Et un nouveau traitement, cela fait deux.

Pour qu'elle ait une sémantique, je dois donc écrire une nouvelle classe d'exception spécifique. Et une nouvelle classe.

Dans la couche processus, s'il n'y a pas d'exception, je vais bien entendu transformer les objets de présentation reçu en paramètre pour les rendre présentable à la couche métier. Je fais de même pour transformer les objets de retour de la couche métier en objets utiles à la couche présentation. Je remplace les entiers, les flottants, etc. en chaines de caractères. Et deux nouvelles classes, ce qui fait trois et deux nouveau traitements ce qui fait quatre.

De plus, je capture l'exception pour l'encapsuler et la propager sous une autre forme. Et un nouveau traitement, ce qui donne cinq.

Pour garder une sémantique, je dois écrire une nouvelle classe d'exception spécifique à la couche processus. Et une nouvelle classe, ce qui fait quatre.

Dans la couche présentation, j'utilise les objets de présentation. En cas d'exception, j'analyse en profondeur la cause, puis je présente des excuses à l'utilisateur. Un traitement de plus, mais c'est inévitable.

Pour respecter l'architecture, j'ai donc écris cinq traitements supplémentaire et quatre classes. Tous cela pour une seule petite méthode métier !

Les impacts négatifs sont nombreux : complexification du code ; multiplication du nombre de ligne de code, donc d'erreurs ; difficulté à faire évoluer le code ; empreinte mémoire plus importante ; consommation CPU plus importantes ; multiplication des ressources de la plate-forme de production ; impact écologique associés.

Rappelez moi l'objectif d'une architecture ? Ce n'est pas d'uniformiser et de simplifier ? C'est réussi ? Il semble que non.

Pourquoi ne pas utiliser des idées simples comme : « Les objets métiers peuvent être directement manipulé par la présentation ». « Les objets métiers sont persistants » « Les exceptions ne doivent en aucun cas être convertie, à moins d'apporter un valeur ajoutée permettant un meilleur traitement de celle-ci. »

Certaines idées émergent maintenant, comme le mariage de la couche métier et de la couche de persistance, grâce à des frameworks d'annotation et de modification dynamique du code. C'est une bonne chose. Mais, sans ces frameworks, c'était possible également. Par excès de structure, on a séparé les couches.

Lorsque l'on propose des solutions alternatives à ces architectures, démontrant clairement la simplification d'une architecture alternative, la réduction du nombre de lignes de code, l'amélioration des performances, on reçoit généralement un refus du client ou de l'architecte. « C'est moins académique... » Que répondre ? Il n'y a pas plus sourd que celui qui ne veux pas entendre.

Nous manquons de bon sens ou de sens pratiques. Les études que nous avons suivi nous incitent tous à faire la même chose, sans réfléchir. Comme le montre brillamment l'expérience de Victoria Horner (http://tinyurl.com/8n7wx2 ), Les singes réfléchissent, les enfants nous singent. Il est temps de devenir adulte et de réfléchir.

A suivre le troupeau, ont fonce vers des catastrophes. Dans un autre métier, la finance par exemple, il fallait surtout suivre le voisin, même si tous le monde savait qu'on allait dans le mur. Maintenant c'est fait.

On ne vous reprochera pas d'avoir tord avec les autres. Seulement d'avoir tord tous seul. Par contre, avoir raison tous seul n'est pas facile.

Au exemple réel. Je devais faire passer des tests en C++, rédigés par d'autres, afin de tester les connaissances des candidats à l'embauche. La question était simple : écrivez une classe qui permet d'avoir une pile de caractères avec les méthodes push() et pop(). (Les Standards Templates Librairies n'exitaient pas encore). Une méthode pour ajouter un caractère à la pile, une autre pour enlever le caractère au dessus de la pile.

Tous les candidats sans exceptions, écrivaient une classe périphérique Item pour pouvoir construire une liste chainée portant le caractère à mémoriser. La classe Stack offraient les méthodes push() et pop() et manipulait la liste chainée en conséquence.

Super, cela fonctionne. Mais, c'est la plus mauvaise réponse pour le cas d'espèce. Il s'agit d'une pile de caractères ! Soit un seul octet en mémoire ! Mettre en place une infrastructure logicielle lourde, avec de nombreux objets, des pointeurs partout est une absurdité. C'est utiliser trois semi-remorque pour aller chercher une demi-baguette.

La bonne réponse est d'allouer un simple tableau de caractère et de gérer un entier indiquant le nombre d'élément. Si le tableau déborde, on en crée un nouveau plus grand, on recopie l'ancien, on ajuste les compteurs et voilà.

La réponse académique n'est pas la bonne. Pour s'en rendre compte, il faut réfléchir un peu et ne pas foncer tête baissée. A t-on appris à réfléchir dans nos formations ? Continuons comme des enfants ou avons nous passé le stade du singe ?

L'inversion de dépendance est la dernière idée à la mode. C'est en effet une excellente solution pour casser la dépendance entre les composants, pour pouvoir les réutiliser dans différents contextes. C'est très bien pour les composants génériques et les frameworks. Mais est-ce vraiment utile pour les simples applications ?

Pour une application, cela permet de lier quelques objets entres-eux le temps d'un test unitaire. Les tests sont ainsi plus léger. Mais en production, les objets à utiliser et à lier sont finis, bien identifiés et ne doivent pas bouger. On ne change pas à chaud de framework de persistance ! Utiliser l'inversion de dépendance dans la phase d'initialisation de l'application pour construire les différents objets et les lier entres eux est une bonne idée. Cela prend quelques lignes de code.

Faire la même chose via un fichier XML complexe, entrainant l'utilisation de framework lourd, cela en vaut vraiment le coup ? J'ai toujours pensés qu'il était plus facile d'écrire quelques lignes de code Java ou C# que d'écrire la même chose en XML. Le rapport signal bruit du XML étant très, très mauvais, quelques lignes de Java ou C# sont toujours plus économique en volume de code, vitesse d'exécution et place mémoire.

Par abus du principe d'inversion de dépendance, les projets ne savent plus lier deux objets sans passer par le framework Spring. Il faut que tous objets puissent être relier à tous les autres objets du monde. C'est vraiment utile ? Il faut vraiment que tous les objets utilisent Spring ? Les quelques objets structurant ne sont pas suffisant ? Il faut un fichier XML énorme pour montrer qu'on maitrise le framework ?

Maintenant, l'évolution consiste à utiliser les annotations pour cela. C'est une très bonne chose. Il faut abandonner les fichiers de paramètres XML si on peut écrire la même chose avec quelques lignes de code !

Lorsque l'on conçoit une architecture logicielle, il faut toujours garder à l'esprit le volume de code nécessaire à écrire pour la respecter. Une bonne architecture est économe en ressources. Elle doit être pragmatique et non académique.

La différence entre un terroriste et un architecte ? On peut négocier avec un terroriste.

À quand une charte de qualité environnement pour les projets informatiques ? Il faut exiger une consommation de ressource minimum dans les appels d'offres. Valoriser les améliorations du code permettant de libérer un des serveur du parc informatique et contribuer à sauver la planète.

Parce qu'y'en a marre de la médiocrité, des développeurs en batteries et de la disparition des petits artisans soucieux du travail bien fait.

1 commentaire:

  1. Ta colère est juste, JP.
    Mais le sauvetage de la planète avec un serveur c'est un argument foireux.

    Caerbannog.

    RépondreSupprimer