Onlangs kwam ik voor de eerste keer in aanraking met finite state machines in Laravel terwijl ik onderzoek deed naar een implementatie voor één van onze klanten. In deze blog wil ik kort uitleggen wat zo’n state machine inhoudt en waarvoor we zijn functionaliteiten kunnen gebruiken.

Dus wat is het juist?

Om het in de woorden van wikipedia uit te leggen: een finite state machine is een abstract, wiskundig model voor het gedrag van een systeem waarbij het model bestaat uit een eindig aantal toestanden, overgangen tussen die toestanden en acties.

Simpel gezegd willen we dat onze applicatie een beperkt aantal uitkomsten aanbiedt aan de gebruiker. Tegelijkertijd willen we ook de mogelijkheden beperken om deze uitkomsten te bereiken. Een goed voorbeeld is de flow die je moet doorlopen wanneer je online een bestelling wil afronden. Een logische volgorde is bijvoorbeeld dat een gebruiker eerst zijn persoonsgegevens ingeeft, dan wordt doorgestuurd naar het betalingsplatform en daarna een bevestiging krijgt in zijn mailbox. Je wil uiteraard niet dat de gebruiker de betaalstap kan overslaan en zijn order onmiddellijk kan bevestigen.

Klinkt goed, ik wil een voorbeeld!

Laten we ons bovenstaande voorbeeld uitwerken. Om de state machine te implementeren in onze laravel applicatie gebruik ik de package laravel-state-machine van sebDesign. Het eerste wat we moeten opstellen is een state machine graph, dit laat onze applicatie weten welke flow hij moet volgen en dankzij de package kunnen we dit eveneens onmiddellijk koppelen aan ons laravel model.

<?php

return [
    'order_graph' => [
        'class' => Order::class,
        'graph' => 'order_graph',
        'property_path' => 'state',
        'metadata' => [],
        'states' => [
            'checkout',
            'pending-user-data',
            'pending-payment',
            'finished',
            'cancelled'
        ],
        'transitions' => [
            'create' => [
                'from' => ['checkout'],
                'to' => 'input-user-data'
            ],
            'input-user-data' => [
                'from' => ['pending-user-data'],
                'to' => 'pending-payment'
            ],
            'payment-succeeded' => [
                'from' => ['pending-payment'],
                'to' => 'finished'
            ],
            'payment-failed' => [
                'from' => ['pending-payment'],
                'to' => 'cancelled'
            ]
        ],
    ],
];

Voor deze simpele use-case kunnen we vijf states aanmaken en de bijhorende transities definiëren. Deze transities kunnen we nu gebruiken om onze state te updaten en onze logische flow aan te houden. Dit creëert leesbare en makkelijk onderhoudbare entrypoints voor onze functionaliteiten.

Extra functionaliteiten voor elke transitie

In veel gevallen zal je extra functionaliteiten willen toevoegen voor of na je transities. In de state graph is het mogelijk om callbacks in te stellen waarop je kan inhaken om zo extra code uit te voeren. Je wil de bestelling misschien nog valideren vooraleer je verder gaat naar een volgende stap of misschien wil je automatisch een bevestigingsmail versturen wanneer de betaling geslaagd is? Laten we dieper ingaan op deze twee voorbeelden.

    'callbacks' => [
        'guard' => [
            'quard_on_creating' => [
                'on' => 'create',
                'do' => ,
                'args' => ['object'],
            ],
        ],
        'after' => [
            [
                'on' => 'payment-succeeded',
                'do' => 'event',
                'args' => ['"payment-succeeded"', 'event', 'object'],
            ],
        ]
    ]

In dit stukje code hebben we deze twee voorbeelden uitgewerkt. In het eerste deel willen we vooraleer we om de persoonsgegevens vragen, eerst de bestelling valideren. Hiervoor stellen we een guard in die in MyService de bestelling nogmaals zal nakijken op fouten. Als argument sturen we hier het Orderobject mee dat we wensen te valideren. Wanneer bepaalde producten niet meer beschikbaar zijn, kunnen we op deze manier mogelijks een bestelling stopzetten en de koper hiervan op de hoogte brengen.. 

In het tweede deel, sturen we een event uit wanneer onze betaling geslaagd is. We kunnen hierop makkelijk meerdere subscribers instellen die verschillende functionaliteiten gaan toepassen. Een van deze subscribers kan de bevestigingsmail afhandelen terwijl een andere een notificatie van de nieuwe bestelling geeft aan ons extern boekhoudprogramma. 

Wanneer zou jij het gebruiken?

Dus, wanneer zou ik het gebruiken? Altijd wanneer een entiteit in je code meer dan twee states heeft!

Na mijn eerste contact met deze architectuur heb ik state machines in verschillende projecten geïmplementeerd. In mijn ervaring helpen ze een ingewikkelde flow te verduidelijken. De callbacks bieden mij een goede manier om complexe code op te splitsen en zorgen ervoor dat deze eveneens makkelijk uitbreidbaar is. Dit resulteert in meer onderhoudbare code en zorgt voor een duidelijke seperation of concerns

 

Auteur: Jinse Camps
Fullstack developer
Jinse Camps

More insights

Adressen register & GEOpunt API

Zelf al eens voor de keuze gestaan of je Google maps, openstreetmaps, of een andere GIS provider zou moeten gebruiken voor adres suggesties? Mocht je enkel Belgische adressen nodig hebben, lees dan zeker verder!

Auteur: Noah Gillard
PHP / Laravel Developer
Noah Gillard AI generated Face
Logo vlaamse overheid

Next.js: Het zoveelste framework?

Zowat om de 10 jaar komt er nieuwe technologie boven drijven, die een verandering teweeg brengt in de manier waarop we software ontwikkelen. Is Next.js deze nieuwe technologie?

Auteur: Dries Cappon
UX, Design, React.js, Next.js
Dries Cappon
Next.js

Laravel 10 release

The release of Laravel 10: The king of types. Alles wat je moet weten over de nieuwe major release!

Auteur: Noah Gillard
PHP / Laravel Developer
Noah Gillard AI generated Face
laravel 10 banner

Saloon - Package / SDK API integrations

API integraties voor een waaier aan services. Weinig tot geen herbruikbare code? Met Saloon maak je er een compacte / overzichtelijke SDK/Package van die je kan hergebruiken in al je projecten en kan maintainen op 1 plek.

Auteur: Noah Gillard
PHP / Laravel Developer
Noah Gillard AI generated Face
Saloon hero image

Codana wint Digital Champ of the year award 2022

Codana is de winnaar van de FeWeb Digital Champ award 2022

Auteur: Joris De Groot
Strategic Director and Managing Partner
Joris De Groot
FeWeb Digital Champ award Codana

De ideale eerste werkplek

Ik ben Ward Vandevoort, 22 jaar en begin november 2021 ben ik gestart bij Codana als junior backend developer. Ontdek mijn verhaal bij Codana! 

Auteur: Ward Vandevoort
Developer
Ward Vandevoort
groepsfoto