Comment sécuriser et pérénniser vos processus métiers avec le composant Workflow de Symfony ?

Image article avec personnage vectorielle

Nous allons débuter cet article en imaginant et en nous plongeant dans un petit scénario.

Imaginez que vous êtes un jeune développeur PHP/Symfony dans une équipe de réalisation et que vous débutez un projet de création de site de E-commerce pour votre client spécialisé dans la distribution de colis de Noël.

Toute votre équipe est donc conviée à participer à une réunion de préparation avec votre client pour définir ses propres besoins et aborder son processus métier qui débute de la confection des colis en usine jusqu’à la livraison chez le client. On vous défie donc pour concevoir et implémenter une solution qui reflètera le coeur de métier de votre client.

Cependant, vous n’avez aucune idée de comment démarrer, les différentes règles énoncées se mélangent et se noient dans votre cerveau, et vous prenez peur face à la tonne d’informations compliquées et spécifiques que votre client vous donne.

Pourtant la nuit tombée, alors que vous n’arrivez pas à dormir, vous repensez à un mot anglais un peu fourre-tout qui pourrait bien être la solution à votre problème : « Workflow ».

Quelle chance ! Vous tapez ce superbe mot dans votre moteur de recherche préféré, et vous tombez sur des résultats qui parlent du composant Workflow de Symfony et ils ont l’air super intéressants !

C’est alors que vous vous posez un tas de questions :

  • Qu’est-ce qu’un workflow ?
  • D’où provient cette notion de workflow ?
  • Comment dois-je procéder pour installer le composant Workflow de Symfony ?
  • Quelle est l’utilité de ce composant et ce qu’il me permet de faire ?

Si vous posez vous aussi ces questions, je vous invite à lire cet article qui vous permettra d’obtenir des réponses pour pouvoir mettre en place ce composant dans votre propre projet et cette fois dans la vraie vie.


1 – Qu-est-ce qu’un workflow ?

C’est la première question que vous devez vous poser, non?

On peut dire qu’un workflow (ou flux de travail) représente « l’ensemble des tâches à accomplir et des différents acteurs impliqués dans la réalisation d’un processus métier ».

Autrement dit, c’est l’ensemble des tâches séquencées qui permettrait à notre client imaginaire d’honorer sa commande de colis de Noël auprès de ses clients.

On voit tout de suite mieux comment le processus métier doit s’articuler non ?

Si vous ne voyez toujours pas, il pourrait se résumer par 6 étapes simples :

  • La création de la commande par le client
  • La préparation du colis en usine
  • L’emballage du colis
  • L’expédition du colis vers le client
  • La livraison du colis vers le client
  • La réception du colis par le client

Ce sont ces différentes étapes ou tâches qui permettent de réaliser le processus métier, et cela nous permet également d’associer des états à notre commande pour en assurer le suivi.

Cela peut donc se modéliser par une machine à états, qui est un modèle mathématique fondateur du composant Workflow de Symfony que nous allons explorer dans la partie suivante.


2 – Théorie des automates et machines à états

Nous allons donc faire un peu de théorie, même si j’ai conscience que ce n’est pas ce que vous préférez, mais cela est important pour comprendre comment articuler notre processus métier.

Nous parlions donc de machine à états, mais nous ne savons pas encore ce que c’est et également le lien qu’elle peut avoir avec le composant Workflow de Symfony.

Une machine à états est un objet mathématique qui nous permet de concevoir des algorithmes pour des processus métiers spécifiques. Chouette, c’est pile ce dont on a besoin !

Elle se compose de deux élements importants que l’on peut voir sur le schéma ci dessous à savoir :

  • Des états (représentés par des cercles) qui décrivent le statut d’un objet dont un seul peut être l’état courant. Pour notre exemple, soit l’ampoule est allumée ou éteinte.
  • Des transitions (représentées par des flèches) qui décrivent les actions à effectuer sous certaines conditions pour passer d’un état à un autre. Pour notre exemple, soit on peut allumer l’ampoule si elle est éteinte, soit on peut éteindre l’ampoule si elle est allumée.

Exemple d’une machine à états simple, un interrupteur.

Ainsi avec cet outil, on est capable de faire un schéma exhaustif de l’ensemble des actions possibles en fonction des états courants, ce qui s’avère important pour cadrer les règles métiers d’un processus.

C’est sur ce principe que se base le composant Workflow de Symfony, c’est pourquoi nous allons installer ce composant.


3 – Installation du composant Workflow de Symfony

Si vous avez déja initié un projet Symfony et que vous disposez du gestionnaire de dépendances Composer, il est très simple d’installer le composant Workflow, il suffit de taper la commande suivante :

composer require symfony/workflow

3.1 – Conception du processus métier et configuration de la machine à états

Une fois votre projet installé avec le composant Workflow, c’est le moment de se creuser les méninges, enfin pas tant que ça!
En effet, nous avons déjà isolé les principales étapes de notre processus métier auparavant et du coup ca devient un jeu d’enfant de mettre en place notre machine à états.

La première étape pour mettre en place une machine à états consiste à créer une entité dans notre projet avec un champ string que nous appelerons state. C’est ce champ qui va pouvoir nous indiquer à quel endroit de la machine à états se trouve notre colis à l’aide de la fonction getState().

Voici un exemple de l’entité Colis pour mieux visualiser:

<?php

namespace App\Entity;

use App\Repository\ColisRepository;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity(repositoryClass=ColisRepository::class)
*/
class Colis
{
    /**
    * @ORM\Id
    * @ORM\GeneratedValue
    * @ORM\Column(type="integer")
    */
    private $id;

    /**
    * @ORM\Column(type="string", length=255, nullable=true)
    */
    private $state;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getState(): ?string
    {
        return $this->state;
    }

    public function setState(?string $state): self
    {
        $this->state = $state;

        return $this;
    }
}

Ensuite, la deuxième étape consiste à aller dans le fichier de configuration ./config/packages/workflow.yaml.

La suite des opérations est relativement simple.
Il suffit d’avoir ses états (places) et notamment son état initial (initial_marking), et ses transitions (transitions) permettant d’aller d’un état à un autre.

Voici l’exemple de la configuration de notre machine à états pour les colis:

framework:
    workflows:
        commande_colis:
            type: 'state_machine'
            marking_store:
                type: 'method'
                property: 'state'
            supports:
                - App\Entity\Colis
            initial_marking: nouvelle_commande
            places:
                - nouvelle_commande
                - en_preparation
                - emballe
                - expedie
                - livre
                - retourne
                - perdu
            transitions:
                preparation:
                    from: nouvelle_commande
                    to:   en_preparation
                emballage:
                    from: en_preparation
                    to:   emballe
                expedition:
                    from: emballe
                    to:   expedie
                livraison:
                    from: expedie
                    to:   livre
                retour:
                    from: [livre, expedie]
                    to:   retourne
                perte:
                    from: [expedie, livre]
                    to:   perdu

Une fois notre configuration correctement remplie, nous allons pouvoir voir ce que nous pouvons en faire et vous allez voir que ça va vous plaire !

3.2 – Export d’une image de la machine à états

Et si nous voyions à quoi ressemble notre splendide machine à états pour gérer nos colis, ça vous dirait non ?

C’est possible grâce à cette commande:

php bin/console workflow:dump commande_colis | dot -Tpng -o gestion_colis.png

Et grâce à cela, nous avons notre belle machine à états avec notre processus métier :

Mais vous devez vous dire, il est mignon lui à afficher sa machine à états, mais il fait quoi concrètement avec !?

Vous avez raison, passons à la pratique !

3.3 – Utilisation de la machine à états

L’intérêt primordial de notre machine à états, c’est de suivre l’état des commandes de chacun de nos colis, et de pouvoir les mettre à jour individuellement.

Pour cela, il suffit simplement d’appliquer les transitions que nous avons définies dans notre configuration pour passer de l’état où le colis se trouve actuellement à un nouvel état.

Par exemple, un colis se trouve dans l’état emballe et que l’on souhaite l’expédier vers le client, il suffit de vérifier et d’appliquer la transition expedition de la manière suivante :

$workflow = $this->get('workflow.commande_colis');

if ($workflow->can($colis, 'expedition')) {
    $workflow->apply($colis, 'expedition');
}

Une fois la transition appliquée, si votre colis était dans l’état emballe son état courant sera expedie. Si ce n’était pas le cas, alors le colis restera dans son état initial.

Comme vous pouvez le voir, cela est relativement simple et permet de passer d’un état à l’autre du processus métier, mais parfois notre processus métier peut être compliqué et on a besoin de plus d’outils pour le retranscrire fidèlement. C’est ce que nous allons voir dans la prochaine partie.

3.4 – Application de règles métiers complexes

En effet, comme décrit plus haut, notre processus métier utilise certaines règles de gestion spécifiques.

Imaginons que nous proposons à notre client de pouvoir retourner son colis sous 7 jours après la date d’expédition s’il n’en est pas satisfait.

Actuellement, notre machine à états telle qu’elle est définie ne nous permet pas de mettre en place cette règle. Mais heureusement, c’est possible d’implémenter cette contrainte grâce aux évènements.

Il nous suffit donc de créer une classe qui implémente l’interface EventSubscriberInterface comme cela :

use Symfony\Component\Workflow\Event\GuardEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class RetourColisListener implements EventSubscriberInterface
{
    public function guardRetourColis(GuardEvent $event)
    {
        /** @var \App\Entity\Colis $colis */
        $colis = $event->getSubject();
        $dateExpedition = $colis->getDateExpedition();
        $now = new \DateTime();

        if ($now->diff($dateExpedition)->format('%a') < 7) {
           $event->setBlocked(true);
        }
    }

    public static function getSubscribedEvents()
    {
        return [
            'workflow.commande_colis.guard.retour' => ['guardRetourColis'],
        ];
    }
}

Une fois cette classe écrite, grâce à l’évenement guard, à chaque fois que le client tentera de retourner un colis, une vérification sera effectuée pour savoir si le colis a été expédié il y a moins de 7 jours. Si ce n’est pas le cas, alors il ne sera plus possible au client de retourner son colis et d’obtenir un remboursement.

Il existe d’autres évènements qui permettent aussi de mieux retranscrire le processus métier de nos applications, en voici la liste :

  • guard : Permet de contraindre et d’appliquer un transition sous certaines conditions.
  • leave : Permet d’exécuter du code lorsque l’on quitte un état de notre machine à états.
  • transition : Permet d’exécuter du code lorsque la transition a été appliquée.
  • enter : Permet d’exécuter du code lorsque l’on va entrer dans l’état de destination, juste avant que l’état courant ne soit modifié.
  • entered : Permet d’exécuter du code lorsque l’on est entré dans l’état de destination, juste après que l’état courant soit modifié.
  • completed : Permet d’exécuter du code lorsque la transition a été effectuée, comme un envoi de mails par exemple.
  • announce : Permet d’annoncer qu’une ou plusieures transitions sont maintenant disponibles à partir du nouvel état.

4 – Conclusion

Dans cet article, je vous ai parlé de workflow, de théorie des automates, plus particulièrement de machine à états et également du composant Workflow de Symfony.

Cela est sans doute très théorique, mais j’espère que vous en retiendrez que ce composant peut vous permettre de prendre au sérieux la partie de conception d’un processus métier pour pouvoir mieux le cadrer et mieux réaliser votre solution.

Je suis bien entendu disponible pour vous guider sur la mise en place de votre workflow, ou même pour répondre aux interrogations que vous pourriez avoir.


Sources

Définition du workflow et machine à états
Documentation du composant Workflow de Symfony
Exemple d’utilisation du workflow
Illustration
Partager cet article
Photo d'un homme de l'équipe de Progicar
Rédigé par Pierre Petillon

Articles suggérés