Spring Roo est un outil de création d’application rapide proposé par Ben Alex, l’un des contributeurs de la société SpringSource. Avec une utilisation d’AspectJ très poussée, c’est un environnement de génération rapide, qui propose de construire rapidement une appication Web. Premières impressions et premières réflexions, c’est parti !
Spring Roo est distribué dans Spring STS ou sous la forme d’une archive à installer. Après avoir ajouté le répertoire bin de Spring Roo à votre PATH, vous pouvez alors le lancer en ligne de commande. Celui-ci démarre et propose une interface type terminal toute simple. Imaginez un terminal Linux, où des commandes shells vous permettent de générer le squelette de votre application MVC, vous y êtes. Le code généré est du Java et des fichiers .aj pour la partie Aspect. Les fichiers sont organisés autour d’un projet Maven, ce qui permet de travailler avec son IDE sans problèmes.
L’utilisation de Roo est simple. J’ai trouvé que l’outil en ligne de commande était bien fait. Plus simple à prendre en main que Grails pour les débutants, il vous faudra quelques minutes à peine pour comprendre son utilisation.
Si vous lancez d’ailleurs votre éditeur Java, et que vous laissez Spring Roo en tâche de fond, celui-ci surveille le code source de votre projet. La programmation « roundtrip » est donc possible, modulo le fait que vous soyez obligé de relancer votre serveur web dès que vous modifiez la structure Java. Des annotations placées dans le code permettent de générer des Aspects. Roo utilise AspectJ.
Voici par exemple une class générée avec Roo :
package org.letouilleur.test.domain; import javax.persistence.Entity; import org.springframework.roo.addon.javabean.RooJavaBean; import org.springframework.roo.addon.tostring.RooToString; import org.springframework.roo.addon.entity.RooEntity; import javax.validation.constraints.NotNull; @Entity @RooJavaBean @RooToString @RooEntity public class JobPost { @NotNull private String title; private String instructions; }
Les annotations de Roo sont avec une rétention de type Source. Ce sont donc des annotations qui ne sont utilisées que lors de l’écriture du code dans votre IDE, ou donc via Roo. Cela a pour conséquence que Roo n’est pas une librairie ou un framework à l’exécution. C’est le socle Spring MVC et Webflow, sur une couche JPA ou autre, bref que du classique. Il n’y a pas de dépendances à l’exécution. A part du code source différent, le résultat est donc le même.
Chaque annotation commence par @Roo ce qui permet de les identifier rapidement. De long débat sur l’utilité d’avoir un aspect pour ce qui est toString animent la blogosphère… Moi je dis pourquoi pas ?
Chaque annotation génère un aspect dans un fichier .aj au même niveau que votre classe.
L’annotation @RooToString génère par exemple cet aspect dans un fichier JobPost_Roo_ToString.aj :
// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. // You may push code into the target .java compilation unit if you wish to edit any member(s). package org.letouilleur.test.domain; import java.lang.String; privileged aspect JobPost_Roo_ToString { public String JobPost.toString() { StringBuilder sb = new StringBuilder(); sb.append("Id: ").append(getId()).append(", "); sb.append("Version: ").append(getVersion()).append(", "); sb.append("Title: ").append(getTitle()).append(", "); sb.append("Instructions: ").append(getInstructions()); return sb.toString(); } }
Finalement, chaque « concern » de mon entité est stocké sous la forme d’un Aspect, au même niveau que celui-ci.
Pas de DAO
Spring Roo suit la même philosophie que Play! Framework, à savoir utiliser le pattern Active Record. Il n’y a pas de DAO, et une explication bien fournie dans la documentation vous explique pourquoi. Encore une fois, c’est plus une approche de développement qu’une histoire de bien ou pas bien.
En quelques mots, comme je vous l’avais montré avec JPA, Spring Roo prend une approche où l’entité n’est pas anémique, mais riche. Chaque entité est capable d’interagir avec la base de données. Vous n’avez pas de DAO, ni de services. Le Controller effectue essentiellement un passage d’argument vers l’entité, qui est celle qui fonctionne avec la base de données.
Pour cela, Roo place dans un aspect tout ce qui est lié à la gestion de la Persistance. Cela permet d’ajouter des finders dynamiques comme avec Grails, chose qui n’est pas possible avec Play! Framework.
Regardez le code de l’aspect JobPost_Roo_Entity.aj :
// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO. // You may push code into the target .java compilation unit if you wish to edit any member(s). package org.letouilleur.test.domain; import java.lang.Integer; import java.lang.Long; import java.lang.SuppressWarnings; import java.util.List; import javax.persistence.Column; import javax.persistence.EntityManager; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.PersistenceContext; import javax.persistence.Version; import org.letouilleur.test.domain.JobPost; import org.springframework.transaction.annotation.Transactional; privileged aspect JobPost_Roo_Entity { @PersistenceContext transient EntityManager JobPost.entityManager; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long JobPost.id; @Version @Column(name = "version") private Integer JobPost.version; public Long JobPost.getId() { return this.id; } public void JobPost.setId(Long id) { this.id = id; } public Integer JobPost.getVersion() { return this.version; } public void JobPost.setVersion(Integer version) { this.version = version; } @Transactional public void JobPost.persist() { if (this.entityManager == null) this.entityManager = entityManager(); this.entityManager.persist(this); } @Transactional public void JobPost.remove() { if (this.entityManager == null) this.entityManager = entityManager(); if (this.entityManager.contains(this)) { this.entityManager.remove(this); } else { JobPost attached = this.entityManager.find(this.getClass(), this.id); this.entityManager.remove(attached); } } @Transactional public void JobPost.flush() { if (this.entityManager == null) this.entityManager = entityManager(); this.entityManager.flush(); } @Transactional public JobPost JobPost.merge() { if (this.entityManager == null) this.entityManager = entityManager(); JobPost merged = this.entityManager.merge(this); this.entityManager.flush(); return merged; } public static final EntityManager JobPost.entityManager() { EntityManager em = new JobPost().entityManager; if (em == null) throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)"); return em; } public static long JobPost.countJobPosts() { return ((Number) entityManager().createQuery("select count(o) from JobPost o").getSingleResult()).longValue(); } @SuppressWarnings("unchecked") public static List<JobPost> JobPost.findAllJobPosts() { return entityManager().createQuery("select o from JobPost o").getResultList(); } public static JobPost JobPost.findJobPost(Long id) { if (id == null) return null; return entityManager().find(JobPost.class, id); } @SuppressWarnings("unchecked") public static List<JobPost> JobPost.findJobPostEntries(int firstResult, int maxResults) { return entityManager().createQuery("select o from JobPost o").setFirstResult(firstResult).setMaxResults(maxResults).getResultList(); } }
L’aspect est ajouté sur la classe JobPost. J’ai testé la completion dans SpringSource STS et ça marche plutôt bien. C’est du Grails, avec un typage fort pratique et agréable à utiliser. Avec un Mac, j’ai un peu galéré avec l’outil intégré dans Roo, le Ctrl-Espace ou Pomme-Espace pour la complétion dans STS n’est pas utilisable. Je trouve qu’il est plus pratique de travailler en ligne de commande dans un xterm. Sur Windows par contre cela marche bien.
Le controleur généré pour lister l’ensemble de mes offres d’emplois est simple :
package org.letouilleur.testroo.web; import org.springframework.roo.addon.web.mvc.controller.RooWebScaffold; import org.letouilleur.testroo.domain.JobPost; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.stereotype.Controller; @RooWebScaffold(path = "jobposts", formBackingObject = JobPost.class) @RequestMapping("/jobposts") @Controller public class JobPostController { }
J’ai essayé d’ajouter un field sur une Entité sans relancer Tomcat, mais cela ne marche pas. Par rapport à Play! Framework vous devez relancer votre serveur. J’ai essayé plusieurs fois, mais je pense que la partie Aspect redemande une compilation. Nous parlons d’annotations avec une rétention au niveau source, cela semble logique.
Des modules ? des plugins ?
Spring Roo permet de mettre en place des aspects comme la sécurité, l’indexation avec SolarR des entités, ou même l’envoi d’un email. Il est aussi possible de développer ses propres outils par exemple.
L’intégration avec Google GWT et Google App Engine est particulièrement poussée. De même que l’outil Cloud Foundry, qui appartient à SpringSource et qui permet de gérer facilement la plate-forme de cloud d’Amazon.
Ce que j’ai aimé
Le contrat est rempli : en quelques minutes vous générez une application web sur un socle Spring, avec une approche très Domain Driven Design qui me plaît beaucoup. Bye bye DAO et Service, c’est une approche plus Web que j’aime bien. La mise en place d’un projet complet avec Maven est bien pensée. Le fait que ce soit du Java est un plus par rapport à Grails pour ceux qui ne connaissent pas encore Groovy.
Dans le cadre de la réalisation d’une application Web, c’est un choix à regarder. Je n’ai pas creusé ensuite l’implémentation dans la partie Controller. L’Add-on Web MVC de Spring Roo génère l’ensemble des méthodes type CRUD. Par contre vous ne les voyez pas dans le code de votre controleur. Ce sont des Aspects qui sont ajoutés ensuite dans la classe finale.
Spring Roo et Play! Framework font tous les deux de l’enrichissement de vos classes. Roo utilise des Aspects, Play! utilise le compilateur incrémental JDT d’Eclipse (pas l’IDE). L’avantage de Play! est qu’il déclare des méthodes dans les classes comme JPASupport ou Model, que vous étendez ensuite dans votre entité. Ceci permet d’avoir de la complétion lorsque vous codez. Votre IDE connaît les méthodes et vous ne perdez pas le typage fort. Ces méthodes sont implémentées lors de la compilation, qui est fait par Play! au lieu d’être fait via Maven ou votre IDE.
La liste des Add-On est déjà bien avancée, et nous n’en sommes qu’à la version Spring Roo 1.0.0.M3. Il y a une bonne communauté déjà semble-t-il.
Un point fort c’est que Roo ne vous locke pas, puisque l’essentiel de l’outil est constitué d’annotations, d’aspect et d’un outil qui surveille votre arborescence de code.
Ce que j’ai moins aimé
Le support dans IDEA IntelliJ est pas top. Lorsque vous codez des pages JSF par exemple, pas de complétion possible avec les actions de votre controleur lorsque vous référencez votre bean. Ce n’est pas la fin du monde, mais on perd du temps. Le support dans l’outil SpringSource STS est bien meilleur. Je vous conseille de le tester et de laisser tomber IDEA IntelliJ ou NetBeans avec Spring Roo pour l’instant.
A priori le support des Aspects dans IDEA 9 avec la version 1.0.0.M3 semble fonctionner. Cela vous permet par exemple d’avoir la complétion des getters/setters sur votre entité au sein du code de votre Controller. Mes derniers essais avec IDEA 9.0.3 professional edition n’était pas concluant, car cela ne marchait pas. Je testerai à nouveau, car sans cette fonctionnalité, et bien Spring Roo perd de son intérêt.
Ne vous amusez pas à changer la structure des packages, ou à essayer de déplacer vos entités dans un autre projet Maven. Comme Grails et comme Play! Framework, il faut accepter un certain cadre pour que l’outil fonctionne. Donc si vous êtes un ayatollah de l’architecture, ben ce sera pas trop votre ami. C’est le même souci avec tous ces outils comme Play! ou Grails.
J’ai un mauvais souvenir de Grails et de la partie CRUD, où finalement j’avais terminé par faire mes pages, mes actions et mes tags. J’ai un mauvais souvenir du module CRUD de Play! qui ne couvre pas mes besoins, et qui n’est pas fait pour être surchargé (encore moins que Grails).
Donc forcément, je n’ai pas mis le nez dans les JSPx, mais je sens que pour faire une vraie application la partie CRUD ira aussi à la benne. On s’en sert pour montrer les bonnes pratiques, mais pour faire une vraie application ? Je me base sur l’exemple de la Pizzeria de la doc de Roo pour appuyer cet argument : j’y crois pas sur une application réelle sur le Web (…genre un jobboard où tu as bossé 5 mois dessus tu vois…)
Ce que je n’ai pas testé
J’ai reposé le crayon après avoir généré mon squelette de code, et je n’ai pas tellement testé la construction d’un vrai projet (je vous le dis honnêtement). J’aime bien essayer de gérer les relations OneToMany/OneToOne/ManyToMany et voir comment s’en sort le framework. Grails avec GORM est particulièrement bon, cette partie est mature. Chez Play! Framework c’est JPA, donc le framework ne fait rien de plus que ce que vous feriez par ailleurs. Ce sera à vous de coder cette partie. Côté Spring Roo, vous pouvez déclarer des relations entre vos entités, et Roo vous aide à les mettre en place. A voir ensuite du côté de la vue comment cela fonctionne.
Je n’ai pas testé la partie tests unitaires et tests d’intégrations avec Selenium, qui me semble bien fait. Cela fait bizarre de faire tester par Roo du code qu’il a lui-même écrit…
Enfin à priori l’outil ne générera pas de code avec des beans Java EE 6 ou CDI. Par contre la partie JPA fonctionne très bien.
Conclusion
Spring Roo est un outil de productivité, qui a le mérite de proposer de bonnes pratiques, et un socle purement basé sur Spring. Pour écrire une application avec un noyau Spring franchement essayez-le. Simple à mettre en place, l’essai ne vous coûte pas cher.
Dans les demandes des utilisateurs, une demande ouverte en 2009 et qui est en cours de traitement porte sur la reprise des JSP modifiées par Spring Roo (voir ROO-8). C’est quelque chose qui est bien traité dans Grails, et je trouve que Roo s’aventure sur un terrain où il y a déjà 2 ans de travail. Grails est vraiment une belle machine, et il tourne bien.
Prenez le temps de tester et de vous faire votre propre opinion, c’est un bon outil.
Références
– Lisez la documentation de référence, la philosophie de l’outil est bien expliqué.
– Le site d’AspectJ
– Why Spring Roo is not my thing ? Bon article sur le blog GridShore
– Spring Roo sucks ! : les arguments sont un peu léger, et il y a un peu de mauvaise foi.
– Spring Introduction in 10mn sur le site d’InfoQ
– Spring Roo : the answer to rapide development ? sur DZone
Merci pour ce retour sur ROO, j’avais essayé il y a quelques temps mais j’avais été rebuté par l’omniprésence du code aspectJ…
J’ai découvert cet outil il y a quelques temps, et c’est vrai qu’il facilite grandement la productivité de développement des projets sur lesquels je travaille. En particulier, j’apprécie énormément la génération d’un projet déjà au format Maven, et je confirme que SpringSource STS est l’outil le mieux adapté pour exploiter l’outil. Ensuite, s’agissant d’un environnement Eclipse, on peut toujours ajouter les plugins additionnels dont on a besoin (Git, par exemple).
Je n’ai pas eu l’occasion de le comparer avec Grails ou Play!, les normes de développement étant assez rigides sur la normalisation des projets avec Spring + Hibernate par ici…
Pour une application classique, la génération CRUD vers Spring MVC assez bien fichue, et facilitera la création des formulaires et des écrans de consultation pour les entités usuelles (utilisateur, dossier, etc.).
Dès qu’on aura des besoins métiers spécifiques, on ne coupe pas à la retouche du contrôleur et des vues, mais dans pas mal de cas, la génération CRUD est un point de départ intéressant.
Enfin, point intéressant, les développements de Spring Roo s’orientent actuellement vers une intégration de GWT pour la génération CRUD.
Je trouve ça marrant cette mode « anti-DAO » alors qu’en réalité, c’est pareil. Dans Roo, la DAO est incluse dans le bean. On fait ça depuis des années, mais pourquoi pas faire de DAO ? Franchement, j’ai du mal à saisir. Le code est le même, juste il suffit que ce soit un Singleton et qu’il soit injecter. Soit quelque chose fait automatiquement. Quel est le gain ?
La quantité de code est la même modulo la déclaration de la classe DAO, le reste est identique sauf qu’on gagne tout simplement en lisibilité et en élégance. Je veux bien qu’on en tienne pas compte, mais en mettant tout dans le bean on gagne juste rien, ou 2 ou 3 lignes de code au plus.
+1 avec waddle
On veux à tout prix éviter les modèles anémiques, au prix de la lisibilité et de l’ajout de surcouches techniques comme l’aspectisation (attention, je suis pas contre les aspects hein…)
Tout ça au nom du Domain Driven ?
Pour éviter de mettre une couche software, on ajoute une couche technique ? Ou est l’intérêt ?
Si y’en a marre des POJO, yaka utiliser des maps (f.ck the typesafe 🙂 )
Le fait que Roo semble effectivement lorgner vers GWT aiguise par contre mon intérêt !
Toute petite remarque (Bernard Pivot vient de me le signaler)
« Mes derniers essais avec IDEA 9.0.3 professional edition n’était pas concluant »
devrait être écrit
« Mes derniers essais avec IDEA 9.0.3 professional edition n’étaient pas concluants »
voilà 🙂
Salut Nicolas,
merci pour ce bel article.
Quelques compléments d’infos au passage :
– Pour le rechargement à chaud : on bosse actuellement sur des fonctionnalités pour que les beans Spring puissent être rechargés sans qu’on ait à redémarrer le serveur Web. Ca fera partie de Spring 3.1 et sera donc tout naturellement intégré dans Roo.
– Pour les pages JSP, tu peux déjà faire du round-trip engineering, c’est à dire que dans une même page JSP, tu peux modifier une partie du code tout en laissant à Roo le soin de garantir que le reste est mis à jour automatiquement en cas de modifications. Ca utilise un système d’id générés, un peu comme le roundtrip engineering en UML (on aime ou pas, mais ça a le mérite d’exister).
– Effectivement, il n’y a pas encore de support pour IntelliJ.
Je vais faire un mail pour le problème de la complétion sous Mac. Ca devrait être facile à corriger :).
Michael.
@Waddle
Tu trouve le pattern DAO élégant ?
Beaucoup considèrent que c’est plutôt un anti-pattern.
Pour faire simple, LE principe de la programmation orienté objet est de grouper les données et les méthodes (en opposition a la programmation fonctionnelle par exemple). Le principe des DAO est exactement l’inverse, c’est une violations du principe même de l’orienté objet. Il y a d’autres raison, mais celle la est je pense la plus importante, cherche « Anemic Domain Model » dans google pour plus d’infos.
En réalité, le pattern DAO à une utilité dans certains cas, mais la plupart du temps il est inutile.
Il faut éviter les approches simplistes du genre « Il faut toujours / jamais faire de DAO », ça dépend vraiment de tes besoins, comme tous les patterns.
Pour revenir au sujet, je ne suis pas sur que la comparaison en ROO et grails / play soit vraiment pertinente.
Roo ne fait que générer le code, faire un outil équivalent pour Play! serait possible, mais ce n’est pas dans le philosophie du framework (l’idée est plutôt de faire en sorte que le code soit simple a écrire et a maintenir, plutôt que de générer du code complexe).
Julien.
@jto
Sans trouver que le DAO soit élégant, dire que ce soit un anti-pattern, c’est pousser un peu.
A l’époque c’était un pattern quasi incontournable, mais l’évolution des méthodes et frameworks
en on fait quelque chose d’un peu lourd à utiliser certes, mais qui ne va pas à l’encontre du but recherché (définition d’un anti-pattern).
En plus, c’est très contextuel.
Idéalement, et avec les technos de mon choix, j’aurais tendance a être plus Domain Driven et rapprocher la couche de persistance du modèle (sans pour autant l’intégrer complètement). Mais le choix n’est pas toujours possible, et le DAO reste encore TRES utilisé.
SVP, charité pour les anciens design patterns !
🙂
@jto
Je le considère pas comme particulièrement élégant, mais ce que je trouve peu élégant c’est remplir un objet avec des méthodes de classe de partout, sans compter le fait que ce n’est pas à un objet représentant le métier à savoir se persister (EJB style inside). Ca, ça va à l’encontre de la POO selon moi.
De plus, comme je l’ai dis, l’effort est le même entre écrire ces méthodes dans une classe dédiée ou dans le bean puisque tout le cablage est automatique.
Effectivement, a une époque pas si lointaine c’était un pattern incontournable.
Pourquoi ? Parce qu’a cette époque la DAO était nécessaire. Si par exemple tu compte sérialiser tes beans pour les balader entre des JVM sur des machines différentes, il est évident que tu ne veux pas te trimballer toute la couche de persistance avec. Avec des framework « share nothing » ces problématiques ne sont pas présentes. Pourquoi devrais je utiliser un pattern devenu inutile?
« on fait quelque chose d’un peu lourd à utiliser certes, mais qui ne va pas à l’encontre du but recherché (définition d’un anti-pattern). »
Humf, bon je suis peut être extrémiste, mais si c’est inutilement lourds, c’est qu’il y’a des lignes de code en trop, plus ya de ligne de code, plus ya de bugs, et les bug vont a l’encontre du but rechercher (a savoir faire une appli qui fonctionne).
De plus, la plupart du temps il va y avoir 2 choses dans les beans
– Des données qu’on persistera d’une manière ou d’une autre
– Les méthodes pour persister / requête les données
Dans le pattern DAO tu sépare ces 2 choses, donc tu te retrouve avec d’un coté une classe avec seulement des données (youhou on a inventé les structure du C) et d’un autre coté une classe avec seulement de méthodes (youhou des fonctions o/). Ca n’est plus du java, c’est du C avec un GC.
Il faut aussi penser que le pattern DAO ne parait pas plus compliqué, parce que les framework d’ORM comme Hibernate cachent une bonne partie des problèmes qu’il amène.
Par exemple supposons que je creer un objet A qui contient une liste de B. Lorsque je reccupere une instance de A, je ne vais pas récupérer les instances de B associées, mais plutôt attendre d’en avoir besoin (lazy loading). Avec hibernate, c’est facile, je fais un a.getBs() qui va automagiquement faire une nouvelle request sur ma base de données. DAO ou pas c’est pareil du point de vu du DEV.
Mais supposons que je n’utilise pas Hibernate, comment traiter se problème ?
Solution 1 (sans DAO):
Dans mon getter, je vérifie que la List de B n’est pas null, si elle l’est je fais une requête et je renvois le résultat, et le stocke dans mon instance.
Tout est transparent pour le dev. Le comportement est exactement le même qu’avec hibernate.
Solution 2 (avec DAO):
En appelant de getter, je me prend un beau null, je doit gérer dans tout les appel au getter cette possibilité,
et le cas écheant rappeler explicitement la bonne DAO.
Laquelle de ces 2 solutions est la plus susceptible de merder selon vous ?
Cela doit être le 3eme sujet qui tend à montrer l’intérêt de mélanger les genres, entités et accès aux données.
Une fois de plus merci Nicolas pour cet article qui nous permet d’appréhender les fwks.
Ceci dit, et pour la 3eme fois donc je suis choqué devant tant de répétition sur l’ajout d’accès aux données depuis les entités, à force de le lire, ça pourrait devenir une mode aux conséquences négatives, ce n’est que mon avis.
J’ai tenté de poser qques questions dans un sujet précédent, soulever les limites très vite atteintes par ce pattern, aucune réponse, aucun débat. Certes c’est chouette pour un POC, ça *semble* plus rapide mais je n’y crois pas un instant pour avoir travaillé pendant un an avec un FWK de persistance custom qui exploitait ce principe.
On nous parle de Anemic domain model, la référence de ce travers dans lequel il ne faut pas tomber est Martin fowler, voir: http://martinfowler.com/bliki/AnemicDomainModel.html.
« It’s also worth emphasizing that putting behavior into the domain objects should not contradict the solid approach of using layering to separate domain logic from such things as persistence and presentation responsibilities. »
Lorsque l’on parle de modèle anémique, on parle d’un modèle anémique côté business et non côté technique car oui un objet du domain model c’est un ensemble de données (les champs, PAS l’ACCES TECHNIQUE aux données) et de comportements (méthodes) mais des comportements métier avec comme tout premier niveau l’implémentation des accesseurs qui, je te cite Nicolas « ne semblent plus à la mode ».
Je repose donc l’une de mes questions qui est restée sans réponse dans un sujet précédent: j’ai une super application mega complexe composée de … deux entités: Commande et LigneCommande avec un tout petit OneToMany entre les 2. Une commande ne peut être crée que si elle a des lignes, sinon elle sert a rien… de même il est inutile de sauvegarder les lignes si la commande n’arrive à être sauvegardée. Question 1: de quoi j’ai besoin? D’une transaction pour sauver le tout ou rien
Question 2: si mes entités sont capables de se rendre persistantes en bdd, qui de Commande ou LigneCommande aura la __responsabilité__ de gérer la transaction globale ? Ca commence déjà a plomber la lisibilité et le choix de l’endroit ou implémenter les choses. Imaginez avec un modèle de 10 classes, je ne parle meme pas de 100.
Note 1: modele anemique ? La premiere des choses a faire est d’avoir un constructeur dans LigneCommande qui FORCE a passer une commande.
Note 2: modèle anémique ? Méthode business type: maCommande.getTotalCommande() qui itère dans la collection des lignes de commande pour calculer le total… puis qui pourra traverse le futur object Avoir pour en retrancher son montant ce n’est qu’une idée… tout dépend des specs.
Note 3: la méthode maCommande.pay() qui traversera le futur objet client pour connaitre son potentiel status de mauvais payeur lui interdisant tout crédit, la je debloque mais elles sont là les méthodes qui évitent un modèle d’être anémique.
Bien a vous, bibi
oups, jto a dégainé avant moi
+1 sur ses arguments
@jto
D’accord avec toi sur l’anémisation des POJO, et le fait de ne plus utiliser les paradigmes (oui, je l’ai placé) objets, à savoir encapsuler au même endroit comportement ET données.
C’est pour ça que le Domain Driven connaît un tel engouement en ce moment, c’est une optimisation « naturelle » et structurelle.
Ce que je veux dire, c’est que c’est une question de choix.
J’ai codé des centaines de DAO, et c’est vraiment lourd. Sur des projets perso, à 90% je suis Domain Driven.
Mais tu n’es pas toujours maître de l’architecture cible.
Et respecter un existant, ça évite l’anti-pattern absolu à mon gout: l’expérimentation permanente, du style « oh-le-zoli-framework-tout-neuf-que-je-vais-essayer-de-placer-parce-que-j’ai-envie-de-m’amuser-avec »
@Ikarius
Je suis complètement d’accord avec toi ^^
Nicolas, une petite précision : j’ai eu un retour de l’équipe Roo, et apparemment le ctrl + space marche bien sous Mac (l’un des committers Roo est sur Mac).
Tu es sûr que ça ne marche pas chez toi ? Si oui, ça serait bien que tu ouvres un ticket avec les versions exactes de Roo et de STS que tu utilises.
Michael.
@jto
« Dans le pattern DAO tu sépare ces 2 choses, donc tu te retrouve avec d’un coté une classe avec seulement des données (youhou on a inventé les structure du C) et d’un autre coté une classe avec seulement de méthodes (youhou des fonctions o/). Ca n’est plus du java, c’est du C avec un GC. »
Un objet contient ses infos, pourquoi plus ? Les DAO ne contiennent pas des fonctions, ce sont des Singletons. Mettre des méthodes de classes dans le bean, ça c’est faire des fonctions.
« Avec des framework « share nothing » ces problématiques ne sont pas présentes. Pourquoi devrais je utiliser un pattern devenu inutile? »
et
« Il faut aussi penser que le pattern DAO ne parait pas plus compliqué, parce que les framework d’ORM comme Hibernate cachent une bonne partie des problèmes qu’il amène. »
bah voilà, Hibernate permet de ne pas s’en préoccuper. Il faut toujours tenir compte de l’existant dans ton propos, pas uniquement quand cela le sert.
« c’est qu’il y’a des lignes de code en trop, plus ya de ligne de code, plus ya de bugs »
Mais quelles lignes de plus ? Je veux bien les compter…3 par bean :
– déclaration du package de la DAO
– déclaration du la class DAO
– attribut de type DAO quelque part
– (on compte l’accolade fermante chef ?)
Mais tant qu’à faire comme chez le marchand de ligne, je peux aussi te dire que 90% du code des DAO peut être regrouper dans une GenericDAO (avec des generics comme son nom l’indique) et que là, j’économise des centaines de lignes par rapport au cas ou je n’ai pas de DAO. Sauf à faire hériter tes beans d’une classe se chargeant de regrouper le code de la même façon, mais ça devient de plus en plus intrisif, et ce ne sont plus des méthodes de classe mais d’instance.
Je finirais en disant que le nombre de lignes importe peu en réalité, et qu’il n’y a pas de lien direct entre ce nombre et le nombre de bugs, linéaire ou non, « car la seul ligne qui ne plante pas, c’est celle qu’on écrit pas ».
Waddle tu pourrais repondre a la question que j’ai soulevée? Comment organises tu ton code niveau d’une simple petite transaction avec un tel pattern?
Par ailleurs je suis allé répondre a tes interrogations sur JPA sur ton blog.
« Un objet contient ses infos, pourquoi plus »
Hum, info est un peu vague. Qu’entend tu par la?
« Les DAO ne contiennent pas des fonctions, ce sont des Singletons »
Le fait de tout mettre dans un Singleton ne change rien, çà reste toujours des fonctions, par définition un objet à un état, tes DAO n’en n’ont pas.
« bah voilà, Hibernate permet de ne pas s’en préoccuper »
Oui, mais hibernate n’est pas toujours la…
De plus Hibernate te cache la complexité, mais elle est toujours la.
Si tu fais de la reflextion sur des beans chargés depuis Hibernate, tu va voir apparaitre des « Proxy » a la place de List. Pas cool…
« Mais tant qu’à faire comme chez le marchand de ligne, […] »
Tu simplifie un peu trop mon propos la 😉
@Patricio
Je ne vois pas ta question. Par ailleurs, merci beaucoup pour ta réponse sur mon blog, j’en suis honoré 🙂
@Tous … bon et sinon Spring Roo vous vous en fichez ? Tiens, regardez la librairie Hades pour JPA et DAO, cela devrait vous intéresser. Il y a eu un talk à Devoxx 2009 sur ce sujet, c’était intéressant.
@Michael : merci pour ton retour. Je ne dis pas que cela marche pas. Je dis que c’est pas super utilisable par rapport à la version en ligne de commande. C’est un argument discutable je te l’accorde.
@jto
« Hum, info est un peu vague. Qu’entend tu par la? »
Je voulais dire qu’un bean porte son information, sa persistence n’est pas de son ressort selon moi.
« Le fait de tout mettre dans un Singleton ne change rien, çà reste toujours des fonctions, par définition un objet à un état, tes DAO n’en n’ont pas. »
Oui, ce n’est peut-être qu’un artifice, je te l’accorde, mais ca reste plus zolie que des méthodes de classes dans le bean, non ? Moi elles me piquent les yeux XD
« Tu simplifie un peu trop mon propos la 😉 »
J’avoue mon crime ! Tentative de lancement de Troll désarmorcée 🙂
@patricio
Après une bonne nuit de sommeil j ai retrouvé ta question 🙂 et la réponse est : avec une DAO, c est ce que je dis depuis le début 🙂
@patricio
« Question 1: de quoi j’ai besoin? D’une transaction pour sauver le tout ou rien »
Je suppose que tu parle d’une transaction au niveau Base de donnée right ?
Hum dans le contexte d’une appli web, la création de Commande et des LigneCommande vont probablement se faire sur plusieurs pages, et donc sur plusieurs requêtes HTTP. Maintenir une transaction va être super compliqué, surtout su tu as plusieurs serveurs web.
Plutôt que de faire une grosse transaction qui sauve tout, je me contenterais de sauver la commande dans son état transitoire, et de mettre une méthode dans mon objet qui me dit si la commande est valide ou pas. Eventuellement, je pourais juste me contenter de garder la commande dans un cache, en attente de lignes. Grace à cela, je peux gerer le reste de l’appli sans trop m’en soucier. Cette approche est plus dans l’esprit du protocol HTTP, ou il n’y a pas d’état entre deux requêtes, et donc, à priori pas de transaction maintenue.
Si tu ne veux vraiment pas sauver une commande invalide, le principal problème est qu’il est vraiment difficile de séparer le métier (a savoir une Commande « vide » est invalide) du technique (par quelle méthode je sauve ma commande valide).
La solution du constructeur n’est malheureusement pas forcement applicable, car il me semble qu’hibernate t’oblige a fournir un constructeur sans argument pour tes modèles.
Si tu laisse la couche de persistance dans ton modèle, le problème se résout très simplement, il suffit de vérifier dans ta méthode « save() » que l’objet est valide, et par exemple de lancer une exception dans le cas contraire.
Si tu utilise une DAO c’est déjà plus compliqué, la solution qui me vient a l’esprit serait de faire une méthode isValid() dans le modèle, et de faire en sorte que ta DAO l’appel avant de sauver. Mais c’est déjà une intrusion du code métier dans la DAO, or justement en créant cette DAO, tu voulais séparer le métier de la technique.
Bref, comme tu peux le voir il y’a des solutions, ce n’est pas tout du compliqué ^^
jto
@Nicolas Martignole
Je crois que tu tiens un sujet pour un prochain billet la ^^
@jto
je parle d’une transaction ‘metier’ bien trop souvent réduite à celle de la bdd.
Donc pour l’appli web, il peut y avoir typiquement un wizard, tu n’es aucunement forcé de sauver a chaque htttRequest, par micro transaction ‘à la autocommit mode’. Comme tu dis tu peux gérer un *cache de use case*. Certains utilisent la bonne vieille httpSession en gérant les risques associés. D’autre utilisent la conversation Seam, qui même si elle associe un contexte de persistence étendu (entity manager), ne laisse bien évidemment pas ni la transaction bdd sous-jacente, ni la connexion jdbc ouvertes.
Bref oublions les choix d’implémentation, au final il te faudra bien *qqch* qui fasse
{
beginTx();
entityManager.persist(order);
// pour toutes les lignes
entityManager.persist(lineOrder);
somethingInMyModel.validOrder();
commitTx();
}
catch OrderNotValidException…
blahblah aussi bien mon exemple (tes arguments sont exacts pour la pluppart) que l’impl sont discutables mais le begin et commit sont a mon sens difficilement dispensables.
Sur cette proposition le DAO est l’entity manager et la méthode décrite peut se trouver ou vous voulez (typiquement un stateful session bean pour en faire bondir qquns 🙂 ).
Je ne suis pas un puriste des multicouches ni même de EE. Je suis plutôt contre le stéréotypage drivé par la technique mais bien plus partisan d’un stéréotypage par responsabilité, d’où mon intérêt énorme pour Seam qui ne fixe aucun type de couche mais permet d’appliquer certaines responsabilités récurrentes à n’importe quel objet, et surtout se focalise sur la notion de contexte, indispensable a mon sens pour aller bien au delà des limites sémantiques qui découlent de http . L’important pour moi est de pouvoir faire évoluer une appli dans le temps et ne pas se focaliser sur la simplicité/rapidité d’implémentation de la page d’accueil.
@Nicolas
désolé, il est vrai que les commentaires dévient du sujet principal abordé initialement mais personnellement et avec tout le respect que j’ai pour toi, il est des phrases dites ‘comme si c’était évident’ qui me font parfois bondir. Ce n’est QUE mon avis, et je ne fais QU’ argumenter. Maintenant soit sûr que ton sujet est bookmarqué, m’a personnellement permis d’avoir une petite idée de Roo et que je saurai le relire si j’ai l’occasion de travailler avec cet outil. Merci à toi.
Au temps pour moi, j’avais l’impression que tu parlais des tx BDD mais non 😉
Au final nos opinions ne divergent pas tant que ça 😉
La choses chose qui me fait un peu réagir, je ne considère pas les limitations de HTTP comme une faiblesse, mais au contraire comme une force (ces limitations ont bcp d’avantages). Du coup je ne suis pas trop fan des conversations de seam (puisque qu’on passe dans un mode statefull) mais si ça te va, c’est ton choix.
Evidemment, quoi que tu fasse il n’y à pas de solution miracle, le métier de développeur est encore un métier « artisanal » et tout se fait au cas par cas. A chacun de trouver la solution adaptée à son application.
Au passage, je pense que le « débat » DAO / pas DAO se pose dans Java car il n’existe pas dans ce langage de système de module / trait comme dans Scala ou Ruby. On pourrait imaginer mettre la couche de persistance dans un trait, mixé avec le bean. De cette manière on a les avantages des 2 approches sans les inconvénients.
jto.
@jto
hum, certaines pratiques allant a l’encontre des règles du jeu (je parle d’HTTP) sont souvent synonymes de problèmes de scalabilité. En ce sens je suis d’accord avec ta remarque.
Maintenant, j’ai lu plusieurs avis concernant le stateful ‘nouvelle génération’ (en opposition a SFSB 2.x). Pour le moment je n’y ai pas été sensible car par de réel inconvénients si ce n’est idéologique (et ça se respecte).
Je prends Seam, je developpe une application en l’exploitant.
Deja il ne m’impose as grand chose, il me laisse une grande liberté. Cette liberté est certe a double tranchant pour qqun qui ne maitrise pas, mais c’est vrai pour toutes les technos non?
Donc je prends seam, il m’ouvre un modèle de conception tres ouvert. Il scale au runtime (c’est ça qui est bon), ou est le soucis? Mais ce serait un autre sujet 🙂
Au dela des problèmes de scalabilité, cela ajoute de la complexité au niveau du framework (bon évidement en temps qu’utilisateur c’est pas vraiment ton problème). Par exemple la plupart des frameworks « statefull » ont eu des problèmes avec la gestions du bouton back (de mémoire, il me semble que seam ne fait pas exception).
Mais comme je l’ai dit, si seam te convient ne te prive pas 🙂
C’est fou ce que cette discussion a dérivé, quel était le sujet de départ déjà? 😉
Thanks for posting this coverage of Spring Roo.
Just in connection with the DAO layer discussion, you can read a little more about why we elected to not have a DAO layer at http://static.springsource.org/spring-roo/reference/html/architecture.html#architecture-dao. Despite our preference for no DAO layer, there have been 64 votes for Spring Roo to supports DAOs as part of Jira ticket https://jira.springframework.org/browse/ROO-301. As such we hope to add DAO layer support in Roo 1.1 or 1.2. One of the prerequisites to DAO support (and a lot of other features) is ROO-1372, which is being coded as we speak.
Best regards
Ben Alex
Project Lead, Spring Roo
Hi Ben
Personnaly I like the Active record pattern and I’m not much more in favor of DAO for Web application.
I really appreciate that you read and posted a comment on my blog. Hope to see you
at Devoxx this year. Thanks for the work on Spring Roo
Nicolas
Blog author