Des applications modulaires avec l'AOP

Problématique de modularité

La modularité est un point crucial dans le cadre du développement de logiciels de gestion.

Une gestion commerciale, un CRM, un ERP, vont :

  • embarquer de très nombreuses fonctionnalités,
  • être installés dans de nombreuses entreprises, chacun ayant leurs subtilités propres en terme de gestion,
  • au sein de fonctionnalités similaires, on va avoir donc de nombreuses variantes de règles de gestion.

Deux entreprises, même si elles exercent le même métier, vont avoir des approches différentes en terme de gestion.

Si l’on veut développer des logiciels de gestion qui tiennent compte des nombreuses variantes en terme de règles de gestion chez ses différents utilisateurs, on se pose très vite la question de la modularité.

L’AOP, ou programmation orientée aspect, est une approche qui permet d’ajouter du comportement à des fonctionnalités sans que celles-ci ne l’aient explicitement prévues, et sans toucher au code source des fonctionnalités existantes.

Ainsi avec une approche AOP votre logiciel de gestion dispose d’autant d’APIs que de méthodes et de propriétés de classes programmées, mêmes privées.

Implémentation native de l’AOP dans it.rocks

Le framework it.rocks embarque en natif son propre moteur de tissage AOP.

Les buts sont multiples :

  • Le framework est conçu pour une utilisation massive de l’AOP : on doit pouvoir faire fonctionner un ERP puissant avec des milliers de branchements en fonction des variantes de règles de gestion employées.
  • Cette utilisation importante nécessite une implémentation qui assure des performances maximales. Les rares implémentations de l’AOP sérieuses existant en PHP ont des implémentations bourrées d’abstractions qui compliquent le code généré, et multiplient les appels intermédiaires. J’ai voulu que le code qui traduit l’AOP soit le plus efficace possible.
  • L’AOP n’existe pas en natif en PHP : pour permettre au développeur de l’utiliser simplement, on génère des scripts PHP modifiés en cache. Une volonté de rendre ces scripts le plus lisibles possibles, pour les besoins de débogage et de compréhension des mécanismes, a conduit ce développement natif, les abstractions générées par les solutions existantes semblant trop complexes à la lecture, à l’exécution, à maintenir.
  • En maîtriser parfaitement l’implémentation permet d’appliquer les variantes souhaitées au fur et à mesure des besoins.

Pour en savoir plus, des pistes pour comprendre le fonctionnement interne de l’AOP dans it.rocks peuvent aider à comprendre le comportement de votre logiciel. Pour devenir un expert de it.rocks, suivez la série d’articles sur l’AOP (en cours de rédaction) :
Fonctionnement interne de l’AOP et du cache

On distingue l’AOP en tissage statique et en tissage dynamique. Mes recherches ayant conduit à la conclusion que PHP ne permet pas l’implémentation de l’AOP en tissage dynamique (les expérimentations permettant une telle implémentation telles que runkit ou le pourtant prometteur AOP-PHP conduisent inévitablement à des bogues et crash de PHP), l’AOP est implémenté ici en tissage statique, toutefois configurable par activation / désactivation de plugins dans le fichier de configuration de votre logiciel.

Concrètement, que permet de faire l’AOP ?

Dans it.rocks, l’AOP est un des mécanismes que peuvent utiliser les plugins pour modifier le comportement du logiciel existant.

Ainsi :

  • on développe notre logiciel de base sans se préoccuper de son extensibilité, si ce n’est en veillant à un bon découpage du code en appels de méthodes spécialisées concises et stables, en respectant autant que possible le principe de programmation SOLID, notamment Ouvert/fermé appliqué aux classes et en particulier aux méthodes.
  • un plugin se branche sur des méthodes existantes, pour en modifier le comportement.

Des exemples :

Exemple du plugin Mysql Maintainer

Ce plugin, activé par défaut dans le framework, permet de maintenir automatiquement la structure de la base de données principale de votre application.

Notamment il rajoute du comportement lorsqu’une erreur Mysql est remontée : il analyse la requête en erreur et tente de mettre à jour la structure de la base de données avant de relancer la requête.

Pour se faire, sa structure de base annonce très simplement le branchement de cette méthode.

  • Le code qui traite les erreurs Mysql du framework n’a pas connaissance du plugin :
    dans ITRocks\Framework\Tools\Contextual_Mysqli :
    	/**
    	 * @param $query string
    	 * @return mysqli_result|boolean false, but other errors managers may change this
    	 */
    	protected function queryError($query)
    	{
    		if (error_reporting()) {
    			$error = $this->last_errno . ': ' . $this->last_error . '[' . $query . ']';
    			trigger_error('Mysql logger error : ' . $error . ' on query ' . $query, E_USER_ERROR);
    		}
    		return false;
    	}
  • Le plugin lui-même annonce qu’il va rajouter du comportement avant chaque appel à queryError() :
    dans ITRocks\Framework\Dao\Mysql\Maintainer :
    	/**
    	 * @param $register Register
    	 */
    	public function register(Register $register)
    	{
    		$aop = $register->aop;
    		$aop->beforeMethod([Contextual_Mysqli::class, 'queryError'], [$this, 'onMysqliQueryError']);
    	}

Tout simplement donc, à chaque fois qu’une erreur Mysql est remontée, avant d’appeler la méthode qui décide de remonter l’erreur, la méthode onMysqliQueryError() du plugin sera appelée, et réalisera les traitements souhaités décrits plus haut.

Si on active le plugin, ce branchement sera effectué et le code sera exécuté.
Si on n’active pas le plugin, c’est comme si on n’avait jamais voulu brancher le moindre traitement optionnel à queryError() : il s’exécutera simplement, sans que la moindre ligne de code ait été rajoutée pour prévoir un éventuel branchement : contrairement à la programmation par événements qui suppose que la liste des événements qu’on peut écouter soit définis pour qu’on puisse y accéder, ici on pose des écouteurs directement sur les méthodes existantes, mais aussi sur les propriétés d’objets (en lecture ou écriture).

Exemple du plugin Email Archive

Ce plugin a pour but d’archiver tous les mails que vous envoyez en utilisant la méthode Email\Sender::send() du framework.

Nativement, cette méthode ne stocke pas les mails envoyés.

Vous pouvez donc en généraliser l’archivage par un plugin tout simple.
Voir les sources du plugin Email\Archive
La ligne à rajouter en priorité normale de votre fichier de configuration indique que vous activez votre plugin :

		Framework\Email\Archive::class,

Vous avez là un exemple le plus simple qui soit de plugin qui rajoute du comportement au logiciel existant.

A votre tour d’utiliser l’AOP

Voyez la documentation complète concernant l’utilisation de l’AOP dans le framework, pour l’appliquer à vos projets.

Montrer l'historique