J’ai eu la chance de participer à un projet e-commerce dans le secteur de l’énergie.
L’application comporte plusieurs services dont un service de commande en java communiquant avec une base documentDB.
Au cours du développement nous avons décidé d’apporter de la robustesse à nos bases de données en implémentant un schéma de validation.
Je vous partage ici, mon expérience sur la mise en place de l’outil Mongock.
Mise en contexte
Dans nos projets du quotidien, les bases de données NoSQL comme MongoDB offrent une grande flexibilité en matière de structure de données, mais cette souplesse peut parfois devenir un point faible. En l’absence de schémas de validation stricts, il est facile d’introduire des incohérences et des erreurs au fil des modifications.
À mesure que les applications évoluent, les données doivent être robustes, cohérentes et conformes aux nouvelles exigences fonctionnelles. Cela rend parfois les migrations complexes, car elles nécessitent souvent des changements structurels ou des corrections dans la base de données.
Dans ce contexte, l’utilisation d’outils de migration comme Liquibase, Flyway ou encore Mongock devient essentielle. Ces outils apportent une solution fiable pour gérer les changements de schéma et les migrations de données, tout en garantissant l’intégrité des données au travers des différentes versions d’une application. Ils permettent également d’automatiser les migrations, réduisant ainsi les risques d’erreurs humaines et facilitant le déploiement dans des environnements complexes.
Au sein de l’équipe, nos expériences nous ont menées vers liquibase, malheureusement la documentation assez pauvre sur le sujet NoSQL nous a freiné dans son usage.
Nous avons alors cherché un outil adapté et un membre de Shodo m’a conseillé Mongock.
Cet article aborde cette problématique spécifique, en montrant comment Mongock permet d’apporter une robustesse à une base MongoDB initialement dépourvue de schéma de validation.
Cahier des charges
Nous avons décidé d’utiliser Mongock car nous souhaitions un outil de validation de schéma et de mise à jour de champs dans nos collections. Nous voulions un outil documenté, robuste, permettant d’automatiser nos changements. Il fallait qu’il s’intègre facilement à Spring et Spring Boot. Cerise sur le gâteau il est compatible avec documentDB, un point rassurant vu que nous nous appuyons sur aws et cette base.
Pour rappel, Mongock est un outil de gestion de migrations de base de données NoSQL conçu spécifiquement pour MongoDB, mais qui peut également être utilisé avec d’autres bases de données NoSQL. Il permet d’assurer des migrations sûres, réversibles et contrôlées dans un environnement distribué ou un environnement de microservices.
Configuration dans votre projet Spring Boot
Pour utiliser Mongock vous devez ajouté 3 dépendances :
Le driver qui assure la communication avec MongoDB pour appliquer les changements lors des migrations :
mongodb-springdata-v4-driver
La librairie Mongock pour s’intégrer Spring Boot et automatiser les migrations :
mongock-springboot-v3
Enfin une libraire pour interagir avec les bases de données MongoDB à travers l’API Spring Data :
spring-boot-starter-data-mongodb
Ensuite vous devrez ajouter dans votre point d’entrée l’annotation “@EnableMongock” qui permet d’exécuter les migrations lors du run de l’application.
@SpringBootApplication(scanBasePackages = "fr.company.product.myservice")
@EnableMongock
public class MyServiceApplication {
public static void main(String[] args) {
SpringApplication.run(MyServiceApplication.class, args);
}
}
Pour effectuer vos changements il faut bien sûr les écrire mais il faut que vous expliquiez à Mongock qu’il doit scanner le dossier où vous les avez écrits :
mongock:
migration-scan-package: fr.company.product.myservice.changes
L’écritures des changements
Création
C’est dans le dossier que vous avez configuré, que vous devez placer vos changements.
Un fichier Java, dans lequel vous écrirez par exemple votre schéma de validation, l’insertion ou encore la mise à jour de données.
Je vous donne l’exemple ici de la création d’un schéma de validation pour une commande dans le cadre d’un projet e-commerce :
@ChangeUnit(id = "change20240829-init-orders", order = "001", author = "Tanguy B.")
public class Change20240829 {
private final ReactiveMongoTemplate template;
public Change20240829(ReactiveMongoTemplate template) {
this.template = template;
}
@BeforeExecution
public void beforeExecution() {
this.template.dropCollection("orders").block();
}
@RollbackBeforeExecution
public void rollbackBeforeExecution() {
}
@Execution
public void execution() {
this.createCollection("orders").block();
}
@RollbackExecution
public void rollbackExecution() {
}
private Mono<Void> createCollection(String collectionName) {
var schema = createSchema();
CollectionOptions options = CollectionOptions.empty()
.validator(Validator.schema(schema));
return template.createCollection(
collectionName,
options).then();
}
private MongoJsonSchema createSchema() {
return MongoJsonSchema.builder()
.required("num")
.properties(
JsonSchemaProperty.string("num"),
JsonSchemaProperty.bool("isOffline"),
JsonSchemaProperty.string("paymentId"),
JsonSchemaProperty.string("transactionId"),
JsonSchemaProperty.object("delivery").properties(
JsonSchemaProperty.string("firstName"),
JsonSchemaProperty.string("lastname"),
JsonSchemaProperty.string("address"),
),
JsonSchemaProperty.date("creationDate"),
JsonSchemaProperty.string("status"),
JsonSchemaProperty.array("events").items(
JsonSchemaObject.object()
.properties(
JsonSchemaProperty.string("name"),
JsonSchemaProperty.string("userId"),
JsonSchemaProperty.date("date")
)
)
)
.build();
}
}
Il à plusieurs étapes ici :
- J’importe la dépendance ReactiveMongoTemplate pour interagir avec l’api Mongo.
- Dans le @BeforeExecution de ma migration, je supprime la collection si elle existe.
- Dans @execution je crée la collection avec son schéma de validation.
Pour écrire les changements, JsonSchemaProperty est très pratique, cette objet java permet une bonne lisibilité et maintenabilité.
L’écriture en json est très flexible et sa combinaison avec JsonSchemaProperty est puissant dans l’écriture de vos changements.
Mise à jour
MongoDb ne disposant pas d’API permettant de mettre à jour, on devra donc l’écraser en utilisant une commande mongo.
Voici un exemple de mise à jour du schéma de commande :
package fr.company.product.myservice.changes;
@ChangeUnit(id = "update", order = "002", author = "Tanguy B.")
public class Migration20240830 {
private final ReactiveMongoTemplate template;
private Boolean orderCollectionExist;
public Migration20240830(ReactiveMongoTemplate template) {
this.template = template;
}
@Execution
public void execution() {
this.updateCollectionSchema("orders").block();
}
public Mono<Void> updateCollectionSchema(String collectionName) {
var updatedSchema = createUpdatedSchema();
CollectionOptions options = CollectionOptions.empty()
.validator(Validator.schema(schema));
return template.collectionExists(collectionName)
.flatMap(exists -> {
if (exists) {
var command = Document.parse("{ collMod: '" + collectionName + "', validator: " + updatedSchema.toDocument().toJson() + " }");
return Mono.from(template.getMongoDatabase().block().runCommand(command)).then();
} else {
return template.createCollection(collectionName, options).then();
}
});
}
public MongoJsonSchema createUpdatedSchema() {
return MongoJsonSchema.builder()
.required("num")
.properties(
JsonSchemaProperty.string("num"),
JsonSchemaProperty.bool("isOffline"),
// ...
// address, delivery...
// et toutes vos modifications
)
.build();
}
}
L’instruction Mongo qui va vous permettre de mettre à jour le schéma de validation:
{ collMod: ‘orders’, validator: « {votreSchema} » }
Les + de Mongock
J’ai trouvé très confortable d’écrire les migrations en java/kotlin avec un gain sur le typage plutôt qu’en xml ou yaml. Je retiens une documentation claire et un outil facile à prendre en main.
Les – de Mongock
L’inconvénient que j’y trouve est sa limitation aux bases noSQL.
Mongock n’est, à ce jour, pas encore intégré dans l’écosystème JavaScript ou Python, contrairement à Liquibase qui prend en charge plusieurs langages, ce qui limite l’usage de Mongock aux projets basés sur la JVM.
Conclusion
Mongock est un outil qui nous a permis de faire évoluer notre schéma de validation au fur et à mesure du projet. Nous l’avons aussi utilisé pour mettre à jour certaines informations liées aux commandes.
Mongock répond donc à notre besoin et permet l’approche reactive.
Sources
https://github.com/mongock/mongock-examples
https://bootify.io/mongodb/mongock-in-spring-boot-maven.html
https://dev.to/omaryaya/5-steps-to-use-mongock-for-mongodb-changelogs-42ho