Les « Derniers Jedi », c’était l’intitulé de notre dernier sprint… Nom de code Apollo Risk Management : un MVP de gestion des risques destiné aux entreprises pour une couverture personnalisée à la clef, un outil permettant à partir d’un questionnaire de calculer une Risk Map en fréquence et en intensité pour leurs activités. CARISMA pour Crédit Agricole Risk Management : une application multi-technologies avec un front Angular, un frontal Apollo GraphQL en NodeJS (agrégation de données) et un back-end Java Spring Boot couplé avec une base de données PostgreSQL. Cet article sera centré sur la réalisation du back-end, les technologies utilisées et les difficultés rencontrées.

 

Spring Boot ? Késako ?

 

Spring Boot est un framework Java open source utilisé pour faciliter la création de micro-services, notamment pour le développement de services REST qui offriront à notre application les informations nécessaires. L’auto-configuration offerte par ce framework applique une configuration par défaut au démarrage de l’application pour toutes les dépendances associées (activée par l’annotation @SpringBootApplication). Ce framework apporte de nombreuses fonctionnalités sur les aspects Web, sécurité, accès aux données et embarque un serveur Tomcat. Enfin, ce dernier apporte son lot de dépendances regroupées sous forme de « starters » (p. ex. spring-boot-starter-security) et facilite la gestion de celles-ci.

 

Une base de données à la fois relationnelle et documentaire ? Oui, mais laquelle ?

 

Notre choix s’est porté sur PostgreSQL, une base de données performante et libre qui nous permettra de mettre en place à la fois notre modèle relationnel ainsi qu’un stockage documentaire, mais pour quels besoins ?

Le besoin d’un versioning des données du questionnaire et de la Risk Map est apparu. Un petit challenge nous orientant vers le format JSON et un stockage documentaire afin de simplifier l’implémentation. Pourquoi ne pas utiliser une base de données documentaire comme MongoDB pour ce besoin spécifique de versioning ? Durant notre phase de cadrage, nous avons vu que PostgreSQL, bien qu’il s’agisse d’une base de données relationnelle, pouvait également stocker des données dans un format de type JSON ou JSONB. Quel type devrions-nous privilégier ?

Une différence notable entre les deux types JSON et JSONB est l’efficacité. Le type JSON stocke une copie exacte du texte (espaces inclus) et un reparsing est nécessaire à chaque accès, tandis que le type JSONB stocke un contenu binaire (aucun reparsing n’est nécessaire, pas de conservation des espaces, ni de l’ordre ou des duplicate keys) et supporte l’indexage qui améliore les performances d’accès. Dans ce cas, pourquoi ne pas partir sur un type JSONB ?

L’implémentation se complique… Il faut savoir que Java ne fournit pas nativement de type de données correspondant au type PostgreSQL JSONB. En conséquence, un RowMapper devra être effectué utilisant les capacités de Spring data pour faire correspondre les données JSONB à un type Java. Nous avons donc utilisé la classe PGObject qui est malheureusement très pauvre en méthode mais nous a permis de récupérer le contenu JSONB stocké en base. Pour le parsing et le traitement du contenu JSONB, nous avons utilisé les librairies Gson de Google. Toutefois, au regard de nos besoins en versioning et de la complexité du modèle de données, la difficulté d’implémentation s’accentue et la maintenabilité du code devient lourde. Quant aux fonctionnalités SQL pour parcourir et effectuer des opérations sur le contenu JSONB, un temps d’apprentissage et d’adaptation est nécessaire avant de se lancer sur cette voie.

Au bout du compte, avec l’accord de l’équipe projet, nous avons pris la décision de ne pas implémenter le versioning au vu de la charge de travail requis. Si c’était à refaire, une base de données documentaire dédiée pour ce type de besoin telle que MongoDB serait beaucoup plus adaptée.

 

Le chargement des données de type référentiel ? Oui, mais comment ?

Une bonne partie des données stockées en base sont des données de type référentiel administrées par le métier. Ce sont des données entreprise (risques, code NAF, Batica) et de scoring (impact en intensité et fréquence d’un risque par rapport à un NAF et niveau d’intensité et de fréquence sur un risque par rapport à une réponse à une question).

Les données Batica proviennent du référentiel Baticible (données en cache) qu’on exploite à ce jour avec pour objectif de s’interfacer à Batica directement dans un avenir proche. Quant aux données de scoring, elles vont nous permettre de générer la Risk Map après une évaluation de ces dernières et une variation appliquée sur les points de bases (impact Risk/NAF en fréquence et en intensité).

Ces données sont aujourd’hui matérialisées par des fichiers CSV et transformées en fichier SQL. Elles sont ensuite chargées via l’outil Liquibase intégré au service Java qui servira de migration des données pour la création des tables, la mise à jour et bien plus encore. De plus, l’outil permet de vérifier que les scripts SQL ont déjà été exécutés en enregistrant le checksum du fichier SQL. Ainsi, à chaque démarrage de l’application, ces fichiers SQL ne seront pas rejoués, autrement dit, seuls les nouveaux fichiers SQL créés sur un répertoire spécifique seront pris en compte.

 

Comment maintenir ces données au niveau applicatif ?

Après revue des différents framework utilisés sur le marché, notre choix s’est orienté vers Spring data JDBC qui offre une gestion plus simplifiée (absence d’offre de caching, lazy loading, write-behind, etc.) et orienté Domain-Driven Design, une approche que nous avons adoptée pour ce projet. C’est un framework plutôt jeune, en comparaison avec Spring JDBC (JDBC template) ou JPA (Hibernate) qui sont beaucoup utilisés et matures : l’un qui implique beaucoup d’écriture de code et l’autre connu pour ses nombreuses fonctionnalités et sa complexité d’implémentation.

Toutefois, nous avons vu les limites de Spring Data JDBC qui souffre de problèmes de performance lorsque l’application est plus importante en termes de volumétrie de données. Comme mentionné précédemment, le framework ne propose pas de lazy loading, ce qui est bien un manque au vu de la complexité de notre modèle qui requiert des relations fortes entre les tables. De plus, une requête de mise à jour des données en base requiert la suppression et l’insertion de données, ce qui est très consommateur en ressources et impacte les performances. Nous avons contourné ces différents problèmes en implémentant les requêtes SQL directement sans utiliser les méthodes natives du framework. Quant au problème de mise à jour, nous avons utilisé JDBC template (batch update) qui offre une performance bien meilleure : un temps de traitement divisé par deux.

Nous avons fait le choix d’utiliser comme identifiant unique (clef primaire) un UUID au lieu d’un identifiant numérique incrémental pour les tables dont les données sont dynamiques et évolutives (dossier client, sites d’entreprises, activités, etc.). Cependant, pour les tables de type référentiel, le métier a créé les identifiants (type String) pour des besoins de compréhension et d’évolution.

 

Et la sécurité dans tout cela ?

 

Spring Security : un puissant framework personnalisable permettant de gérer à la fois l’authentification et les autorisations pour les applications Java. Voilà une belle introduction pour ce framework qui nous a permis de gérer les droits d’accès par rôle d’utilisateurs (Manager, AssurPME, CAE, etc.) ainsi que par caisse régionale.

L’authentification par KeyCloak avec la création des access/refresh token, pour l’utilisateur souhaitant accéder à un service, sera validée en corrélation avec le frontal Apollo GraphQL. Celui-ci fera ensuite les appels REST aux services back-end Java accompagnés du token. Les micro-services Java sont stateless et s’occupent uniquement de la validation des rôles et des droits associés : READ, CREATE, UPDATE, DELETE. La caisse régionale est également un critère supplémentaire (envoyé dans le token) permettant de rendre visible tous les dossiers clients par caisse pour un rôle Manager par exemple.

Nos services back-end Java au sein de notre environnement AWS Kubernetes profitent déjà de l’isolation nécessaire (aucun ingress exposé) et éviter les attaques venant de l’extérieur. La validation et le renouvellement du token sont donc portés uniquement par le frontal.

 

Pour conclure, CARISMA est le fruit d’un projet multi-technologies avec un back-end découpé par domaine métier et utilisant un modèle de données complexe. Au cours de ce projet au sein du back-end, nous avons implémenté nos services REST, construit un modèle de données, élaboré un mécanisme de chargement de ces données, sécurisé les accès par rôle, expérimenté plusieurs frameworks d’accès aux bases de données, ainsi que la gestion documentaire dans PostgreSQL qui s’est révélé plutôt fastidieux à réaliser. Un projet CARISMAtique avec également des technologies front-end que je n’aborde pas dans cet article mais qui pourraient faire l’objet d’un sujet à aborder prochainement.