Ankündigung

Einklappen
Keine Ankündigung bisher.

Persistenz einer Workflow-Engine

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Persistenz einer Workflow-Engine

    Guten Morgen,

    ich habe für mein Framework eine _einfache_ Workflow-Engine entwickelt. Diese basiert im Prinzip lediglich auf einer Workflow-, einer Node- und einer Thread-Klasse.

    Dem Workflow werden per Konfiguration oder manuell die Knoten und ihre Reihenfolge zugewiesen. Ein "Thread" übernimmt sowohl Ausführung als auch Unterbrechen (und Speichern des aktuellen Status) als auch das wiederaufnehmen.

    Wobei ich jetzt allerdings seit Tagen hänge und wo ich mir nicht sicher bin, was das beste ist, ist die Fortführung eines mehrteiligen Vorgangs mit Benutzerinteraktion, wobei auch ein Zurückspringen möglich sein soll.

    Kurz zum Prinzip meines Frameworks, damit meine Ansätze unten verständlicher sind: Das Framework basiert auf HMVC-"Nodes" (Diese haben erst mal nichts mit den Workflow-Nodes zu tun), die vergleichbar mit Controller-Actions aus MVC-Frameworks sind und z. B. per URL angesprochen werden können, z. B. .../cms.page/page/1 Dies soll hier aber nicht zur Diskussion stehen.

    Meine Gedanken bis jetzt (Anhand einer mehrteiligen Registrierung - Adressdaten A, Benutzerdaten B, AGB C):
    Es gibt eine HMVC-Node "Start", die den Workflow startet. Dazu wird im ersten Schritt das Formular für die Adressdaten angezeigt). Die Action dieses Formulars zeigt ebenfalls auf "Start", so dass beim Abschicken erneut die Node A des Workflows aufgerufen wird. Sind die eingegebenen Daten valide, kommt Workflow-Node B, für den wieder ein Formular für die entsprechenden Daten angezeigt wird. Darauf hin folgt wieder serverseitig die Validierung und ggf. der Schritt zu Workflow-Node C.

    Etwas einfacher dargestellt (Workflow-Node: "WN"):
    1. Anzeige WN A
    2. Verarbeitung Daten in WN A
    3. Bei korrekten Daten weiter zu 4. ansonsten zurück zu 1.
    4. Anzeige WN B
    5. Verarbeitung Daten in WN B
    ...

    Jetzt stehe ich erst mal prinzipiell vor der Entscheidung, wie der Status des Workflows aufrecht erhalten werden sollte: per URL oder serverseitig (z. B. per DB oder Session).

    Vorteil URL:
    - Man kann per History zurück gehen
    - Man kann leichter einen "Abbruch"-Mechanismus implementieren
    Nachteil URL:
    - Vielleicht kommt es zu Integritätsproblemen innerhalb des Workflows, wenn man nicht aufpasst

    Vorteil serverseitig:
    - Man hat die volle Kontrolle über den Ablauf
    - Man kann z. B. per Ajax leichter mit dem Client kommunizieren
    Nachteil serverseitig:
    - Man kann nur schlecht einen Abbruch-Mechanismus implementieren
    - Der User kann nicht "Zurück" zum vorher gehenden Schritt gehen.

    Dazu sei noch gesagt, dass die Workflow-Nodes eine Art Verify-Mechanismus implementieren können, mit dem sie prüfen, ob sie anhand des vorhandenen Workflow-Zustandes überhaupt ausgeführt werden dürfen.

    Das sind die beiden Möglichkeiten, die mir bis jetzt in den Sinn gekommen sind. Was haltet ihr für den besseren Weg? Vielleicht hat ja noch jemand eine ganz andere Idee...

  • #2
    Ich bevorzuge die Verflechtung beider Möglichkeiten. Per URL erfolgt die Navigation und in der Persistenzschicht wird der Zustand gespeichert.

    Damit ist es theoretisch möglich, unter Kenntnis der URLs direkt zu einem Schritt im Formular zu springen. Nachdem du einen Verifizierungs-Mechanismus eingebaut hast, musst du nur noch entscheiden, ob per header() auf die Formularseite weitergleitet wird, die als Nächstes ausgefüllt werden kann oder aber, ob unter der eigentlich falschen URL die nächste auszufüllende Seite anzeigt wird. Ich halte die Weiterleitung für das korrekte Vorgehen.

    Es müsste ausreichend sein, die jeweils zuletzt erfolgreich validierte Formularseite in der Persistenzschicht zu speichern. Intern würde ich jeder Node eine inkrementierende ID zuweisen.

    Folgende Annahme:
    i = ID der zuletzt validierten Seite.
    x = ID der Seite, die angezeigt werden soll.
    Danach gilt: Die Seite darf angezeigt werden, wenn x ≤ (i + 1).

    Für den Fall, dass in deinem Beispiel der Nutzer von Schritt "AGB" zu Schritt "Adressdaten" zurückspringen und Eingaben ändern sollte, würde ich definitiv einen erneuten Durchlauf des Formulars erzwingen. Es ist sehr wahrscheinlich, dass zwischen den Seiten Abhängigkeiten bestehen können

    Kommentar


    • #3
      Ja, so klingt das eigentlich ganz gut. Bei mir haben die Workflow-Nodes String-IDs, da es Verzweigungen geben kann. Aber im Prinzip stimmt das mit den IDs. Was mich etwas nervt, ist, dass in jede Node die Verifizierung hinein muss, aber das lässt sich wohl kaum umgehen...

      EDIT: Wobei es bei dieser Methode einfacher werden würde, an beliebige Stellen des Workflows zurück zu springen und man muss auch immer nur die aktuelle Workflow-Node im Status speichern.

      Was mich etwas stört, ist, dass die Nodes bis zu einem gewissen Punkt wissen müssen, welche Rolle sie innerhalb des Workflows spielen. Andererseits wüsste ich auch nicht, wie man das anders lösen könnte..

      Kommentar


      • #4
        Meiner Meinung nach sind die Knoten relativ "dumm". Inwiefern müssen sie wissen, welche Rolle sie spielen?

        Im ersten Beitrag schreibst du von einer Workflow- und Threadklasse. Worin genau unterscheiden sich diese und wieso übernimmt nicht die Workflowklasse die Aufgaben der Threadklasse? Bzw. was hat dich dazu veranlasst, es derart zu trennen? Intuitiv gesehen hätte ich ich Konfiguration, Zuweisung der Reihenfolge, Ausführung, Speicherung etc. in einer Klasse vereinigt.

        Zuletzt würde mich interessieren, wie du das meinst, dass dich die Verifizierung in jeder Node stört? Solange es sich um Benutzereingaben handelt, ist eine Validierung unumgänglich. Ich gehe stark davon aus, dass du eine Formularklasse einsetzt. Im Grunde genommen nimmt sie dir all die Arbeit ab und es muss nur noch isValid() o.ä. aufgerufen werden.

        Ach, und ein Punkt noch: Angenommen der Nutzer möchte vor dem Absenden der Registrierung seinen getätigten Eingaben überprüfen und navigiert nochmals von Seite zu Seite. Werden die vorbelegten Eingaben erneut validiert oder hast du eine Routine implementiert, die überprüft, ob überhaupt Änderungen vorgenommen wurden und dementsprechend die Validierung auslässt oder nicht?

        Kommentar


        • #5
          mh.. Doppelpost

          Kommentar


          • #6
            Inwiefern müssen sie wissen, welche Rolle sie spielen?
            Insofern, als dass sie wissen müssen, ob sie ausführbar sind, weil sie z. B. bestimmte Daten voraussetzen. Was wiederum bedingt, dass sie sich zumindest z. T. ihrer Rolle im Workflow bewusst sein müssen. Aber ich denke, das lässt sich nicht anders lösen..

            Im ersten Beitrag schreibst du von einer Workflow- und Threadklasse. Worin genau unterscheiden sich diese und wieso übernimmt nicht die Workflowklasse die Aufgaben der Threadklasse?
            Der Workflow ist die Abbildung der Definition eines Prozesses, während ein Thread die konkrete Ausführung dieses Prozesses ist. Heißt: Wenn sich ein Benutzer in der o. g. Registrierung befindet, dann kümmert sich der Thread z. B. um die Speicherung der Daten dieses Prozesses.
            Der Workflow ist quasi das Programm, der Thread die Ausführung.
            Kann sein, dass die Namensgebung etwas ungünstig gewählt ist, aber mir schien das so plausibel zu sein.

            ist eine Validierung unumgänglich. Ich gehe stark davon aus, dass du eine Formularklasse einsetzt. Im Grunde genommen nimmt sie dir all die Arbeit ab und es muss nur noch isValid() o.ä. aufgerufen werden.
            Ja, ist im Prinzip richtig und ich denke, darauf wird es auch hinauslaufen.

            Werden die vorbelegten Eingaben erneut validiert oder hast du eine Routine implementiert, die überprüft, ob überhaupt Änderungen vorgenommen wurden und dementsprechend die Validierung auslässt oder nicht?
            So weit bin ich noch nicht Allerdings wird ja der Status des Threads und die dazugehörigen Daten gepeichert, so dass die Nodes dann wissen, ob sie bereits aufgerufen wurden. Der Workflow stellt eine Methode getNextNode bereit und anhand derer kann man dann direkt wieder zum letzten Schritt springen, da alle vorherigen ja bereits durchgeführt wurden.
            Also so:
            1. Adressdaten -> done
            2. Benutzerdaten -> done
            3. AGB -> nochmal zur Überarbeitung zu 1.
            4. Jetzt werden von Nr. noch mal alle Nodes durchgegangen, ob diese mit den geänderten Daten noch valide sind. wenn ja, geht es weiter zur jeweils nächsten Node, ansonsten bleibt der Prozess bei der nächsten, nicht validen Node hängen.

            EDIT: Das ist jedoch im Moment nur die Vorstellung. implementiert ist dies so noch nicht. momentan würde der Prozess dann von der Stelle neu anfangen, wo man sich gerade befindet. Das ist allerdings nicht soooo schlimm, da ja alle Daten noch da sind, und man sich im besten Fall einfach bis zum Ende durchklicken kann.

            Kommentar


            • #7
              Nutzt du die Workflows nur für "Short-Running" Workflows, wie im Beispiel? Oder sollen da auch mal "Long-Running" Workflows laufen, die auch mehrere Wochen dauern können (Freigaben oder solche Geschichten)? Bei den längeren führt um eine Speicherung in der Datenbank ja kein Weg herum. Implementierst du also jetzt keinerlei Hooks / Strukturen für die Persistierung bekommst du später u.u. ein Problem.

              Bei uns wird gibt es Workflow-Vorlagen, die dann jeweils instanziiert werden. Jeder Knoten kennt Vorgänger und Nachfolger und gibt die Kontrolle bei Erledigung oder Abbruch entsprechend weiter. Workflows lassen sich zusätzlich ineinander verschachteln. Damit kann man eigentlich fast alles abbilden.

              Als Inspiration kann man sich die Workflow Foundation bei .NET anschauen.

              Kommentar


              • #8
                Nutzt du die Workflows nur für "Short-Running" Workflows, wie im Beispiel? Oder sollen da auch mal "Long-Running" Workflows laufen
                Eigentlich sollte beides abbildbar sein.
                Bei den längeren führt um eine Speicherung in der Datenbank ja kein Weg herum.
                Denke ich auch.
                Implementierst du also jetzt keinerlei Hooks / Strukturen für die Persistierung bekommst du später u.u. ein Problem.
                Stimmt - das wäre vielleicht auch eine Möglichkeit. Statt dem Thread das Speichern zu überlassen, lieber eine Art Manager von außen, der auf Events, z. B. nach dem Ausführen einer Node reagiert. Meinst Du so etwas?
                Jeder Knoten kennt Vorgänger und Nachfolger
                Hm.. Das halte ich für problematisch. Dann wäre es ja schwierig, die Knoten mehrfach zu verwenden. Und es müssen ja auch Branches möglich sein. Wie bildet ihr das ab?

                Kommentar


                • #9
                  Für das Speichern gibt es ja nur zwei Möglichkeiten: Entweder die Knoten speichern sich selbst oder sie Schicken ein Event, dass sie jetzt gerne gespeichert werden möchten

                  Jede Aktivität ist eine eigene Klasse, die von einer Basisklasse erbt. Eine Parallelisierung kennt halt nicht nur ihren Nachfolger (der nächste Knoten NACHDEM die Äste wieder zusammenlaufen), sondern auch die jeweils ersten Knoten der Äste. Beim If-entsprechend.
                  Wie man den Flowchart des Workflows nun genau abbildet, dazu gibt es ja sicherlich einiges an Literatur. Unser Ansatz ist da eher aus dem Bauch heraus, als theoretisch fundiert

                  Behandelt man den Fluss im Workflow und nicht in den Knoten muss man immer, wenn man etwas an einer Aktivität ändert, an den Workflow ran. IMHO nicht gerade ideal für die Wartbarkeit.

                  Kommentar


                  • #10
                    Verstehe ich das richtig, dass ihr den Workflow also nur innerhalb der Knoten abbildet? Das ist natürlich auch ein verführerischer Gedanke, weil dann jede Menge Overhead wegfällt. Und durch ein Interface könnte man trotzdem sauber Workflow-Knoten erhalten... Die Idee gefällt mir ziemlich gut. Was dafür spricht, ist auch, dass mir jetzt kein Fall einfällt, bei dem man einen derartigen Workflow-Knoten mehrfach verwenden könnte, außer er ist seehr allgemein gehalten..

                    Das einzige, was daran stört, wäre, dass man einen Workflow nicht per Konfiguration erstellen könnte.. Wobei, das ginge ja eigentlich auch. Dann müssten die Knoten halt die Konfiguration kennen...

                    Der Ansatz gefällt mir immer besser...

                    Kommentar


                    • #11
                      Zur eigentlichen Frage:

                      Ich würde den Zustand in jedem Fall serverseitig speichern und nicht in der Url.

                      Begründung:

                      Du musst - so oder so - serverseitig prüfen, welche Zustand der aktuelle ist. Ein "Springen" über die URL sollte nur auf zulässigen Pfaden möglich sein. Zulässige Pfade sind durch ein Zustandsdiagramm abzubilden.

                      So gesehen kann dann die URL - von GET abgesehen - eben immer die selbe sein. Man kann auch zustandsbasierte URLs verwenden, halte ich aber eher für ein "Geek"-Feature.

                      Das Wie:

                      Die Persistierung würde ich persönlich durch Serialisierung/Caching mit einem frei wählbaren "Expire" entweder gebunden an die Session oder eben an einen Account lösen. Ob man in der Datenbank oder im Filesystem oder im Arbeitsspeicher cached sollte ebenfalls frei wählbar sein.


                      Zum Thema Workflow explizit/implizit:

                      Ich finde es intuitiver, wenn es eine Kontrollstruktur gibt, die für die Orchestrierung deiner Nodes zuständig ist. Gerade für das Suchen nach dem aktuellen Zustand oder das Abfragen von kritischen Pfaden ist diese sowieso notwendig. Ist aber eventuell auch nur Geschmacksache.

                      Kommentar


                      • #12
                        Die Knoten-Definition könnte man problemlos in einem XML ablegen. Der angegebene Typ wird instanziiert und die enthaltenen XML Tags vom Knoten selber geparst. Man hätte also im Interface / der Basisklasse eine WriteToXML und LoadFromXML Methode. Im Grunde genommen also ActiveRecord.

                        Bei der Workflow-Foundation wird die Workflow-Definition ja auch in XML (XAML) abgelegt.


                        @Renner

                        Das würde aber halt bedeuten, dass deine Kontrollstuktur die Interna der Knoten kennen muss. Sonst kann sie den kritischen Pfad nicht korrekt berechnen. Und das steht natürlich einer einfachen Erweiterbarkeit entgegen.

                        Kommentar


                        • #13
                          Ich kenne den "kritischen Pfad" nur im Zusammenhang mit der Dauer eines Projekts. Was ist hier damit gemeint?

                          Kommentar


                          • #14
                            Genau das. Bei längeren Workflows interessiert oft das voraussichtliche Enddatum des Workflows. Außerdem muss bei Verwendung von if's im Workflow festgestellt werden, welcher Pfad nun überhaupt noch möglich ist

                            Kommentar


                            • #15
                              welcher Pfad nun überhaupt noch möglich ist
                              Das kann man doch aber eigentlich immer nur anhand des aktuellen Knotens feststellen, oder?

                              Kommentar

                              Lädt...
                              X