Des microservices en Java à la mode Hipster

Des microservices en Java à la mode Hipster

Quelques notes sur le développement d'applications avec JHipster 4 (ie. Spring Boot, Angular & pleins d'autres joyeusetés).

Très courte intro à JHipster

TL;DR; (<< je voulais le placer celui-là)
JHipster est un generateur / configurateur d'application incluant les frameworks et librairies couramment utilisés dans le développement d'application web (monololithique ou microservice).
En gros, ils nous font bénéficier de leur expérience sur la config des outils pour une architecture classique d'application web avec un backend Java et un frontend Angular (ou React à venir).

Vous vous réveillez ce matin et vous dites que vous développeriez bien un microservice. On ne va pas sortir la grosse artillerie, une classe controller pour réceptionner la requete et un service pour la traiter. Puis vient une envie de sécurité et d'authentification, puis une envie de logger et de monitoring, puis une envie de load balancing mais du coup, quand j'ajoute un microservice, je ne vais pas configurer leur adresse à la main à chaque fois donc pourquoi ne pas avoir un registre...etc et pas nécessairement dans cet ordre.
JHipster va donc vous apporter tout cet ensemble avec une config fonctionnelle et opérationnelle. Il est cependant bon de connaitre les technos sous-jacentes ou bien de les découvrir par ce biais.

Que m'apporte JHipster

J'insiste mais JHipster n'est pas un framework mais un générateur de codes qui se base sur différentes librairies et framework dont voici une liste non exhaustive, basée sur la version 4.14 :

  • Une architecture d'application monolithique ou de webservices
  • Spring Boot / MVC
  • Génération des fichiers de configuration et gestion centralisée
  • Scripts Maven & Gradle
  • Environnement de tests unitaires et de bouts en bouts avec mocks
  • Caches à différents niveaux, basés sur Hazelcast
  • Fichiers de configuration Docker prêt à l'emploi
  • Zalando problem pour génerer des retours d'erreurs plus explicites
  • Swagger pour la documentation de vos APIs
  • Auth avec JWT, Okta ou server central d'authentification
  • Gateway ou reverse proxy en bon français avec Zuul de Netflix ou Traefik
  • Service discovery avec Consul ou la stack de Netflix
  • Centralisation des logs vers du Elasticsearch et console Jhipster dédiée avec un kibana déjà configuré
  • ...

Comme vous pouvez le voir, le génrateur apporte tous ces "à cotés" que l'on peut avoir tendance à minimiser et à mal chiffrer.

Les inconvénients

Il faut aussi parler de ce qui fâche et connaitre les limites. D'un point de vue purement "philosophique", on pourrait penser que le choix est limité concernant les technologies. A mon sens, c'est un faux débat puisqu'il s'agit de technos ouvertes et rien n'empeche de remplacer ou d'utiliser ses propres technos (dans une certaine limite).

Autre point, il est facile de partir d'une feuille blanche et de commencer un nouveau projet à l'aide du générateur mais une fois que le projet est lancé et qu'il faut l'upgrader, cela peut devenir plus ou moins simple en fonction de ce que vous avez modifié. Mais vous retrouvez ce problème avec l'ensemble des frameworks comme le rappel Uncle Bob (article en anglais) puisque vous couplez nécessairement votre code.

Commencer un projet JHipster

Cette partie étant bien couverte sur leur site, je ne vais pas m'attarder là dessus mais par la suite, vous trouverez des petites astuces utiles.
Après avoir installé les dépendances (yarn, maven, git...), vous pouvez aller dans votre répertoire de projet et taper la commande jhipster.
Vous serez ensuite guidé pour la création de votre application.
Pour démarrer en développement votre application de type monolothique ou gateway, il ne faut pas oublier de lancer un yarn start ou refaire un build webpack (cf commandes ci-dessous). Des alias de commandes sont déjà définies dans le package.json. Il arrive souvent qu'en démarrant uniquement le backend on obtient une page blanche. Cela arrive quand vous n'avez pas compilé le frontend (yarn start par exemple).

Note pour une utilisation d'une version spécifique de JHipster: par exemple, JHipster v5 est sorti mais vous souhaitez rester encore un peu en 4. Vous pouvez forcer le chargement d'une version spécifique par yarn: yarn global add generator-jhipster@4.14.5, et vous voilà avec
Screenshot_2018-07-04_19-00-58

Rappel de quelques commandes de bases

JHipster permet de générer du code depuis des instructions en ligne de commande. Vous pouvez facilement

  • ajouter une entité : jhipster entity leNomDeVotreEntite
  • ajouter un service (avec ou non son interface) : jhipster spring-service leNomDu. Le mot Service est automatiquement ajouté à la fin. Cela va du coup créer une interface et un fichier d'implémentation avec le tag spring @Service
  • ajouter une resource (ou rest controller) : jhipster spring-controller leNomDucontroller. Le générateur crée un controller dans web.rest avec le mot Resource à la fin et la classe de test avec le setUp du Mock.
  • pour un build en dev, sans liquibase : ./mvnw -Pdev,no-liquibase et pour regenerer le front-end de la gateway yarn webpack:build.

Utilisation des fichiers de conf

Par défaut, JHipster génère les fichiers de configuration en yaml pour l'environnement de développement, prod et un global.
A la fin de chacun, vous pouvez ajouter vos paramètres dans la section application. Par exemple, à la fin de application-dev.yml ajoutez:

application:
    demo:
        foo-param: bar

puis mappez ces paramètres dans la class config/ApplicationProperties.java. Pour faciliter l'usage, nous créeons une inner class au nom du groupe de configuration (ici demo). A noter que le signe - du fichier de config sera retranscris en camelCase.

@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
public class ApplicationProperties {
    private final ApplicationProperties.Demo demo = new ApplicationProperties.Demo();

    public ApplicationProperties() {
    }

    public ApplicationProperties.Demo getDemo() {
        return this.demo;
    }

    public static class Demo {
        private String fooParam = "";
        
        public String getFooParam() {
            return fooParam;
        }

        public void setFooParam(String fooParam) {
            this.fooParam = fooParam;
        }
    }
}

et enfin dans votre Service ou ailleurs dans votre code, vous pouvez récupérer la configuration en injectant un objet ApplicationProperties:

@Service
@Transactional
public class MonServiceImpl implements MonService {

    private final Logger log = LoggerFactory.getLogger(MonServiceImpl.class);

    private final ApplicationProperties applicationProperties;

    public MonServiceImpl(ApplicationProperties applicationProperties) {
        this.applicationProperties = applicationProperties;
    }

    @Override
    @Timed
    public void demo() {
        log.debug("Demo method");
        String fooParam = applicationProperties.getDemo().getFooParam();
        return;
    }

Ajouter un composant existant dans une page.

Nous avons ajouté une entité et JHipster a généré les pages CRUD pour nous.
Nous voudrions pas exemple ajouter un composant sur la home page qui liste des composants

Il faut exporter les modules pour pouvoir les réimporter.

  • Dans enquiry-woofis.module.ts
exports: [
        EnquiryWoofisMainInputComponent,
        EnquiryWoofisComponent
    ],
  • Dans entity.module.ts
exports: [
        WoofisEnquiryWoofisModule
    ],
  • Dans home.module.ts
imports: [
        WoofisSharedModule,
        WoofisEnquiryWoofisModule,
        RouterModule.forRoot([ HOME_ROUTE ], { useHash: true })
    ],

Pour plus d'explications, voir la doc d'Angular.

Utilisation de QueryService

Depuis les dernières versions (aux environs de 4.13), les API Rest peuvent bénéficier de critères pour les appels. Par exemple, vous avez un point d'entrée /api/cars/ pour lister des voitures. Vous pouvez alors facilement passer vos parametres dans votre URL: /api/cars?color=red&manufacturingDate.greaterThan=2015-05-01.

Pour cela, l'interface Repository hérite de JpaSpecificationExecutor<T>. Une classe ObjectQueryService définit les appels aux méthods find, findAll... en passant en paramètre un Criteria.

Dans le fichier JSon de votre entité qui se trouve dans le folder .jhipster, vous avez l'instruction "jpaMetamodelFiltering": true,.

Désactiver l'authentification du service

Ce n'est pas bien donc ne lisez pas ce qui est ci-dessous.
Les autorisations d'accéder aux groupes de ressources sont définies dans la class config/MicroserviceSecurityConfiguration. Dans la méthode configure(...), ne changez pas .antMatchers("/api/**").authenticated() en .antMatchers("/api/**").permitAll() sinon vous shuntez l'authentification des appels de vos services.