Spring Boot et Quarkus : frères ennemis

  • #Frameworks & développements

Publié le Mis à jour le Par

Christophe Marchand, consultant digital et formateur chez OXiane Institut, Groupe Clever Age, présente la distinction entre Spring Boot et Quarkus, deux frameworks de développement qui, bien qu’ils aient beaucoup en commun, présentent des différences significatives.

Je développe des applications web avec Java depuis de longues années, avec plein de frameworks différents. J’ai commencé dans l’antiquité, en écrivant du HTML dans des servlets, avant l’existence de JSP. Puis avec Struts – on ne parlait pas encore de Struts 2 – puis avec Tapestry, Wicket, Cocoon… la grande époque où un nouveau framework magique sortait tous les 3 mois.

Mais tous ces frameworks avaient pour objectif de générer des pages à afficher dans un navigateur, c’étaient donc des frameworks front, bien qu’ils tournaient côté serveur. L’arrivée de JQuery, la standardisation d’ECMA Script a permis aux développeurs de se concentrer sur ce qu’ils devaient réellement afficher, et pas sur l’écriture de choses atroces comme if(navigator==IE4), else if(navigator==IE5.5). Et là est apparu le concept de développeur back.

Je fais partie de ceux-là, j’ai arrêté le front pour me concentrer sur les tâches qu’on ne peut faire que côté serveur.

Pendant des années, j’ai développé des API Soap ou Rest avec Spring Boot. Et puis Quarkus m’est apparu, et m’a permis de comparer les deux frameworks. Je vous propose aujourd’hui un petit retour sur ces deux frères ennemis, qui ont beaucoup en commun et sont pourtant tellement différents.

Objectifs

D’un point de vue marketing, Spring Boot et Quarkus veulent tous les deux accélérer ; mais pas accélérer les mêmes choses.

Spring Boot se positionne comme une solution pour démarrer un projet très rapidement, et être immédiatement capable d’aller en production. Donc accélération du kick-off du projet. On ne parle pas ici de performances, mais de productivité des développeurs ; pouvoir faire une démo à la fin de la journée de kick-off.

Quarkus, de son côté, pousse un mantra Supersonic Subatomic Java. On comprend qu’on parle de performances. Et effectivement, d’un point de vue marketing, Quarkus se présente comme le framework qui permet de développer des applications qui démarrent rapidement (supersonic) et consomment peu de ressources (subatomic).

En fait, rien ne nous permet de voir que ce sont tous les deux des frameworks pour développer des applications back en Java. Ah oui, back, mais pas seulement… Ils offrent tous les deux la possibilité de créer des applications sans serveur http, des batchs ou des CLI.

Si on regarde plus en détail comment Spring Boot fait pour accélérer le démarrage des projets, on trouve Spring Initializr, des mécanismes de génération de projet, de la gestion de dépendances avec de l’auto-configuration. On trouve exactement la même chose chez Quarkus, avec StartCoding, avec là aussi des dépendances – nommées extensions – et de l’auto-configuration.

Au final, bien que les deux produits aient des objectifs différents, on voit que du premier abord, ils sont très proches.

Support des standards

Le développement back Java a longtemps été standardisé par Java EE, avec ses travers : obligé d’hériter de classes techniques, rien de prévu pour l’isolation et l’encapsulation, pas d’abstraction, et surtout pas de possibilité d’écrire simplement des tests unitaires.

Spring a été le premier à dire que cette façon de programmer était mauvaise, que la période de Java EE était l’hiver de la programmation, et que le printemps (Spring) devait arriver.

Spring a proposé plein de moyens d’introduire de l’abstraction, du découplage, au travers de l’inversion de dépendance, de la configuration de l’assemblage. A cette époque, on ne parle que de Spring Framework, pas encore de Spring Boot. A cette époque Spring se positionne comme une alternative aux EJB, Java 5 et les annotations n’existent pas encore, et le seul standard qui existe est Java2EE, qui ne convient pas du tout à Rod Johnson ; CDI n’existe pas encore dans Java EE, et donc, il faut tout créer. Spring invente tout, et définit son propre standard, même si à l’époque il n’y a pas ou peu de javadoc, d’API, puisque Spring nous pousse à écrire des classes qui n’héritent de rien.

Donc Spring ne respecte aucun standard existant, il définit ses propres standards et les impose à la communauté des développeurs. Il fera des efforts pour supporter plus tard les annotations standard, comme javax.inject.Inject, mais avec des pincettes.

De son côté, Quarkus arrive beaucoup plus tard. En 2018. A cette époque, Oracle a racheté Sun, se débarrasse de Java EE en le refilant à Jakarta, les EJBs sont morts et enterrés, et on commence à se dire que déployer des applications dans des conteneurs JavaEE lourds qui mettent 10 minutes à démarrer plein de services dont on n’a absolument pas besoin, c’est très old-fashion. Les micro-services sont dans l’air, le cloud pose ses contraintes, et Eclipse a publié Microprofile qui standardise, sur la base de Java EE 7, un ensemble de spécifications qui permettront de développer en Java des applications pour le Cloud et les micro-services. Quarkus respecte cette spécification Microprofile, mais pas totalement ; certaines parties de Microprofile ne sont pas supportées par Quarkus. Aujourd’hui, Microprofile s’appuie sur de nombreux aspects sur Jakarta EE (nouveau nom de Java EE depuis son abandon par Oracle) et propose donc, pour les développeurs qui développaient avec des EJBs, le support des mêmes standards. Ils sont donc sont rapidement opérationnels avec Quarkus.

Là encore, des similarités entre les deux frameworks, même si l’un a défini ce qui est devenu un standard, et que l’autre suit un standard existant, orienté micro-services et Cloud. Attention, ce n’est pas parce que les standards Spring ne sont pas à l’origine conçus pour les micro-services qu’il est impossible de réaliser des applications micro-service avec Spring Boot, bien au contraire.

Outils de développement

C’est à la mode ! Tous les frameworks, toutes les librairies vous proposent des assistants pour coder rapidement. Maven a popularisé les archetypes, Angular à sa CLI, qui vous permet de créer des composants simplement, Spring a sa Spring Tool Suite, Quarkus à son Developer Joy. Voyons ce qu’apportent ces différents outils.

Côté Spring, nous avons tous utilisé Spring Tool Suite, un gros plugin dans Eclipse qui fournit des assistants, des générateurs pour Spring Boot. Mais Eclipse est de moins en moins à la mode, IntelliJ a pris le pas. On trouve dans IntelliJ quantité de plugins communautaires, la version Ultimate d’IntelliJ apporte nativement de nombreux outils pour développer avec Spring. Spring lui-même fournit une dépendance, Developer tools, qui est faite pour simplifier la vie des dévs : auto-configuration pour désactiver les caches (qui rendent difficile la mise au point des applications), la gestion automatique des redémarrages, ou la prise en charge de LiveReload (rechargement automatique de l’application front quand le back est modifié) pour les navigateurs qui le supportent, et enfin le remote-coding, qui permet de modifier le code source sur sa machine de dév et d’exécuter voire debugger sur une machine distante.

Quarkus a une approche légèrement différente : il n’est pas nécessaire d’avoir un IDE avec des plugins pour pouvoir bénéficier d’assistances lors du développement. Tout d’abord, la CLI de quarkus permet de créer de nouveaux projets, d’ajouter de nouvelles extensions, de façon simple. Pas strictement nécessaire, car le plugin maven Quarkus fournit les mêmes services, sans avoir à installer quoi que ce soit. Il propose de n’avoir qu’un unique fichier de configuration pour tout le projet, même si on y trouve mélangé des choses qui pourraient être séparées : certaines concernent la configuration de l’application, d’autres les options de build ; pas certain que ce soit une si bonne idée. Quarkus propose aussi un mode d’exécution dédié au développement ; on le lance par la CLI, ou via le plugin maven quarkus:dev. Dans ce mode, plein de choses sont automatiquement gérées, un peu comme avec les Spring Dev-Tools. En particulier, Quarkus propose nativement les Dev-Services : vous avez déclaré que votre application persisterait ses objets dans une base Postgre ? Pas besoin de démarrer un conteneur Docker à côté, Quarkus le fait pour vous ; non seulement il démarre un conteneur Docker, mais en plus il déclare la configuration nécessaire côté applicatif pour se connecter à cette base de données. Vous utilisez Kafka ? En Dev-Mode, Quarkus démarre un conteneur Kafka, qui est en plus exposé à l’extérieur, et qui est réutilisé par votre autre application Quarkus  qui vient consommer les messages émis par la première ; mise en commun de la ressource. Cette fonctionnalité est une vraie assistance, car elle fait effectivement gagner énormément de temps quand on développe : plus besoin de démarrer et d’arrêter les différents conteneurs, tout est pris en charge automatiquement. Enfin, Quarkus fournit la Dev-UI ; dès que vous démarrez votre application, est exposée sur une URL une IHM permettant d’accéder aux différents composants de votre application : visualisez les messages Kafka émis, monitorez votre base de données, explorez les bearer tokens d’authentification, etc. c’est très pratique, et pas du tout invasif.

Les deux frameworks proposent pleins d’outils pour simplifier la vie des développeurs, mais Quarkus, à mon sens, a un peu d’avance sur Spring concernant les outils de dév, en particulier avec les Dev-Services ; cela permet à des développeurs sans aucune connaissance Docker d’utiliser des conteneurs pour développer sans même comprendre ce qu’il se passe. Cela ne signifie pas non plus qu’on peut rester ignorant de tout et être efficace sur Quarkus, mais ça accélère la montée en compétences.

Méthodologie de développement

La méthodologie de développement qu’on utilise avec les deux frameworks est très similaire, seul le vocabulaire diffère : d’un côté, on ajoute une dépendance (au sens Maven), de l’autre on ajoute une extension.

Lorsqu’on a besoin d’une nouvelle brique technique, dans Spring, on ajoute une nouvelle dépendance qui est en général un starter. Ce starter fournit un ensemble de dépendances, avec des versions qui sont cohérentes entre elles, et une auto-configuration définie par des beans qui sont activés par des annotations conditionnelles. Cela permet d’avoir une configuration par défaut qui fonctionne, et de permettre de la surcharger soit par des propriétés, soit par des beans qu’on viendra définir dans notre configuration.

Quarkus a repris exactement le même modèle, mais parle d’extensions au lieu de dépendances. Comme dans Spring, une extension est une dépendance Maven, comme dans Spring, on ne définit pas sa version, c’est géré par un bom, et comme dans Spring, un mécanisme d’auto-configuration est aussi mis en place. Simplement, dans Quarkus, ce mécanisme d’auto-configuration est moins mis en avant que dans Spring, même si on s’appuie là encore sur les mêmes mécanismes.

Si vous êtes développeur Spring Boot, vous ne serez pas surpris par la façon d’ajouter des composants techniques dans votre application Quarkus, c’est la même façon de procéder. Simplement, la CLI ou le plugin Maven vous permettent d’ajouter l’extension sans toucher le pom.xml, et donc en diminuant les risques de faire une bêtise.

Pour savoir ensuite comment développer avec le composant technique ajouté, deux méthodologies pédagogiques diffèrent : Spring propose des documentations de référence, alors que Quarkus propose des guides.

La partie Spring est assez classique, parfaitement exhaustive. Tout y est : exemples simples, présentation des concepts, explications plus avancées, Javadoc. Mais ce n’est ni fun, ni sexy ; ça fait très old-fashion, très projet Apache des années 2005.

Côté Quarkus, on opte plutôt pour des guides, qui présentent tous un projet complet qui fonctionne, avec toutes les explications des exemples de code. Et il y a des guides sur tout ! Le fin découpage des extensions amène à avoir de très nombreux guides, parfois plusieurs pour une même extension, parfois juste pour une annotation (@RunOnVirtualThreads), mais très souvent, ces guides ne sont pas exhaustifs ; il faut aller chercher dans les quelques documentations de références (il y en peu, et pas simples à trouver) pour avoir les détails précis pour répondre aux besoins particuliers. Mais les guides répondent aux cas les plus courants, et sont en général suffisants.

Sur cette partie de méthodologie, de documentation, à mon sens, Quarkus est en avance, plus simple, moins risqué, et avec une documentation plus facilement accessible, plus facile à lire et à appliquer, même si elle est moins complète.

Spring Data vs Panache

Je souhaiterais détailler ici un aspect de la programmation qui est traitée de deux façons différentes par les deux frameworks : l’accès aux bases de données relationnelles. Les deux s’appuient sur JPA, qui quoi qu’on en dise est devenu le standard, après les errements des EJBs entity CMP, pour ceux qui ont connu. Donc on retrouve la définition des @Entity pour modéliser les data que l’on souhaite persister. Mais les deux frameworks ont dû trouver que les classes permettant de chercher et de persister ces data sont trop compliquées et ils proposent chacun une alternative élégante, bien que très différente.

Spring Data propose de créer un repository par entité (plus ou moins), repository qui s’appuie sur la définition qu’en donne Eric Evans, dans son livre Domain-Driven Design. Dans Spring Data, ce repository est une interface, qui hérite d’une interface standard de Spring (JpaRepository par exemple) qui fournit les méthodes d’accès basiques et celles qui permettent d’enregistrer notre entité. Dans cette interface qu’on vient de définir, on ajoute des méthodes de recherche, en utilisant une syntaxe précise qui permettra à Spring de générer dynamiquement les requêtes sur la base du nom de la méthode. Ainsi, une méthode findByPrenomIgnoreCase(String prenom) génèrera une requête dans laquelle la clause where contiendra UPPER(prenom) = UPPER(?prenom). Ainsi, en tant que développeur, je n’écris pas de code. Même si c’est perturbant au premier abord, même si on se demande « comment est-ce que je mets un breakpoint dans une interface ? », on se rend très rapidement compte qu’on a pas besoin de mettre de point d’arrêt ; ça marche. Et comme on n’écrit pas de code, on n’a pas non plus besoin d’écrire de test unitaire. Gros gain de temps et de productivité.

Évidemment, il reste toujours des cas où cette génération ne peut pas fonctionner ; c’est rare, mais cela arrive. Et dans ce cas là, tout est moins simple ; on peut forcer la définition de la requête (JPQL ou native), on peut étendre notre repository avec une implémentation native de certaines méthodes, mais c’est tout de suite très compliqué, et pas du tout intuitif.

A l’opposé, Quarkus nous impose d’écrire une implémentation soit d’un repository, comme dans Spring Data, soit d’un Active Record, autre pattern proposé par Martin Fowler en 2003 dans son livre Patterns of Enterprise Application Architecture. Dans celui-ci, l’entité hérite d’une classe qui fournit sous la forme de méthodes statiques tout ce qu’il faut pour persister l’entité. Quel que soit le modèle de programmation choisi pour la persistance (on peut faire cohabiter les deux), il faut fournir une implémentation pour les recherches. Mais Quarkus s’appuie sur Panache, une sur-couche d’Hibernate qui masque une très grande partie de la complexité, en fournissant beaucoup de méthodes très simples.

J’ai beaucoup de mal à choisir entre les deux solutions proposées par les deux frameworks. J’apprécie vraiment le fait de ne pas écrire du tout d’implémentation dans Spring Data, mais également la simplicité d’utilisation de Panache, qui permet d’écrire très simplement ses requêtes.

Conclusion

Dans ce billet, je n’aborde volontairement pas les sujets de performances. Je me suis limité aux aspects qui concernent au premier chef les développeurs. Je vous renvoie à un billet publié par Grzegorz Piwowarek qui détaille très clairement les différences de performances entre les deux frameworks.

Du point du vue du développeur, Spring a pour lui l’ancienneté, la communauté de développeurs importante, les ressources et l’inconscient collectif qui en fait la solution pour développer des back-ends en Java. Mais Quarkus apporte un point de vue différent, pas révolutionnaire, mais concret et parfaitement adapté au Cloud, pour lequel il se revendique le meilleur. Certes, certaines choses ne sont pas aussi bien que dans Spring, on l’a vu avec Spring Data et Panache, mais sur d’autres sujets, il est en avance, par exemple avec le client Rest qui se définit simplement avec une interface et pas d’implémentation (tient tient…).

Allez jeter un œil à Quarkus, c’est intéressant, et la montée en compétence n’est pas si coûteuse.


Références

  • Domain-Driven Design, Eric Evans, 2004, ISBN-13: 978-0-321-12521-7
  • Patterns of Enterprise Application Architecture, Martin Fowler, 2003, ISBN-13: 978-0-321-12742-6
  • Spring Boot vs Quarkus, Grzegorz Piwowarek, 08/01/2024