Partie back de l’application
Description de la problématique
Ce chapitre présente la partie arrière d’une application avec une base SQL. Un chapitre dédié traitera de l’équivalent avec les bases NoSQL. Nous utilisons SQL avec des applications qui s’appuient sur une base de données historiquement SQL ou avec des applications qui changeront peu. En effet, la complexification d’une application basée sur une base de données relationnelle est à mettre en regard avec l’utilisation directe d’une base NoSQL.
Pour une base SQL, un ORM (Object Relational Mapping) est un outil qui permet de passer d’un modèle logique objet vers un modèle physique relationnel. Il en existe plusieurs comme Hibernate, MyBatis mais le plus utilisé est Hibernate qui est souvent couplé avec JPA.
JPA est une API Java qui s’appuie sur une implémentation d’un ORM, nous utilisons JPA avec Hibernate. Hibernate apporte des fonctionnalités supplémentaires. Les annotations et API Hibernate ne sont utilisées que si les fonctionnalités dont on a besoin ne sont pas disponibles par JPA.
L’ORM fait le lien entre la base de données et les objets Java de plus bas niveaux que l’on regroupe dans la couche dite « couche de domaine métier ».
Pour les cas simples, l’ORM gère le SQL pour...
Mise en œuvre
1. Configuration d’une entité de la couche domaine
Par exemple, pour configurer une entité simple de la couche domaine :
@Entity
@Table(name = "table_book")
public class Livre implements Serializable {
@Id
@Column(name = "id")
private String auteur;
private int nbPage;
private String titre;
// getters et setters
[...]
Cet exemple ne montre que des API et des annotations JPA car c’est un cas simple :
Élément |
Signification |
|
@Entity |
Indique qu’il s’agit d’une entité POJO. |
|
@Table(name = "t_book") |
Indique le nom de la table public class Livre implements Serializable. Une entité doit être sérialisable. |
|
@Id |
Indique que le champ sera une clé primaire. |
|
@Column(name = "id") |
Indique le nom de la colonne qui contiendra la clé primaire. |
|
private String auteur |
Donnée métier |
L’auteur du livre |
private int nbPage |
Donnée métier |
Le nombre de pages |
private String titre |
Donnée métier |
Le titre |
Il n’y a pas d’annotation sur les données métier car il s’agit d’un type qu’Hibernate mappe sans difficulté au niveau du type et du nom de la colonne. Dans le cas contraire, nous aurions ajouté une annotation @Column et personnalisé la colonne.
Il est aussi possible d’avoir des variables de classe qui ne sont pas enregistrées dans la base de données. On annote alors la variable avec l’annotation @Transient.
Les classes de la couche domaine sont accédées via la couche Repository.
Mapping many-to-one et one-to-many
@Entity
public class Book implements Serializable {
@Id
private String title;
private int nbPage;
@OneToMany
private Set<Chapter> chapters =
new HashSet<Chapter>();
// getters et setters
Par défaut, les relations entre objets sont du type lazy loading. Cela signifie que les données contenues dans les collections ne sont remontées de la base que si on y accède. Il faut donc éviter d’utiliser...
Pour aller plus loin
Il existe des frameworks qui simplifient grandement la mise en place d’un backend qui fonctionne souvent grâce aux annotations processors.
1. Librairie Java jcabi-aspects
Cette librairie basée sur AspectJ offre certaines annotations d’AOP couvertes par Spring ou Lombok comme @Async, @Cachable, @Loggable et d’autres qui manqueraient comme : @LogExceptions, @Quietly, @RetryOnFailure, @UnitedThrow. La documentation se trouve ici : https://aspects.jcabi.com/
2. Métriques AspectJ
Il existe une librairie qui permet d’ajouter des métriques dans nos backends via des annotations AspectJ. Elles permettent d’avoir des éléments fins sur les appels. La documentation est disponible ici : https://github.com/astefanutti/metrics-aspectj
Utilisation de MapStruct
Nous avons vu que notre code est organisé en couches. Nous pouvons utiliser le modèle DTO, qui consiste à définir des classes simples pour transférer des données entre les couches. Un des principaux problèmes que l’on rencontre réside dans l’écriture d’une grande quantité de code de mappage. Il existe une bibliothèque Java MapStruct qui permet d’automatiser, via de la génération de code, la phase de mapping à travers la description de celle-ci par des interfaces simples et des annotations sur les classes. MapStruct peut s’utiliser avec le CDI de Spring conjointement avec Lombok en créant sous la forme d’un bean le Mapper. La documentation se trouve ici : https://mapstruct.org/documentation/spring-extensions/reference/html/
1. Approche API-First
La conception de backend pour des Web Services SOAP ou REST existe depuis des années. Pendant très longtemps l’API était déduite des appels codés dans les backends. Nous codons un service et nous ajoutons et publions ensuite, parfois de manière automatisée, l’API correspondant à ce service. Il en résulte des API qui ne sont pas toujours idéales.
Une nouvelle approche consiste à prédéfinir l’API pour ensuite coder l’application pour qu’elle rende...
Les outils
Swagger et OpenAPI ont chacun une panoplie d’outils qui sont complémentaires. Les deux lignées suivent les mêmes standards.
1. Swagger
Les sources sont disponibles sur GitHub ici : https://github.com/swagger-api
Swagger supporte plusieurs outils :
Outils |
Utilité |
Swagger Editor |
Editeur d’API |
Swagger UI |
Visualisation et interactions avec l’API |
Swagger Codegen |
Générateurs de code/fichier d’API |
2. OpenAPITools
Les sources sont disponibles sur GitHub ici : https://github.com/OpenAPITools
OpenAPITools supporte plusieurs outils :
Outils |
Utilité |
OpenAPI Generator |
Générateurs de code/fichier d’API |
OpenAPI Style Validator |
Pour suivre les standards d’entreprise |
OpenAPI Diff |
Comparaison de deux spécifications |
3. Autres
Il en existe d’autres qui sont listés et visibles ici : https://openapi.tools/
Les générateurs de code
Swagger et OpenAPI ont tous les deux une suite d’outils qui permettent, entre autres, de générer du code depuis une description d’API et réciproquement. Nous avons déjà vu le plugin Springfox pour générer la description de l’API depuis le code, à la compilation et au runtime. Ils ont aussi des outils pour générer du code à partir de la descripion de l’API.
La description de l’API se fait à travers un fichier JSON ou un fichier YAML. À partir de ce fichier, il est possible de générer du code dans différents langages et pour différents serveurs. Nous nous concentrons sur la génération de code client et serveur avec les technologies Java et Spring Boot. Le générateur est polymorphe. Il existe sous la forme d’une CLI (Command Line Interface) dans un JAR, d’un paquet npm et d’un plugin maven.
La version maven est la plus simple pour pouvoir apporter des modifications. Nous avons en effet le même "problème" qu’avec les générateurs de code comme jHipster pour lesquels le modèle utilisé pour la génération ne correspond pas exactement aux fichiers que l’on souhaiterait. Les générateurs Swagger et OpenAPI n’ont pas les mêmes jeux de templates et ont chacun...
Utilisation du plugin
1. Pour le générateur Swagger
Configuration maven
<plugin>
<groupId>io.swagger</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>3.8.1</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>swagger.yaml</inputSpec>
<language>java</language>
<library>resttemplate</library>
</configuration>
</execution>
</executions>
</plugin>
2. Pour le générateur OpenAPITools
Configuration maven
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>5.4.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>
${project.basedir}/src/main/resources/petstore.yml
</inputSpec>
<generatorName>spring</generatorName>
<apiPackage>fr.eni.openapi.api</apiPackage>
<modelPackage>fr.eni.openapi.model</modelPackage>...
Personnalisation
Il est possible de personnaliser les templates qui sont utilisés pour la génération du code.
1. Swagger
La personnalisation du générateur Swagger est un peu plus complexe. Le plus simple est de patcher un jeu de templates. Le moteur de template est Mustache.
2. OpenAPITools
La personnalisation pour l’OpenAPI est relativement simple. Il existe plusieurs niveaux de personnalisation comme indiqué sur le site du produit : https://openapi-generator.tech/docs/customization/. Cela fonctionne bien et le moteur de template est Mustache par défaut.
Conception d’une description API
Si l’on conçoit une API qui suit une version de la norme OpenAPI, cela ne signifie pas forcément qu’elle sera utilisable, car les générateurs de code n’implémentent pas toujours du code qui couvre l’intégralité de la norme. Il faut la tester avec les outils Swagger et OpenAPI, au niveau de la génération du code des DTO mais aussi des contrôleurs et des clients REST. Il faudrait tester des combinatoires Spring MVC/Spring WebFlux, Java/Kotlin, dans les différentes versions de ces derniers. Les API qui utilisent le polymorphisme et l’héritage devront être particulièrement testées.
Il faudra indiquer les configurations testées avec si possible le code généré avec sa façon de le tester car beaucoup de problèmes ne se voient qu’au runtime.
Outillage pour le design de l’API
L’outil jHipster peut être utilisé pour une approche API-First (https://www.jhipster.tech/doing-api-first-development/) mais il faut déjà avoir son fichier YAML (ou JSON).
Il n’existe pas beaucoup d’outils publics poussés qui permettent de créer un contrat d’interface d’API à partir d’un modèle UML ou de projection au sens DDD du terme.
Outil |
Site |
Open source |
Stoplight |
Oui |
|
SwaggerHub |
Oui |
Nous pouvons configurer un modeleur UML avec générateur de code Java pour générer des classes, les annoter avec les annotations Swagger API et générer le YAML (ou JSON) depuis ces classes en personnalisant les templates.
Spring Actuator
Spring Boot Actuator est un module qui permet d’obtenir des informations opérationnelles sur notre application.
Pour l’utiliser avec Spring Boot, il suffit d’ajouter la dépendance maven :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Le module fonctionne avec les applications Spring MVC et Spring Webflux et il utilise les endpoints HTTP et JMX pour exposer les informations.
Avec Spring Boot 2, seuls les endpoints /health et /info sont exposés pour limiter les problèmes de sécurité.
La configuration de la sécurité peut être faite via un bean de configuration :
@Bean
public SecurityWebFilterChain securityWebFilterChain(
ServerHttpSecurity http) {
return http.authorizeExchange()
.pathMatchers("/actuator/**").permitAll()
.anyExchange().authenticated()
.and().build();
}
Endpoint |
Usage |
/auditevents |
Liste les événements liés à l’audit de sécurité tels que la connexion/déconnexion de l’utilisateur. Nous pouvons appliquer des filtres. |
/beans |
Liste tous les beans disponibles dans notre BeanFactory. Contrairement à... |
Points clés
-
Les objets de la couche métier sont des objets de type Entity gérés par un EntityManager.
-
Les objets sont parfois disponibles sous forme de proxy.
-
JPA propose l’utilisation de plusieurs niveaux de cache.
-
Nous pouvons avoir des types personnalisés.
-
La gestion des accès concurrents est réglable.
-
Nous améliorerons la maintenabilité de l’application en respectant le principe des couches applicatives.