Accéder au contenu principal

Concevoir un système d’annotations pour l’automatisation

À la suite de l'article concernant Automatisation d'interface utilisateur, j'ai reçu différents commentaires relatifs au choix qui avait ét fait. J'ai décidé d'écrire un autre article présentant différentes conceptions, avec leurs avantages et inconvénieunts respectifs

Annotation et convention over configuration

L'approche qui avait été adoptée était basée sur une annotation par responsabilité, avec le moins de choix possible. Un maximum d'automatisation avait été mis en place.


@Order(value = 9)
@HideField(value = SectionEnum.TABLE)
@Mandatory
private Integer groundElementOwnerId;

Les annotations indiquent 

  • que le champs serait affiché à la neuvième position.
  • que le champ serait caché dans la section table de la page.
  • que le champ est obligatoire

@Order(value = 7)
@TableStruture
private Map<String,String> properties;

Ici il y avait détection de la map et un tableau était affiché.




Différentes conventions avaient été mises en place.

Concernant les select, lorsque le nom de l'attribut contenait "id" et comportait plus de deux caractères, une tentative était faite pour rechercher un service au nom du dto et de trouver une méthode s'appelant findAll. Le résultat était mis dans le modèle et pouvait être utilisé dans thymeleaf.

Lorsqu'un traitement spécifique était nécessaire pour un champ, ce dernier pouvait être masqué via l'annotation et une gestion personnalisée était alors implémentée dans un template thymeleaf.

Cette première approche avait été simple en mettre place, car il y avait peu de cas spécifique à gérer. Le développeur pouvait aisément ajouter de nouveau dto.

La gestion de la construction de la structure dynamique était assez simple malgré les quelques annotations à gérer. Peu d'attribut était disponible dans chaune des annotations.


Annotation générique

Avec cette approche, une seule annotation est définie avec l'ensemble des attributs possibles. C'est possiblement la plus simple, même si le développeur doit fournir davantage d'informations. Le principal défaut est qu'il est possible que le développeur assigne des valeurs inexistantes  pour le type spécifié. Deplus, l'annotation a plusieurs responsabilités.

@FormField(type = FormFieldType.PHONE, order = 9, pattern = "999 999-9999")
private String phone;
    
@FormField(type = FormFieldType.SELECT, optionsProvider = "getCities", order = 5 )
private String city;
    
@FormField(type = FormFieldType.CHECKBOX, options = {"sportCheck:Sport", "musiqueCheck:Musique", "lectureCheck:Lecture"}, order = 15 )
private List<String> interest;

Une seule annotation facilite la construction de la structure dynamique. Seuls les attributs existants pour un type donnée sont pris en compte.


Annotation par responsabilité

Les responsabilités sont définies via différentes annotations. Le dto devient plus verbeux, et la gestion plus complexe pour construire la structure dynamique. C'est une version avancé que la première approche.

@FormField(type = FieldType.SELECT)
@SelectConfig(valueField = "id", labelField = "name")
private List<Ville> villes;

@FormField(type = FieldType.INPUT, placeholder = "+1 (555) 123-4567")
@FormStyle(cssClass="form-control")
@FormValidation(required=true, readonly=true, pattern="'mask' : '999 999-9999'")
@Order(8)
private String phone;

Annotation générique avec avec type spécifique

Chaque type dispose de sa propre annotation avec ses propres attributs. Ainsi, le développeur ne peut pas se tromper sur les propriétés applicables.

@HiddenInput(order=1)
private Long idEditeur;

@TextInput(minLength = 2, maxLength = 20, order=2)
private String name;

@TextInput(readonly=true, order=3)
private String age;

@SelectInput(optionsProvider = "getCities", required = true, order=4)
private String city

Approche assez simple à comprendre, cependant l'attribut order bien qu'utile pour la position d'affichage n'étant pas un attribut HTML, il vient briser le concept de séparation des responsabilités. Il pourrait être mis dans une autre annotation. Parfois un juste milieu est la meilleur solution.


Une librairie a été créé: https://github.com/marccollin/formatic


Quel approche au niveau des annotations préférez-vous?



Commentaires

Posts les plus consultés de ce blog

Toshiba AC100 et Android 4

Toshiba AC100 est un smartbook. Toshiba est la seule grosse entreprise qui a commercialisé un smartbook, malgré que plusieurs autres en aient présenté. Un smartbook est similaire à un netbook (taille) en plus d'avoir des caractéristiques souvent proche à un mobile (GPS, 3g, processeur ARM, autonomie). Le Toshiba AC100 utilisait le système d'exploitation Android en version 2.1. Toshiba a effectué une mise à jour Android 2.2. Il a arrêté la commercialisation et le support de cette machine. L'appareil ayant un bon rapport qualité/prix, la communauté le prit en charge. Il est possible d'installer une version beaucoup plus récente d'Android telle que la 4.1. La rom alternative Cyanogen a été utilisée pour ce portage. Les procédures citées ont été faites sous Linux.  Sous Windows, il faut télécharger une version de nvflash pour ce système. Préparation Télécharger les fichiers: cm_ac100-ota-eng.cm10-beta2.zombah.zip recovery.ac100-cm10_beta2.img via ce...

Spring data jpa et Hibernate’s Warning “firstResult/maxResults specified with collection fetch”

Nous verrons dans cet article comment corrigé le problème  Hibernate’s Warning “firstResult/maxResults specified with collection fetch” qui peut survenir lorsqu'il y a un fetch d'effectuer et qu'il y a du paging. Nous utiliserons spring data jpa. Détail structure Imaginons une relation 1 à plusieurs Lorsqu'on obtient un parent, on désire obtenir aussi sa liste d'enfants. En sql, il y aurait une jointure à effectuer. Il est question d'une recherche avancé où on pourrait recherche par plusieurs critères. Spécification Création d'une spécification pour construire une requête dynamique @Component public class ParentSpecification {     public Specification<Parent> recherche(RechercheParent recherche) {         return (root, cq, cb) -> {             Join<Parent, enfant> enfantJoin;              List<Predicate> rootPredicates = new ArrayList<>();       ...

Migration d’une application Spring Boot 2.2 vers Spring Boot 4.0 : retour d’expérience concret

 En 2019, nous avons développé une application métier basée sur : Java 8 Spring Boot 2.2.7 Gradle 6.6.1 Thymeleaf Bootstrap 4.3.1 Environ 40 000 lignes de code Java Fin 2025, voici un retour d’expérience concret sur la migration vers : Java 25 Spring Boot 4.0.1 Gradle 9.2.1 La partie thymeleaf, n'a pas été touché, il y a quelques warning concernant l'obsolescence de certain commande tel que th:include. Une migration plus rapide que prévu Un point important : en moins de 30 minutes, l’application démarrait sur la nouvelle stack. Évidemment, "ça démarre" ne veut pas dire "tout fonctionne", mais ce premier jalon est très révélateur : la base Spring Boot reste solide les outils de migration sont matures la dette technique était maîtrisée Les changements majeurs rencontrés De javax.* à jakarta.* C’est probablement le changement le plus visible. Tous les imports javax.* doivent être migrés ve...