Ga naar inhoud

External tasks (worker-principe)

Liever geen PHP? Gebruik een config-gestuurde worker

De meeste workers kun je zonder PHP als JSON definiëren — zie Dynamische workers. Schrijf alleen een PHP-handler (hieronder) als geen enkel action-type volstaat. Eigen handlers horen in development/handlers/; de generieke zaak-*/notify-*-handlers zijn platform-reusables in portal.

Hoe BPMN-processen werk laten uitvoeren door het portaal, via Operaton external tasks en een dispatcher met auto-discovery.

Het idee

Een BPMN-service-task met type = external voert zelf geen code uit; hij zet een job klaar op een topic. Een worker haalt die job op (fetchAndLock), doet het werk, en meldt de taak af (complete) of laat 'm falen (failure, met retry). Het topic is dus alleen een contract/naam — de code zit in de worker.

BPMN service task            Operaton-engine           Portaal (dispatcher)
type=external          ──▶   job op topic       ──▶    fetchAndLock(topic)
topic="notify-admin"                                   → handler.handle()
                                                       → complete / failure

Architectuur in dit portaal

Niet één worker-proces per topic, maar één dispatcher die alle handlers automatisch ontdekt:

  • app/Console/Commands/ExternalTaskWorkerCommand.php — de dispatcher (php artisan operaton:external-task-worker). Draait altijd als supervisord- programma external-task-worker. Hij scant app/ExternalTasks/, abonneert in één fetchAndLock op álle topics, en stuurt elke taak naar de juiste handler.
  • app/ExternalTasks/TopicHandler.php — het contract dat elke handler extend.
  • app/ExternalTasks/*Handler.php — de handlers (de eigenlijke code per topic).

Gevolg: een nieuwe worker toevoegen = één handler-class erbij + deployen. Geen wijziging aan supervisord.conf, geen extra proces, geen UI.

Een nieuwe worker maken

  1. Teken in de modeler (Beheer → Processen) een service task, zet rechts Implementation = External en een Topic (bv. mijn-topic).
  2. Maak app/ExternalTasks/MijnHandler.php:
<?php

namespace App\ExternalTasks;

class MijnHandler extends TopicHandler
{
    public function topics(): array
    {
        return ['mijn-topic'];           // één of meer topics
    }

    public function variables(): array
    {
        return ['objectUrl', 'foo'];     // procesvariabelen die je nodig hebt
    }

    public function handle(array $task): array
    {
        $objectUrl = $this->var($task, 'objectUrl');   // lees een procesvariabele
        // ... doe het werk (gooi een exception om de taak te laten falen) ...
        return ['resultaat' => 'ok'];    // procesvariabelen terug naar het proces
    }
}
  1. Commit, push en open een PR. Na het mergen rolt de serverbeheerder de nieuwe image uit (developers hebben geen terminaltoegang); de dispatcher pikt mijn-topic dan automatisch op. Zie Een formulier/proces live brengen.

Submappen mogen — de discovery is recursief. Handlers zijn per domein ingedeeld; de namespace volgt het pad (PSR-4, PascalCase-mappen):

app/ExternalTasks/
├── TopicHandler.php                 (de basisklasse)
├── Autorisatieverzoek/             (handlers van het autorisatieverzoek-proces)
├── Zgw/                            (zaakgericht werken: zaken, documenten, ...)
└── Common/                         (handlers die meerdere processen delen)

Zet een nieuwe handler in de passende map (of maak er een nieuwe aan); de dispatcher vindt 'm ongeacht de diepte.

Het contract (TopicHandler)

Methode Verplicht Doel
topics(): array ja op welk(e) topic(s) de handler luistert
handle(array $task): array ja het werk; retour = procesvariabelen; throw = taak laten falen
variables(): array nee welke procesvariabelen meegehaald worden (default geen)
lockDuration(): int nee lock-duur in ms tijdens verwerking (default 60000)

Hulpfunctie *$this*->var($task, 'naam', $default) leest een procesvariabele uit de taak.

Afronden, falen & idempotentie

  • handle() geeft een array terug → de dispatcher doet completeExternalTask met die variabelen.
  • Gooit handle() een exception → externalTaskFailure met retries − 1 en een backoff (60s). Bij 0 retries ontstaat er een incident (zie Beheer → Incidenten).
  • Maak handle() idempotent. Als het werk slaagt maar het afronden faalt, wordt de taak na de lock opnieuw opgepakt. Voorbeeld: ZaakAanmakenHandler checkt eerst of er al een zaak bestaat voor de of_referentie.

Bestaande handlers

Topic(s) Handler Doet
notify-admin, notify-approved, notify-rejected Autorisatieverzoek/MailNotificationHandler verstuurt de autorisatieverzoek-mails
zgw-zaak-aanmaken Zgw/ZaakAanmakenHandler maakt een zaak in Open Zaak uit een verzoek-inzending

Operationeel (serverbeheerder)

Deze checks draaien op de server en zijn voor de serverbeheerder (developers hebben geen terminaltoegang):

  • Status: docker exec portal-portal-1 supervisorctl statusexternal-task-worker RUNNING.
  • Logs: de dispatcher logt per taak topic: afgerond of topic: fout - ... (docker logs portal-portal-1).

De BPMN gebruikt de camunda:-namespace (camunda:type, camunda:topic); Operaton accepteert die. Zie Formulieren-tips voor de modeler-kant.