Naar de inhoud
Inzichten

Flow control met state machines in Laravel

e-commerce flow

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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

 

Meer inzichten

  • SymfonyCon 2024: code in harmonie

    Editie 2024 van SymfonyCon vond plaats in het prachtige Wenen, dus een van onze experts ging ter plaatse. Even de nachttrein op, wat cultuur opsnuiven, en dan: volop focussen op twee dagen vol Symfony. Onze inzichten lees je in dit verslag! 

    SymfonyCon 2024: code in harmonie
  • Qodo: een AI-copiloot voor coderen en testen

    Recent kwamen we Qodo op het spoor: een tool die met behulp van Artificiële Intelligentie (AI) ons kan helpen coderen en testen. In deze blog lees je ons relaas van de eerste ervaringen.

    Qodo: een AI-copiloot voor coderen en testen
  • Lunar en Codana slaan de handen in elkaar

    Lunar en Codana slaan de handen in elkaar en gaan samen verder onder de naam Codana. Deze fusie creëert een digital product studio met meer dan 30 experts en een duidelijke ambitie: uitgroeien tot een toonaangevende speler op de Belgische en Europese markt.

    Lunar en Codana slaan de handen in elkaar
  • Het nieuw avontuur van Lore bij Codana

    Lore Vanderlinden vertelt je alles over haar avontuur bij Codana. Ze combineert een technische achtergrond als front-end ontwikkelaar met een passie voor ondernemen in haar rol als projectmanager. Ontdek hoe dat in zijn werk gaat in deze blog! 

    Het nieuw avontuur van Lore bij Codana
  • Van Stagiair naar Digital Project Manager bij Codana

    Wat begon als een zoektocht naar een stageplek, groeide uit tot een kans die Jelmer nooit had durven dromen: een vaste rol in een dynamisch en ambitieus bedrijf. Lees hier alles over zijn traject bij Codana.

    Van Stagiair naar Digital Project Manager bij Codana
  • Cross-platform applicaties met React Native

    Nog nooit was het ontwikkelen van native mobiele applicaties zo toegankelijk als vandaag. Bij Codana doen we dit door gebruik te maken het React Native, een open-source framework dat werd ontwikkeld door Meta.

    Cross-platform applicaties met React Native