Serviceaufrufregeln
In Schleupen.CS gibt es Regeln dafür, welcher Service einer bestimmten Kategorie welchen Service einer anderen Kategorie auf welche Weise nutzen darf. Diese Regeln haben folgende Ziele:
- Unterstützung des Principle Of Least Surprise:
Beispiel: Der Aufruf eines Process Service von einem Entity Service, wäre sehr unerwartet. (Gelöst werden kann dies durch ein ausgelöstes Business Event, das den Geschäftsprozess startet oder fortführt - Dependency Inversion auf Service-Ebene.) - Förderung von Resilienz:
Wenn beispielsweise ein Service synchron in Kette zwei weitere Services aufruft, die jeweils Daten ändern, so wäre so etwas wie eine verteilte Transaktion notwendig, um unerwünschte Systemzustände zu vermeiden. Eine solche technische Kopplung zu einem Transaktionsmanager (MSDT o.ä.) ist in Schleupen.CS nicht erlaubt, da derartige Technologien nach unserer Erfahrung insbesondere programmiersprachen- / plattformübergreifend nicht zuverlässig genug funktionieren. Das Problem lässt sich auf mehrere Arten lösen (beispielsweise durch Sagas), nur i.A. nicht über synchrone, direkt in Reihe geschaltete Aufrufe. - Minimierung von Kopplung:
Um die Kopplung der Länder untereinander zu begrenzen, kommen die sogenannten Geschäftsprozess-Komponenten ins Spiel, die hier die Orchestrierung übernehmen. Entsprechend darf ein Land beispielsweise ein anderes Land nicht synchron schreibend aufrufen (Schreibend == persistierend). - Unterstützung einer Schichten-Architektur auf Makroebene:
Um ein besseres Systemverständnis zu erzielen, gibt es in Schleupen.CS die Geschäftsprozess-Komponenten. Nur diese dürfen Process Services beinhalten. - Beachtung von Abstraktionsniveaus (~ Single-Responsibility-Principle (SRP) auf Architekturebene):
Orchestrierung auf Ebene der Geschäftsprozesse hat beispielsweise ein höheres Abstraktionsniveau als ein Activity Service eines Landes.
Exemplarische Beispiele der Servicenutzungsregeln:
Im Folgenden werden exemplarisch einige Regeln aufgeführt und kurz erläutert. Die angeführten Fälle versuchen einen Einblick in die Thematik zu geben.
Fall: Benutzeroberfläche ruft Process Service auf
Der Aufruf von Benutzeroberfläche zu einem Process Service ist natürlich erlaubt, da dies eine einfache Möglichkeit ist, um Geschäftsprozesse zu starten bzw. fortzuführen.
Fall: Ein Process Service ruft einen Activity Service eines Landes schreibend auf
Auch dieser Aufruf ist recht offensichtlich zu erlauben. Da Process Services als Workflows implementiert werden, sind auch schreibende Aufrufe in Reihe möglich. Denn die oben beschriebenen Probleme bzgl. der Implementierung von Transaktionen, die sich über mehrere Microservices erstrecken, lösen sich durch die zeitliche Entkopplung durch Verwendung von Workflows / Sagas auf.
Fall: Ein Activity Service eines Landes ruft eine lesende Operation eines Activity Service eines Landes auf
Das Lesen von Daten ist prinzipiell eine unkritischere Abhängigkeit als eine Schreibende. Würde man diese Möglichkeit nicht erlauben, würde das bedeuten, dass mehr Daten über die Orchestrierungsebene transportiert werden müssten, was in deutlich mehr Implementierungsaufwand und einem schlechteren Laufzeitverhalten münden würde.
Die Unterscheidung, ob sich die Logik auf Änderungen oder das Lesen von Daten bezieht, ist fundamental und zieht sich komplett von Servicedesign bis in die Domäne durch. Stichwort: CQRS.
Fall: Ein Activity Service eines Landes ruft synchron eine schreibende Operation eines Activity Service eines Landes auf
Da ein Activity Service im Allgemeinen auch die eigenen Daten modifiziert, ist dieses Problem identisch zu dem Aufruf zweier schreibender Services. Denn es ist egal, um welche Art von Ressource (Datenbank, Service, etc.) es sich handelt. Auch hier wird so etwas wie eine verteilte Transaktion benötigt, was in Schleupen.CS auf Ebene von GP-Komponente durch Implementierung von Workflows als Sagas erfolgt.
Matrix
Die folgende Matrix definiert die Regeln für den Zugriff von einem Baustein auf den anderen.
Aufrufer | Ruft auf | Ziel | Erlaubt | Anmerkung |
---|---|---|---|---|
Implementierung UI | → | ProcessService | (i.A. als Abschluss eines Dialogablaufs, da SHIL (Service - Human Interaction Layer) nicht direkt unterstützt wird - hier ist Polling notwendig) | |
Implementierung UI | → | ActivityService | ||
Implementierung UI | → | QueryService | ||
Implementierung UI | → | EntityService | ||
Implementierung UI | → | EventService | ||
Implementierung UI | → | CommandService | ||
Implementierung Workflow | → | ProcessService | ||
Implementierung Workflow | → | ActivityService | ||
Implementierung Workflow | → | QueryService | ||
Implementierung Workflow | → | EntityService | (Beachte hierbei: ist eine Verletzung des Abstraktionsniveaus vorhanden? Don't do it) | |
Implementierung Workflow | → | EventService | auslösen | |
Implementierung Workflow | → | CommandService | ||
Implementierung ActivityService (in GP-Komponente) | → | ProcessService | ||
Implementierung ActivityService (in GP-Komponente) | → | ActivityService | ||
Implementierung ActivityService (in GP-Komponente) | → | QueryService | (nur lesend) | |
Implementierung ActivityService (in GP-Komponente) | → | EntityService | (nur lesend) | |
Implementierung ActivityService (in GP-Komponente) | → | EventService | auslösen | |
Implementierung ActivityService (in GP-Komponente) | → | CommandService | ||
Implementierung ActivityService (in Land) | → | ProcessService | ||
Implementierung ActivityService (in Land) | → | ActivityService | ||
Implementierung ActivityService (in Land) | → | QueryService | (nur lesend, nur Land darf aufgerufen werden) | |
Implementierung ActivityService (in Land) | → | EntityService | (nur lesend) | |
Implementierung ActivityService (in Land) | → | EventService | ||
Implementierung ActivityService (in Land) | → | CommandService | ||
Implementierung QueryService (in GP-Komponente) | → | ProcessService | ||
Implementierung QueryService (in GP-Komponente) | → | ActivityService | ||
Implementierung QueryService (in GP-Komponente) | → | QueryService | (nur lesend) | |
Implementierung QueryService (in GP-Komponente) | → | EntityService | (nur lesend) | |
Implementierung QueryService (in GP-Komponente) | → | EventService | ||
Implementierung QueryService (in GP-Komponente) | → | CommandService | ||
Implementierung QueryService (in Land) | → | ProcessService | ||
Implementierung QueryService (in Land) | → | ActivityService | ||
Implementierung QueryService (in Land) | → | QueryService | (nur lesend, nur Land darf aufgerufen werden) | |
Implementierung QueryService (in Land) | → | EntityService | (nur lesend) | |
Implementierung QueryService (in Land) | → | EventService | ||
Implementierung QueryService (in Land) | → | CommandService | ||
Implementierung EntityService | → | ProcessService | ||
Implementierung EntityService | → | ActivityService | ||
Implementierung EntityService | → | QueryService | ||
Implementierung EntityService | → | EntityService | ||
Implementierung EntityService | → | EventService | auslösen | |
Implementierung EntityService | → | CommandService | ||
Implementierung EventService (in GP-Komponente) | → | ProcessService | ||
Implementierung EventService (in GP-Komponente) | → | ActivityService | (nur lesend) | |
Implementierung EventService (in GP-Komponente) | → | QueryService | ||
Implementierung EventService (in GP-Komponente) | → | EntityService | (nur lesend) | |
Implementierung EventService (in GP-Komponente) | → | EventService | auslösen | |
Implementierung EventService (in GP-Komponente) | → | CommandService | ||
Implementierung EventService (in Land) | → | ProcessService | ||
Implementierung EventService (in Land) | → | ActivityService | ||
Implementierung EventService (in Land) | → | QueryService | (nur Land darf aufgerufen werden) | |
Implementierung EventService (in Land) | → | EntityService | (nur lesend, nur Land darf aufgerufen werden) | |
Implementierung EventService (in Land) | → | EventService | auslösen | |
Implementierung EventService (in Land) | → | CommandService | (nur Land darf aufgerufen werden) | |
Implementierung CommandService | → | ProcessService | ||
Implementierung CommandService | → | ActivityService | ||
Implementierung CommandService | → | QueryService | (nur lesend, nur Land darf aufgerufen werden) | |
Implementierung CommandService | → | EntityService | (nur lesend) | |
Implementierung CommandService | → | EventService | ||
Implementierung CommandService | → | CommandService |