- Blog Xebia France - http://blog.xebia.fr -
Le pattern Specification pour la gestion de vos règles métier
Posted By Nicolas Lecoz On Mardi 29 décembre 2009 @ 8:27 In Java / JEE | 22 Comments
Souvent lorsque l’on parle de gérer les règles métiers, on pense à moteur de règle, pas forcement …
Le design pattern Specification est une solution de gestion de vos règles métiers.
Ce pattern a été formalisé par Eric Evans, père du DDD, et Martin Fowler que l’on ne présente plus.
Ce pattern est simple mais très puissant. Il permet de :
Cette solution a récemment été mise en place sur un gros site d’eCommerce en France. Je partage ici avec vous mon retour d’expérience, donc laissez vous convaincre !
Prenons un exemple simple mais significatif, un site d’eCommerce de fruits et légumes.
Supposons que l’on parte d’un besoin simple, exprimé par Ted, notre expert fonctionnel : « En tant qu’internaute, je veux ajouter des produits (donc des fruits ou/et légumes) à mon panier ».
Bob, Le valeureux développeur, n’hésite pas un seul instant et réalise la besogne de la manière suivante :
Collection<Produit> panier = new Set<Produit>(); panier.add(new FraiseDePlougastel(5, Unite.BARQUETTE)); panier.add(new PommeDeTerreBinch(2, Unite.KILO));
Le développeur se dit « Trop facile pour que se soit ça … », réfléchit, » Je sais, on peut pas ajouter à l’infini ! On doit de se limiter à une valeur réaliste, par exemple 10 Kilogrammes de produit par panier »
Bob aime le travail bien fait et décide de refactorer son code de la manière suivante afin de s’adapter au mieux à une solution réaliste :
public class Panier {
private static final double LIMITE_DE_POIDS = 10.0;
private Collection<Produit> contenu = new Set<Produit>();
public double getPoidsEnKilo() {
...
}
public void ajouter(Produit produit) {
// TODO Bob : gerer pre condition suivant besoin de Ted
if (getPoidsEnKilo() + produit.getPoidsEnKilo() < LIMITE_DE_POIDS) {
contenu.add(produit);
}
}
}
Une fois le refactoring effectué, Bob s’empresse de remonter le problème à son expert fonctionnel préféré. Ted, ne niant pas la faille, indique à Bob qu’il va revoir son besoin. Mais que pour cela il a besoin d’un peu de réflexion.
Le temps de la réflexion pris, Ted revient vers Bob avec un besoin affiné :
« En tant qu’internaute habitant en Ile de France, je veux ajouter jusqu’à 100 kilos de produit à mon panier »
« En tant qu’internaute de France métropolitaine, je veux ajouter jusqu’à 25 kilos de produit à mon panier »
« En tant qu’internaute des DOM/TOM, je veux ajouter jusqu’à 10 kilos de produit supportant le transport en avion à mon panier »
Bob, devenu tout blanc : « Ça va être compliqué à implémenter »
Ted : « Oui, mais j’ai un super transporteur en ile de france, commercialement je ne peux pas m’en passer »
Ted, pour rassurer Bob : « T’inquiètes pas dans 3 mois, on changera tout ca, on aura de nouveaux transporteurs »
Bob : « Ah, mais on sera déjà en production … »
Les affaires se compliquent pour le pauvre Bob, comment va t’on pouvoir l’aider?
Tout d’abord, une application « n’est qu’une » succession d’évaluations de règles métiers qui sont structurées et hiérarchisées.
Ces règles sont de complexité et de durée de vie très variable.
En discutant avec des référents fonctionnels, j’ai pu sentir une certaine frustration sur la gestion des règles métiers. Cette frustration se ressent encore plus sur les micros règles que l’on change fréquemment.
En effet, ils aimeraient avoir une forte visibilité sur ces règles métiers : « est ce que la documentation décrit correctement le code? ». De plus, ils aimeraient avoir une plus grande réactivité sur l’évolution de ces règles.
Sans formalisme pour gérer toutes ces règles mouvantes, il y a une probabilité importante pour que l’on introduise une dette technique dans l’application :
Tout le monde a autour de lui un exemple d’abandon de code. C’est un cercle vicieux. En effet, moins on connait un code et moins on va essayer de le modifier ; moins on va essayer de le modifier et moins on connaîtra le code … (bouclez ! on se retrouve à la réécriture complète de ce code).
Pour les règles métiers qui mettent en avant une stratégie commerciale cela n’est pas acceptable.
Il faut avoir une grande réactivité et une très bonne gestion du changement. Il faut donc une excellente connaissance de l’implémentation de la part des développeurs et des fonctionnels.
Ainsi, idéalement un expert fonctionnel aimerait :
Pour faciliter la maintenance d’un tel modèle, la solution suivante peut être mise en place : l’utilisation du Pattern Specification.
L’implémentation d’une règle métier suit le contrat d’utilisation suivant :
public interface Specification<T> {
public boolean isSatisfiedBy(T candidate);
public Specification<T> or(Specification<T> specification);
public Specification<T> and(Specification<T> specification);
public Specification<T> not();
}
une méthode issatisfiedby détermine si la règle métier est respectée. les trois autres méthodes, or, and et not permettent de combiner les règles métiers entre elles.

Il y a trois classes utilitaires permettant d’implémenter les opérateurs :
AndSpecificationOrSpecificationNotSpecificationPar exemple la classe AndSpecification :
public class AndSpecification<T> extends AbstractCompositeSpecification<T> {
@Override
public boolean isSatisfiedBy(final T candidate) {
boolean result = true;
for (Specification<T> specification : this.specifications) {
result &= specification.isSatisfiedBy(candidate);
}
return result;
}
public AndSpecification(Specification<T>... specifications) {
super(specifications);
}
}
Les règles métiers étendent la classe LeafSpecification :
public abstract class LeafSpecification<T> extends AbstractCompositeSpecification<T> {
public abstract boolean isSatisfiedBy(T candidate);
}
La règle RegleProduitsDuPanierSontDeSaison permet de déterminer si tous les produits du panier sont de saison.
public class RegleProduitsDuPanierSontDeSaison extends LeafSpecification<Panier>{
public boolean isSatisfiedBy(Panier panier) {
for (Produit produit : panier.getProduits()) {
if (!Mois.estDeSaison(produit.getMoisDeSaison())) {
return false;
}
}
return true;
}
}
La règle RegleProduitsDuPanierSontOranges permet de déterminer si tous les produits du panier sont oranges.
public class RegleProduitsDuPanierSontOranges extends LeafSpecification<Panier> {
public boolean isSatisfiedBy(Panier panier) {
for (Produit produit : panier.getProduits()) {
if ( ! (produit instanceof Abricot
|| produit instanceof Carotte
|| produit instanceof Citrouille
|| produit instanceof Mandarine
|| produit instanceof Orange)) {
return false;
}
}
return true;
}
}
La règle ReglePromoJAimeLesProduitsOranges se repose sur la composition des deux règles précédentes. Si les deux règles précédentes sont respectées alors la commande du client sera éligible à la promotion J’aime les produits oranges !
public class ReglePromoJAimeLesProduitsOranges extends LeafSpecification<Panier> {
RegleProduitsDuPanierSontOranges regleProduitsDuPanierSontOranges = new RegleProduitsDuPanierSontOranges();
RegleProduitsDuPanierSontDeSaison regleProduitsDuPanierSontDeSaison = new RegleProduitsDuPanierSontDeSaison();
public boolean isSatisfiedBy(Panier panier) {
return regleProduitsDuPanierSontDeSaison.and(regleProduitsDuPanierSontOranges).isSatisfiedBy(panier);
}
}
La solution est fiable, si et seulement si les développeurs et les fonctionnels connaissent précisément l’implémentation de ces règles.
Il faut donc une solution rigoureuse. La solution ne sera rigoureuse que si le développeur est rigoureux dans sa documentation : une modification dans le code implique une modification dans la documentation. Les règles métiers sont volontairement limitées à un nombre de traitements restreints afin d’alléger ce travail de documentation. Par la même occasion, la documentation sera moins longue et donc plus abordable pour un humain.
Voici un exemple de patron de documentation pour une règle métier :
| Nom | Nom de la classe qui correspond à la classe en Java (donc sans d’accent, sans espace, et qui ne commence pas par un chiffre) |
| Cas d’utilisation | Où on utilise cette règle |
| Dépendance | Il est possible de combiner les règles. Une règle peut être simplement une combinaison d’autres règles |
| Données en entrée | Quelles sont les données analysées |
| Description & algorithme métier | Décrire le plus précisément possible la règle métier (à la valeur près) |
| Configurabilité | Indiquer si la règle est modifiable, activable, desactivable à chaud |
Les règles métiers ont intérêt à être classées par package, un package par domaine fonctionnel.
Cette solution a été mise en place sur un projet d’eCommerce à des fins de maintenance et de réactivité aux demandes d’évolution. Après 6 mois, l’équipe est satisfaite du résultat voire même un peu bluffée.
En effet, les équipes fonctionnels avaient émis un besoin fort de pouvoir gérer les règles.
Pour cela, l’équipe technique a entrepris une étude. Cette étude avait une double fonction :
Lors de l’étude, différents moteurs de règle Drools, Jess, Java Rules Engine, Groovy Rules, etc., ont été comparés. Aucune solution, hormis Drools, ne nous semblait fiable. Drools était quant à lui trop complexe pour notre besoin.
La solution pattern est intéressante car conceptuellement et techniquement très simple.
Si les développeurs et les fonctionnels jouent le jeu de la documentation, alors on a une vraie maitrise des règles métiers. Ce dernier point reste le plus risqué mais cela ne dépend que des acteurs du projet.
De plus, cette solution nous a permis de gagner du temps :
Le pattern Specification est une alternative sérieuse au choix d’un moteur de règle. En effet, bien souvent l’utilisation de ce dernier est une solution démesurée par rapport à la complexité du besoin exprimé. Elle est moins risquée technologiquement (pas de formation, pas d’inconnue technique), moins couteuse et plus simple.
En agrémentant avec un peu de code utilitaire, il est facilement possible de rendre plus fonctionnelle notre modeste solution de gestion de règles métiers :
Article printed from Blog Xebia France: http://blog.xebia.fr
URL to article: http://blog.xebia.fr/2009/12/29/le-pattern-specification-pour-la-gestion-de-vos-regles-metier/
Click here to print.