Workflow-Engine einmal anders

Schon in einigen Projekten war die Herausforderung bestimmte Abläufe oder auch „Prozesse“ hochskalierbar, verlässlich ausführen zu können. Dabei kam dann von den einen Projektverantwortlichen schnell der Ruf nach einer Workflow-Engine, von den anderen eher das Gegenteil: „Bloß keine Workflow-Engine …“.

Ich selbst bin auch nicht unbedingt ein Freund von „graphischer Programmierung“ oder geschwätzigem XML, was fast schon Programmiersprache sein will, aber in fast jeder
Hinsicht unhandlicher ist als eine „normale“ Programmiersprache wie Scala oder Java.

Aus diesem Grund reizte mich die Idee einen alternativen Ansatz zu versuchen. Da ich, wie gerade schon erwähnt, kein Freund von „in XML programmieren“ bin, sollte die
Beschreibung des „Prozesses“ – das Prozessmodell – in Java erfolgen.

Hier bietet sich das State-Pattern an, welches eine Prozessablauf mit Hilfe von Zustandsobjekten und einem Kontext beschreibt. Konkret werden also nur Interfaces bzw. abstrakte Basisklassen benötigt, die von einem konkreten Prozess implementiert
werden. Dazu der „Contract“ der den Ablauf / die Semantik beschreibt und schon kann ein „Prozess“ statt in XML in Java geschrieben werden.

Allerdings kann man nun einwenden, dass das normale Ausführungsmodell einer JVM mit Threads nicht die gleichen Vorteile bietet wie eine Workflow-Engine.

Inspiriert von den Ansätzen von Dalma und Javaflow oder auch den Arbeiten von Doug Lea (FJTasks) ist aber auch auf einer Java VM mehr möglich, als einfach für jeden Prozess
und jede seiner Nebenläufigkeiten einen neuen Thread zu starten.

Würde man das tun, wären diese bei den eingangs genannten Anforderungen sicher bald aufgebraucht (viele Prozessinstanzen, womöglich viele davon nur wartend).

Auch mit der Skalierbarkeit haben viele Workflow-Engines gewisse Probleme, weil sich die Arbeit schlecht über einen Cluster verteilen läßt. Aber auch hier gibt es im Standard
Java gute Lösungansätze, wie z.B. GridGain.

Ein weiterer Aspekt, den Workflow-Engine bedienen, ist Prozesspersistenz. Prozesse, die auf der Engine ablaufen, lassen sich persistieren. Damit ist der Ablauf unterbrechbar, wiederherstellbar und nachvollziehbar. Wenn allerdings Prozesse normale durch normale Java-Objekte dargestellt werden, dann lassen sie sich auch mit einfachen ORM Mitteln persistieren.

Zu guter letzt sind die meisten Prozesse immer auf irgendwelche Services angewiesen, die von ihnen aufgerufen werden, oder Calls an die Prozesse absetzen. Hierzu soll
für das Java basierende Prozessmodell ein Container /Application-Server zum Einsatz kommen. Meine Wahl fiel auf Spring, wobei sicher auch andere Lösungen denkbar wären.

Das Rezept für meine Workflow-Engine lautet also:

  • Prozesse sind Java-Klassen, die nach dem State-Pattern einen Ablauf implementieren.

  • Diese Klassen sind typischerweise Spring-Beans, die vom Container „Services“ konsumieren.

  • Die Klassen sind Serializable und damit „Remoting“-fähig für GridGain.

  • Die Klassen tragen (optional) Persistenz-Annotationen, welche die Engine dafür nutzt den Prozesszustand zu persistieren.

Heraus kommt eine Lösung,

  • die hochskalierbar ist, weil Prozesse über GridGain auf einem Cluster von Nodes verteilt werden können.

  • die den gewohnten Komfort einer IOC-Umgebung genießt, bei der nach Belieben „Services“ genutzt werden können (natürlich nicht nur Webservices).

  • die den Prozesszustand nach jedem Prozessschritt mit Hilfe eines Persistenzproviders wie z.B. Hibernate persistiert.

  • die Prozesse einfach als Java-Programm beschrieben, welches mit einfachsten Mitteln und voller IDE Untersützung entworfen werden kann.

Besonders den letzten Punkt möchte ich noch einmal unterstreichen: Man kann es gar nicht hoch genug bewerten, wie viele Vorteile sich allein dadurch ergeben, dass man in
der gewohnten Umgebung arbeiten kann:

  • kein Lernen einer Prozessbeschreibungssprache (in XML).

  • alle Möglichkeiten und Konstrukte einer Programmiersprache wie Java.

  • die gewohnte IDE.

  • simples Debuggen und Testen (noch besser zu unterstützen durch Mocks und eine spezielle Engine, die ein lokales Debuggen direkt ermöglicht).

  • Einfachstes Wiederverwenden von vorhandenem Code oder vorhandenen Services (meist sind keinerlei „Wrapper“ oder „Adapter“ notwendig).

Wie die Lösung konkret aussieht, werde ich wohl heute nicht mehr zusammenschreiben, deshalb wird’s demnächst hier ne Fortsetzung geben …

Dieser Beitrag wurde unter Java veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *