Activez le simulateur C'est parti !
Actualités

Mettez à jour vos schémas de validation simplement avec Mongock

Oct 21, 2024 · 7 minutes read

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 : 

  1. J’importe la dépendance ReactiveMongoTemplate pour interagir avec l’api Mongo.
  2. Dans le @BeforeExecution de ma migration, je supprime la collection si elle existe.
  3. 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://mongock.io

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

Tanguy BERNARD
Tanguy BERNARD