Architecture Dynamique basée sur la solution Grails

le 22/09/2007 par Ismael Hery
Tags: Software Engineering

Tout projet de développement implique des choix d'architecture. Quels patterns de code ? Quels outils de build ? Un projet innovant place ces question sur un axe temporel : les réponses adaptées ne sont pas les mêmes entre la 1ere itération et la 20ème itération. Une architecture "dynamique" permettra de maximiser la valeur apportée par une architecture applicative à un moment donné d'un projet.
D'abord les bleus et autres hématomes du développeur et de l'architecte d'application. Qui n'a jamais ressenti une certaine gêne en expliquant au client, au bout d'une 1ere itération de 2 semaines, que l'essentiel des stories n'est pas livré parce que " la mise en place du build a pris plus de temps que prévu " ou encore " le département intégration n'a pas livré la base Oracle de dev à temps " ?

Plus pernicieux (faut regarder sous le capot), qui n'a jamais pesté sur le temps perdu à coder des mappings objets métier->DTOs fastidieux et totalement passe plat (même propriétés du model métier au DTO) !

Il ne s'agit pas de remettre en cause les patterns et outils évoqués dans ces exemples mais plutôt de se questionner sur le timing optimal pour leur introduction dans un projet innovant. Entendons-nous un minimum sur les termes : un projet innovant signifie à minima un métier flou, non maîtrisé par la MOE (et parfois par la MOA...) tandis que dynamicité de l'architecture d'application recouvre les changements d'outis, de pratiques et de patterns au cours du projet.

Des principes à leur mise en oeuvre

Les agilistes ont introduit la notion de design incrémental qui reçoit des échos positifs des architectes d'application. Pourtant la traduction dans les choix d'architecture et les pratiques de développement sur les plateaux projets, tarde encore à se concrétiser. Les principaux axes de l'architecture dynamique sont le build, l'intégration et surtout les formes de code. Je citerai des exemples concrets sur la base du framework Grails. Au passage, je suis très preneur d'exemples équivalents sur des framework de spectre de fonctionnalités équivalents (SEAM, Rails...).

Dynamique de build

Configurer 4 projets maven dans la première itération semble parfois inepte, quand dans ces premiers jours les dépendances externes sont minimums, que chaque couche contient quelques classes seulement et qu'on a pas avancé d'un iota sur le domaine métier !

Dans le cas d'un développement sur Grails, les 1ere itérations peuvent faire l'impasse sur l'outil de build " entreprise " pour utiliser directement les scripts du framework. Les quelques dépendances de la première itération seront simplement déposées dans le répertoire " lib " de l'application. Si l'appli Grails utilise une approche " blended ", c'est-à-dire avec des appels à du code java, on mettra en place un seul et unique artifact maven.

Quels déclencheurs pour dépasser cette première " proto-archi " de build ? Le projet avançant, ce n'est plus 2 ou 3 dépendances externes mais 10, 20, qui vont s'ajouter, avec une gestion de version de plus en plus fastidieuse : c'est là que maven fait sont entrée fracassante, sous les houra de la foule en liesse.

Dans le cas des couches (des artifacts buildés) la séparation des responsabilités et la réutilisation seront les principaux facteurs poussant à l'éclatement d'une couche applicative en plusieurs.

Dynamique d'intégration

Minimiser le coût de l'intégration des premières phases a été grandement simplifié par l'outillage récent. Les vieux loups de mer du design incrémental tenterons de nous vendre la quintessence de la simplicité avec une " persistence fichier " pour les 1ères itérations : je dis tu bluffes Martoni et je demande à voir.

La facilité de mise en place et d'utilisation des bases in memory permet maintenant de ne pas avaler d'un seul coup un gros morceau indigeste du nom d'Oracle ou Sybase. Dans le cas de Grails, HSQLDB est bundlé et configuré par défaut comme datasource : coût de la mise en place = 27 secondes.

Un apport plus novateur de Grails est le script BootStrap.groovy qui permet par exemple d'injecter très rapidement des données métier au démarrage de l'application, en bénéficiant des raccourcis de GORM et en évitant de mettre en place un import SQL ou DBUNIT.

Mon graphe d'objets se complexifiant, mes volumes de données augmentant, je passerai tôt ou tard sur la techno cible, avec des bons vieux insert sql pour la reprise des données...

Dynamique de pattern de code

Cet axe de l'architecture dynamique est plus excitant encore, car invisible pour qui n'a jamais mis les yeux sur le code. Les perspectives d'amélioration de la productivité sont très importantes dans ce domaine.

Les DTOs

Maximiser le feed-back client c'est aller vite dans la livraison de fonctionnalités, en minimisant notamment l'entropie, i.e. le volume de code inutile. Dans les premières itérations il s'agit souvent de remonter jusqu'à la GUI les " proto-objets " métier pour valider une compréhension du domaine avec le client. Or quoi de plus inutiles (donc coûteux) que des DTOs passe plats, depuis qu'hibernate permet de remonter jusqu'à la GUI des objets métier ? Accessoirement, coder des DTOs passe plat est une des activités de dev les plus déprimante qui soit sur terre.

Qu'on ne me fasse pas de procès d'intention, le pattern DTO n'est pas près de disparaître. Le métier et la GUI allant en se complexifiant au cours du projet, il refera surface au premier formulaire calqué sur plusieurs objets ou alors dès que des composants GUI riches et réutilisables seront nécessaires...

Si vous utilisez Grails vous vous contenterez d'abord d'afficher les POJOs métiers dans la GUI, par exemple ${book.author.name}. Puis petit à petit vous introduirez des " command objects " pour les formulaires complexes.

Les DAOs

Hibernate facilite grandement l'accès à la base, à tel point qu'on se surprend à voir la base comme un vrai repository d'objet. L'écriture d'un DAO sur des requêtes simples conduit souvent à des classes et des méthodes anémiques, qu'on hésite à fondre dans le service métier pour limiter le nombre d'entité. Du coup exit le DAO, on mélange allègrement la persistence et la logique, le code résultant restant propre et testable tant que les requêtes restent simples.

Vous avez pigé le truc, tôt ou tard le code d'accès aux données se complexifiera et son externalisation de la logique métier deviendra inévitable. L'autre facteur de décorrélation DAO/logique métier sera la testabilité de la logique métier, grandement simplifiée si le service prend en paramètre des POJOs remontés de la base au runtime ou construit manuellement au test time.

Dans le cas de Grails une évolution typique du code d'accès aux données est la suivante :

  1. Grâce à la puissance de GORM (n'ayons pas peur des mots), je mets facilement du code d'accès à la base dans mes controlleurs Grails.
  2. La logique métier dépasse le CRUD et je l'externalise dans un service (Java ou Groovy) qui accède directement aux objets en base.
  3. Le bigniou se complexifie encore et la logique d'accès à la base et la testabilité du tout m'incite à séparer logique métier et DAO.

DTOs et DAOs sont des exemples emblématiques de ces patterns qu'ils faut savoir embrayer et débrayer au moment opportun, mais d'autres pattern sont là, à attendre un timing optimal.

Conclusion

Les principes exposés ici (architecture itérative et incrémentale) ne sont pas des scoops, loin de là. En revanches, les frameworks récents permettent de les appliquer concrètement sans trop d'effort des équipes et surtout sans rupture technologique forte au cours du temps. Le prototype des premiers jours n'est plus en PHP ou pur HTML mais en MVC, la persistence des premiers jours n'est plus en " fichier " mais en base relationnelle. On ne cassera jamais tout pour jeter un investissement conséquent.

La dynamicité de l'architecture prend en compte le facteur temporel que revendique le pattern zone innovation->zone rationalisation. En revanche le facteur temps des exemples abordés ici situe ces évolutions dans la seule zone d'innovation : ces nombreux changements doivent intervenir dans les itérations de développement seules, bien avant le passage en TMA !

Plusieurs postulats implicites n'ont pas été explicités en début de post :

- la qualité interne (invisible du client) n'est pas négociable. Les patterns " légers " des premières itérations ne doivent pas nuire à la robustesse, la lisibilité et la testabilité du code.

- l'architecture dynamique n'est pas compatible avec la cascade. Les outils et les patterns calquent leurs évolutions sur le recueil du besoin et les feed-backs client, au fur et à mesure des itérations.

- les développeurs et architectes en charge du projet sont compétents, responsabilisés et courageux.

Compétents parce qu'ils connaissent les différentes approches accessibles et la pertinence de chacunes.

Responsabilisés parce que les patterns émergent de leur activité quotidienne et qu'ils n'attendent pas les 2 heures de support hebdomadaire de l'expert pour remettre en cause leur archi.

Courageux parce qu'ils challengent en permanence leur architecture applicative en reconnaissant que les décisions d'hier ne sont plus les bonnes aujourd'hui ou que l'archi de demain équivaut à un sur coût aujourd'hui.

Gilles Laborderie et moi-même présenteront des exemples de dynamicité d'architecture pendant la conférences Grails eXchange. Voir à ce sujet aussi les autres sujets OCTO.