Aller au contenu

Changement de l'ORM pour gérer nos entités

Contexte

Depuis le début du projet nos entités sont structurées autour du concept d'Aggregate du Domain Driven Design. Chaque Aggregate est responsable de l'intégrité de ses données et de celles de ses relations. Les objets qui sont liés dans un Aggregate lui appartiennent, par conséquent ces objets ne peuvent pas être utilisés dans d'autre Aggregate. Si on veut réutiliser une donnée d'un de ces objets alors il faut le dupliquer.

Cette structure permet de ne pas avoir de liens trop forts au système de persistence. Le but est de pouvoir référencer entre Aggregate de différents types stockés dans des stockages distincts. (Par exemple avoir un id qui pointe sur un Processus Workey dans un Document)

Pour la persistence on utilise doctrine/orm. Cet ORM est conçut autour du fait de représenter les relations bases de données en relations entre objets. On contourne donc son comportement pour avoir notre structure objet.

Ce choix nous posent plusieurs problèmes :

  • erreur d'ordre des opérations à effectuer en bdd
  • doctrine conserve des relations d'Aggregate à leur suppression
  • gestion de la mémoire compliquée car doctrine garde tout en mémoire

Pour résoudre ces problèmes nous envisageons de basculer sur l'ORM formal/orm qui est conçu autour du concept d'Aggregate et de la gestion mémoire.

Nous avons réalisé un POC de cet ORM pour le comparer à Doctrine. Ce dernier est défini dans ce ticket.

Résultats du POC

Sur un import de masse de données Formal est 2 fois plus rapide que Doctrine et consomme 38% moins de mémoire.

Sur un listing de masse de données Formal est 2 fois plus lent que Doctrine. Au vu des abstractions qu'utilise Formal cette différence risque de ne pas être résorbable.

Dans le cas d'une mise à jour d'un Aggregate Formal est exponentiellement plus lent que Doctrine pour mettre à jour une relation dans une collection (au plus il y a de données au plus la mise à jour est longue). Ce problème est un soucis connu de Formal, mais des optimisations de l'outil permettront de le résorber en partie.

Dans chacun des tests on n'a plus besoin de penser à la gestion de la mémoire avec Formal.

Décision

Le changement d'ORM est une étape risquée d'autant plus qu'on n'a pas de recul sur l'utilisation de Formal. Cependant ce dernier nous permet quand même de résoudre nos problèmes actuels.

Le choix est donc d'essayer Formal sur les parties non critiques (pour avoir le moins d'impact en cas d'incidents).

Nous l'utiliserons d'abord sur la gestion des taches asynchrones et sur la gestion d'elasticsearch. Cela nous permettra d'avoir un recul sur son comportement en production sans avoir de grand impact sur la logique métier.

En prévision du refactoring des autres Aggregate, le design des méthodes de ces derniers vont changer. Formal utilise des classes immuables et appellent le repository pour appliquer les modifications. On va donc traiter nos entités Doctrine comme si elles étaient aussi immuables où chaque méthode de modification doit retourner $this et le repository doit être appelé avec l'objet qui est retourné par la méthode. Cela nous permettra de n'avoir qu'à modifier l'implémentation de nos entités le moment où on basculera. Les méthodes Repository::add() devraient être renommées en ::put() pour être le plus proche des repository de Formal.