Migration Spring boot 4.0.0
Rapport de migration Spring Boot 3.5.0 → 4.0.0
🎯 Objectifs de la migration
-
✅ Mettre à jour l’application OreSiNg vers Spring Boot 4.0.0 (Spring Framework 7). -
✅ Garder le comportement fonctionnel identique (tests verts). -
🤝 Fournir un retour d’expérience pour les prochaines migrations internes. -
🧾 Identifier les dettes techniques introduites pendant la migration.
🧱 Base technique et dépendances
Spring Boot et BOM
-
✅ Migration du parent:-
spring-boot-starter-parent3.5.0 → 4.0.0.
-
-
✅ Introduction du BOM Testcontainers:- Ajout de
org.testcontainers:testcontainers-bom:2.0.2dansdependencyManagementpour aligner toutes les libs Testcontainers.
- Ajout de
-
⚠️ Ne pas doubler les versions Testcontainers (éviter de mixerpostgresql:1.xettestcontainers-postgresql:2.x). -
⚠️ Vérifier que toutes les dépendances Spring Boot (starter-test, starter-webflux, etc.) sont bien pilotées par le parent et non redéclarées avec des versions différentes.
Testcontainers PostgreSQL
-
✅ Passage de:org.testcontainers:postgresql:1.21.3
- à:
-
org.testcontainers:testcontainers-postgresql(2.0.2).
-
- Intégration plus propre avec Spring Boot 4 et suppression des problèmes de classes “shaded”.
- Utilisation d’un bean de test dédié (
TestDatabaseConfig) pour démarrer un container PostgreSQL 18 avecwithInitScript.
-
😬 Contournement partiel du nouveau support Spring Boot 4 + Testcontainers (annotations@ServiceConnection) au profit d’unDataSourcemanuel dans un@TestConfiguration.
→ À revoir plus tard pour simplifier et utiliser la config auto Spring Boot.
🗃️ Flyway et PostgreSQL 18
Problème rencontré
-
❌ Erreur initiale:Unsupported Database: PostgreSQL 18.0
- Cause:
- Support modulaire Flyway pour PostgreSQL (nécessité de
flyway-database-postgresql) + configuration d’URL/user/mot de passe non alignée avec le container.
- Support modulaire Flyway pour PostgreSQL (nécessité de
Solution mise en place
-
✅ Retour aux dépendances Flyway “classiques” + ajout du module PostgreSQL:flyway-coreflyway-database-postgresql
-
✅ Désactivation de l’auto-config Spring Boot pour Flyway dans les tests et:- Création d’un bean
Flywayexplicite dansTestDatabaseConfig:- branché sur le
DataSourcedu container. locations("classpath:migration/main")baselineOnMigrate(true)- injection explicite des placeholders (ex:
publicRoleId).
- branché sur le
- Création d’un bean
- Scripts main + scripts schémas secondaires (via
MigrateService) exécutés correctement sur PostgreSQL 18 en Testcontainers. - Contrôle total sur l’ordre et la base cible des migrations.
-
😬 On contourne l’auto-config Flyway de Spring Boot 4 (propriétésspring.flyway.*) pour les tests:- Bean Flyway “manuel” spécifique aux tests.
- Placeholders gérés à la main dans ce bean.
- Idée pour plus tard:
- Revoir l’usage de
spring.flyway.*+@ServiceConnectionpour s’appuyer davantage sur l’auto-config.
- Revoir l’usage de
🔐 Sécurité (Spring Security 7)
Configuration HTTP et filtres
-
✅ SecurityFilterChainmodernisé avec la syntaxe lambda (authorizeHttpRequests, etc.). -
✅ AuthorizationFiltertoujours injecté et ajouté via:-
.addFilterAfter(authorizationFilter, BasicAuthenticationFilter.class).
-
Problèmes rencontrés:
-
😵 Le filtre était bien instancié mais jamais appelé dans certains tests:- MockMvc n’était pas configuré avec Spring Security.
- Un mapping path (
{nameOrId}œ/rightsRequest) contenait un caractère parasite (œ), générant un 405 inattendu.
Solutions:
-
✅ Construction deMockMvcvia:MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
-
✅ Correction du mapping:-
"/applications/{nameOrId}/rightsRequest"(suppression duœ).
-
-
✅ Vérification explicite par logs queAuthorizationFilterest bien invoqué.
- Endpoints comme
/api/v1/loginsont protégés côtéHttpSecurity, mais la logique d’auth est dans un filtre custom. - Bien séparer:
- ce qui doit être
permitAll()pour laisser le filtre faire l’auth, - de ce qui doit être vraiment protégé en amont.
- ce qui doit être
🌐 Web / MockMvc / Tests d’intégration
Factoring des tests
-
✅ Création d’une classe de base:-
AbstractIntegrationTest:@SpringBootTest(classes = {OreSiNg.class, TestDatabaseConfig.class})@ActiveProfiles("testmail")@TestPropertySource("classpath:/application-tests.properties")@DirtiesContext(BEFORE_EACH_TEST_METHOD)- Construction de
MockMvc+ injection des dépendances partagées (JsonRowMapper,UserRepository,AuthenticationService, etc.).
-
-
✅ Les tests d’intégration concrets héritent maintenant deAbstractIntegrationTest:- réduction du boilerplate,
- configuration homogène.
- Tests d’intégration plus lisibles et cohérents.
- Moins de duplication d’annotations et d’initialisation de fixtures.
- Couplage fort des tests à une configuration unique (
AbstractIntegrationTest).- Penser à introduire des variantes si d’autres profils/envs de tests apparaissent (ex: sans sécurité, sans DB, etc.).
🧩 JSON, Jackson et JsonRowMapper
Problèmes de désérialisation rencontrés
-
❌ HttpMessageConversionExceptionsurLocalDateTimeRange. -
❌ Problème debooleanprimitif (setted) non nullable:-
Cannot map null into type boolean.
-
Causes:
- Spring MVC désérialisait directement certains
@RequestBodyvers des types contenant:-
LocalDateTimeRange, - des sealed interfaces,
- des booléens primitifs.
-
- Or l’application s’appuie historiquement sur un
JsonRowMappercustom:-
ObjectMapperconfiguré avec:- modules spécifiques (
LocalDateTimeRange, date/time, sealed types, etc.), -
DeserializationProblemHandlercustom.
- modules spécifiques (
-
Solutions:
-
✅ Pour les endpoints critiques (ex:/applications/{nameOrId}/authorization):- passage du
@RequestBodyàString body, - désérialisation via
JsonRowMapper.readValue(body, CreateAuthorizationRequest.class).
- passage du
-
✅ Pour les champs booléens:- passage de
boolean→Booleanlorsque null est possible (ou laissé tel quel quand la valeur est garantie).
- passage de
- Cohérence avec le design existant: le binding complexe JSON
↔️ domaine passe toujours parJsonRowMapper. - Spring MVC ne tente plus de désérialiser des types qu’il ne connaît pas.
-
😬 Reliance forte à unObjectMappercustom central (JsonRowMapper) non réutilisé automatiquement par Spring MVC.- Piste d’amélioration:
- exposer
jsonRowMapper.getJsonMapper()comme@Primary ObjectMapperglobal et vérifier que tous lesMappingJackson2HttpMessageConverterl’utilisent, - ou documenter clairement que tous les endpoints manipulant des types complexes doivent prendre un
String/JsonNodeet passer parJsonRowMapper.
- exposer
- Piste d’amélioration:
🧪 Testcontainers, DB de test et Flyway
Stratégie actuelle
-
✅ TestDatabaseConfig:PostgreSQLContainer<>("postgres:18.0")-
.withInitScript("migration/openadom_user.sql")pour créer l’utilisateur technique. - Bean
DataSourceexplicitement configuré avec l’URL du container. - Bean
Flywaydédié, avec:locations("classpath:migration/main")- placeholders injectés.
-
✅ MigrateServicecontinue à gérer les migrations par schéma applicatif après les migrations “main”.
- Base de test représentative (PostgreSQL 18).
- Migrations maîtrisées.
-
😬 Double config DB/Flyway: auto-config Spring Boot en prod vs. bean manuel en tests.- À moyen terme, envisager de:
- utiliser
@ServiceConnectionetspring.flyway.*pour les tests, - ou assumer officiellement que Flyway sera toujours piloté manuellement (doc interne).
- utiliser
- À moyen terme, envisager de:
🧹 Modifications “à la marge” et dettes repérées
Ajustements cosmétiques ou “lint”
-
✅ Renommages sans impact fonctionnel:- Ex:
binaryfiledataset→binaryFiledatasetdansBinaryFileInfos.
- Ex:
-
🧹 Suppressions d’imports non utilisés (ex:DataRepositoryForBuffer).
Ces changements sont sains, mais à documenter comme “cosmétique” pour ne pas les confondre avec de vrais impacts migration.
Contournements / patches spécifiques à Spring 4
-
⚠️ BeanObjectMapperajouté dansOreSiNg(ou autre config) pour essayer de propagerJsonRowMapper:- À valider / stabiliser:
- soit l’assumer comme convention standard,
- soit le retirer si finalement non utilisé par Spring MVC.
- À valider / stabiliser:
-
⚠️ Changements sur les endpoints (GET/POST/mapping) pour contourner des erreurs de mapping ou de sécurité:- ex: correction du path de
rightsRequest, - attention à ne pas masquer un vrai changement de requirement.
- ex: correction du path de
✅ Synthèse visuelle
-
✅ Migration core Spring Boot 3.5.0 → 4.0.0 (parent, BOM, starters principaux). -
✅ Tests d’intégration 100% verts (après adaptation des tests et des filtres). -
✅ Flyway opérationnel avec PostgreSQL 18 + Testcontainers. -
✅ Refactoring des tests d’intégration viaAbstractIntegrationTest. -
😬 Points à revisiter:- Intégration plus “standard” de Testcontainers avec
@ServiceConnection. - Utilisation systématique de
JsonRowMappervs. configuration d’unObjectMapperglobal partagé. - Allègement des beans “manuels” (Flyway, ObjectMapper) lorsqu’un starter Spring peut faire le travail.
- Clarification des responsabilités sécurité (endpoints
permitAll()vs. filtrage dansAuthorizationFilter).
- Intégration plus “standard” de Testcontainers avec
Ce rapport peut servir de base:
- à une checklist interne “Migration Spring Boot 3.x → 4.x”,
- et à une revue de dettes techniques pour des tickets de refactor ultérieurs.