Systemübersicht und Serverdienste

Um einen besseren Einstieg in das System zu bieten ist es hilfreich, zunächst zu verstehen welche Dienste dem System zur Verfügung stehen. Dies gibt ebenfalls einen besseren Überblick darüber, welche Möglichkeiten der Anwendungsebene überhaupt geboten werden.

Dienst Verwendung in synQup
RabbitMQ Message Queue Server
verteilt Modul- und Core-Aufgaben an Arbeitsprozesse (Worker)
MongoDB schemalose NoSQL-Datenbank
hält neben den sogenannten Transferdaten (z.B.: Produkte, Bestellungen…) in einer getrennten Datenbank auch Nutzdaten des Systems (Logs, Zeitseriendaten)
MySQL RDBMS
hält Systemkonfiguration (Nutzer, Flows, Ausführungen…)
Redis RAM-gestützter Key-Value-Storage
bietet Modulen und Core unkompliziertes Caching, Möglichkeit zur Threadsynchronisation u.V.m.

synQup selbst besteht aus Modulen und Core (die Trennung wird im nachfolgenden Abschnitt genauer beschrieben) und stellt eine PHP-Anwendung auf Symfony-Basis dar.

Module & Core

Datenverarbeitung innerhalb von synQup besteht immer aus einem Zusammenspiel der Hauptanwendung (des sogenannten Cores) und mindestens einem Modul. Die Aufgaben von Core und Modul werden im Folgenden exemplarisch als Abgrenzung dargestellt:

Aufgaben Core:

  • Prozessmodellierung (Welche Module spielen wie zusammen?)
  • Ablaufsteuerung (Starten der Module)
  • Monitoring (Überwachung des Modulfortschritts)
  • Alerting
  • Dateisystemabstraktion
  • Authentifizierung & Authorisierung (Welcher Nutzer darf was und wie ist dessen Identität zu bestätigen?)
  • Zentralisiertes Logging
  • Bereitstellung der Oberfläche

Im Kontrast hierzu übernehmen die Module spezifische Integrationsaufgaben. Das kann beispielsweise das Anbinden eines bestimmten ERP-Systems oder das Einlesen eines bestimmten Datenformates sein. Wirklich mächtig sind Module erst dann, wenn sie wiederverwendbar und erweiterbar gestaltet werden.

Flows & Executions

Ein Flow ist eine abstrakte Datenflussbeschreibung aller Teilschritte, die Daten durchlaufen müssen. Die Instanz eines Flows, also eine konkrete Ausführung, nennt man Execution bzw. FlowExecution. Ein einfaches Beispiel für einen Flow könnte so aussehen:

Flow A:

  • Lade die Datei ~/DATEN.zip vom FTP-Server ftp.example.com mit den Benutzerdaten example / password in das lokale Dateisystem demoFs herunter. (I)
  • Entpacke die Zip-Datei DATEN.zip im Root-Ordner des lokalen Dateisystems demoFs. (I)
  • Lese die CSV-Datei daten.csv im demoFs Dateisystem mit der Spaltenbeschreibung (…) ein. (I)
  • … (T)
  • Exportiere alle Produkte, die sich seit dem letzten Update geändert haben in den Shopware 6 Webshop shop.example.com mit den API-Zugangsdaten (…) (O)

Gehen wir davon aus, dass dieser Flow mit einem Cronjob gesteuert wird und jeden Tag um 10:00 gestartet wird. Eine FlowExecution ist also zum Beispiel die konkrete Ausführung des Flow A am 16.09.2021 um 10:00.

Schön zu erkennen ist an diesem Flow-Beispiel auch die Trennung von Konfiguration und Modulcode: Das FTP-Modul wird hier für unseren Beispielkunden für den FTP-Server ftp.example.com konfiguriert. Beim nächsten Kunden könnte dieser Host example.org lauten. In einem Flow werden also konfigurierbare, wiederverwendbare Module in ein Zusammenspiel gebracht.

Auch wird hier deutlich, dass synQup auch die Abstraktion der Dateisystem-Ebene übernimmt. Das verwendete Storage-Backend ist also in Richtung der Module eine Black-Box. Hierdurch lassen sich auch komplexe Anforderungen realisieren und die Integration in bestehende Systemlandschaften wird vereinfacht: hinter demoFs im obigen Beispiel könnte sich also ein lokales Dateisystem, ein (S)FTP-Server oder auch ein AWS S3 Bucket verbergen.

Alle Schritte in einem Flow lassen sich in einen von drei Namespaces einsortieren: Eingabe, Verarbeitung und Ausgabe. Im Core finden sich die englischsprachigen Bezeichnungen input, transformations und output wieder. Diese Schritte laufen sequentiell und in sich geschlossen ab: die Schritte im Namespace output können erst laufen, nachdem alle transformations-Schritte fertig gelaufen sind.

Zur einfacheren Organisation zeigt es sich als best practice, auch bei eigenen Modulen immer aus der Perspektive der Transferdatenbank eine Entscheidung zu treffen, in welchen Namespace ein (Teil-)modul einsortiert wird: ein Modul, dass Daten aus der Transferdatenbank in ein Zielsystem exportiert wäre also ein Ausgabemodul. Ein Modul, welches Daten in die Transferdatenbank hinzufügt, wäre ein Eingabemodul, ein Modul welches auf Daten in der Transferdatenbank arbeitet ein Verarbeitungsmodul.

Ablaufsteuerung

Ein Flow besteht also immer aus verschiedenen Schritten: bestimmte Module (in den obengenannten Gruppen), die nacheinander laufen. Dieses "nacheinander" ist bei synQup flexibel zu betrachten: neben den oben genannten Prozesschritten Eingabe, Verarbeitung und Ausgabe lässt sich auch der Ablauf innerhalb eines einzelnen Prozessschritts (also beispielsweise das Zusammenspiel aller Input-Module für einen bestimmten Flow) konfigurieren: hierfür werden sogenannte DispatchConditions eingesetzt (also Bedingungen, die erfüllt werden müssen, bevor ein Modul gestartet werden kann).

In unserem obigen Beispiel finden sich bereits implizierte Dispatch-Conditions wieder: die Module im Input-Schritt (blau markiert) würden, ohne DispatchConditions zunächst alle gleichzeitig mit dem Beginn des Input-Schritts gestartet werden (Nebenläufigkeit innerhalb der Prozessschritte, sequentieller Ablauf zwischen den Prozessschritten). Wie müsste also eine solche DispatchCondition aussehen? Auf was würde sich diese überhaupt beziehen?

Progress-Handling

Alle startbaren (Teil-)module in synQup müssen eine sogenannte ProgressDefinition bereitstellen: diese ProgressDefinition ist eine Blaupause der vorgesehenen Abarbeitung einer bestimmten Aufgabe. Ein einfaches (in der Praxis sind diese ProgressDefinitions u.U. komplexe Baumstrukturen) Beispiel für einen CSV-Import:

  • Modulstart: Prüfen des Vorhandenseins der zu importierenden Datei, Formatprüfung
  • Aufteilen der Datei in Batches
  • Verarbeitung dieser Batches hin zu Transferprodukten
  • Aufräumen: Löschen/Verschieben der nun importierten Datei

Aus dieser Blaupause wird jedes Mal, wenn ein Flow startet, ein Eintrag in einer Tabelle erzeugt. Das jeweilige Modul hat die Verantwortung darüber, dem Core beim Start der Verarbeitung die Zahl der gesamten Prozesschritte (total count) und in der Verarbeitung die verarbeiteten Teilschritte (processed count) zu melden. Hierüber kann der Core sowohl erkennen wenn ein Modul abgestürzt ist (= keine Änderungen am Progress mehr) als auch Ausführungszeiten, Durchsatz et cetera protokollieren. Auch die Frage des letzten Abschnitts ist somit beantwortet: Module können in Abhängigkeit des Fortschritts ihrer Geschwistermodule im gleichen Teilschritt gestartet werden (sodass, wie im obigen Beispiel, die CSV-Datei erst verarbeitet wird, nachdem sie aus der Zip-Datei entpackt wurde).