User Tools

Site Tools


applications:prom:projectstructure

Die Projektstruktur in PRO•M

Die folgende Diskussion war der Weg, der zur Projektstruktur geführt hat. Sie kann übersprungen werden, und das Ergebnis der Suche kann hier gesehen werden. Die Projektstruktur in PRO•M ist hierarchisch: Wenn wir DDD1) anwenden, müssen wir innerhalb dieser Struktur Aggregates identifizieren und definieren. In der Delphi 6.0 Entwicklung war das einzelne Projekt das Aggregate, und für bestimmte Änderungen wurde UOW2) verwendet, indem mehrere Projekte gemeinsam in eine Transaktion behandelt wurden. Wird z.B. Projekt 1.1.2 von Projekt 1.1 entfernt und unter Projekt 1.2 gehängt, werden alle 3 Projekte gesperrt. Es wird dann geprüft, ob Projekt 1.1.2 kein Vorfahr von Projekt 1.2 ist, weil es ansonsten einen Zirkel gibt3).

Es wird aber auch geprüft, ob Projekt 1.2 noch keine Vorgänge unter sich hängen hat. Dafür werden dann die Projekte 1.2.1. und 1.2.2 auch noch gesperrt4).

Wenn also Projekt 1.1.2 unter Projekt 1.2 gehängt wird, werden folgende Projekte gesperrt: Das bedingt eine Transaktion, bzw. ein UOW5). Dafür müssen alle Projekt auch in einem Dataspace leben, ansonsten müsste man eine verteilte Transaktion für den UOW verwenden, etwas das spätestens seit Pat Hellands Artikel vermieden werden sollte.

Identifizieren von Aggregates für die Projektstruktur

Das System/Der Mandant als Aggregate Root

Das System könnte für die Projektstruktur verantwortlich sein: Vorteile:

  • Zirkelbezüge können strikt vermieden werden
  • Das Read Model ist immer konsistent

Hierbei handelt es sich aber um ein globales Aggregate mit allen bekannten Nachteilen:

  • Bei tausenden von Projekten müsste das gesamte Aggregate geladen werden wenn ein neues dazu kommt oder die vorhandene Struktur verändert wird - kostspielig,
  • Bei Hunderten von Anwendern, die allesamt auf die Projektstruktur zugreifen möchten, besteht ein großes Konfliktpotenzial. Das kann zwar aufgelöst werden, indem man bei einem Zugriffskonflikt6) die Aktion ein weiteres mal sendet. Eine saubere Trennung wäre das trotzdem nicht, sondern nur eine nachgelagerte Lösung eines Problems, das durch die schlechte Wahl der Aggregates entstanden ist.

Das Hauptprojekt als Aggregate Root

Man könnte das Hauptprojekt, bzw. den Projektstrukturplan7) als Aggregate Root verwenden: So können alle Regeln sicher gestellt werden, u.a.

  • keine Zirkelbezüge
  • keine Vermischung von Projekten und Projektvorgängen auf einer Ebene
  • Statuskombinationen von Projekt zu Teilprojekt
  • und vieles mehr

Es gibt trotzdem noch Problemen mit dieser Struktur, ähnlich wie bei einem globalen Aggregate:

  • Immer noch ein Konfliktpotenzial, weil der Zugriff auf alle Teilelemente immer über das Hauptprojekt läuft. Angenommen ein Organisationsleiter legt ein Hauptprojekt an und darunter 3 Teilprojekte, die er alle an Teilprojektleiter delegiert. Die drei Teilprojektleiter sollten nun unabhängig von einander an ihren Teilprojekten arbeiten können, ohne sich in die Quere zu kommen. Das ist in dieser Struktur nicht möglich, bzw. nur mit hohen Kosten verbunden. Wenn z.B. ein Teil-Teilprojektleiter an Projekt 1.2.2 arbeitet8), müsste immer die Gesamte Struktur geladen werden. Und wenn ein anderer an Projekt 1.2.1 arbeitet, müssten die Änderungen bei Konflikten eventuell mehrmals gesendet werden, damit sie gespeichert werden können.
  • Es gibt keine Möglichkeit aus einem Teilprojekt9) ein Hauptprojekt10) zu machen - Man kann keine Entitäten aus Aggregates ziehen um daraus eigene Aggregates zu machen.
  • Es gibt keine Möglichkeit aus einem Hauptprojekt ein Teilprojekt zu machen11).

Ein Problem ist, dass die Projektstruktur Projekte enthält. Im Grunde müsste die Projektstruktur Referenzen auf Projekt enthalten, wobei jedes Projekt ein eigenes Aggregate ist:

Projektstruktur als Aggregate Root

Das sieht ja schon gewaltig aus. Ist aber im Grunde recht einfach. Ein Projekt ist ein eigenständiges Aggregate, und kann auch als solches existieren. Über die Projektstrukturpläne wird es in eine Struktur eingefügt.

Innerhalb einer Projektstruktur können Regeln bezüglich der Zirkelbezüge und dem Vermischen von Projekten und Projektvorgängen geprüft und eingehalten werden. Das Arbeiten am Projekt ist unabhängig vom Arbeiten an der Struktur, was schon ein großer Schritt in die richtige Richtung ist.

Was noch nicht geht ist, dass die Teilprojektleiter unabhängig von einander die Strukturen bestimmen können. Ein weiters Problem entsteht, wenn man z.B. die Referenz auf Projekt 1.2 aus der Projektstruktur 1 in die Projektstruktur 2 unter die Referenz auf Projekt 2 hängen möchte12). Man müsste alle Referenzen unter der Referenz auf Projekt 1.2 ebenfalls mit rüber nehmen, also die Referenzen auf die Projekte 1.2.1 und 1.2.2. Es gäbe also 6 Schritte:

  1. Löse die Referenz von Projekt 1.2.1 auf die Referenze auf Projekt 1.2 in der Struktur 1 auf
  2. Löse die Referenz von Projekt 1.2.2 auf die Referenze auf Projekt 1.2 in der Struktur 1 auf
  3. Löse die Referenz von Projekt 1.2 auf die Referenz auf Projekt 1 in der Struktur 1 auf
  4. Erstelle eine neue Referenz unter der Referenz auf Projekt 2 in der Struktur 2 auf Projekt 1.2
  5. Erstelle eine neue Referenz unter der Referenz auf Projekt 1.2 in der Struktur 2 auf Projekt 1.2.1
  6. Erstelle eine neue Referenz unter der Referenz auf Projekt 1.2 in der Struktur 2 auf Projekt 1.2.2

Es sind 6 Schritte, die 2 Aggregates betreffen. Die Reihenfolge muss eingehalten werden, weil die Struktur nach jedem Schritt konsistent sein muss. Man könnte die Schritte 1 bis 3 und 4 bis 6 verschmelzen. Es bleiben aber immer auf jeden Fall 2 Schritte.

Das bedeutet auch, dass die globale Sicht nicht immer zwingend konsistent sein muss. Wenn man das Ergebnis der Schritte 4 bis 6 vor dem Ergebnis der Schritte 1 bis 3 sieht13), würde man Projekt 1.2 mit den Teilprojekten 1.2.1 und 1.2.2 in beiden Projektstrukturen sehen. Das wäre nur vorübergehend, und man würde Mechanismen einbauen, um die Mengen-basierte Regel schließlich konsistent einzuhalten14).

Genauso wichtig, wenn nicht wichtiger, ist es, dass die Komponenten, die das Lesemodell verwenden, von dieser möglichen, vorübergehenden Inkonsistenz wissen und sie nicht als Fehler sehen, sondern Wege haben, damit umzugehen. Es gibt aber auch noch folgende Möglichkeit:

Projekt plus Teilprojekt als Aggregate

Was hat das denn wieder auf sich? Es gibt in diesem Modell keine separaten Projektstrukturen, die vorhanden Projekte in eine Baumstruktur bringen. Die Struktur ist Teil des Projektes. Das ist im Grunde eine sehr natürliche Herangehensweise. Ein Organisationsleiter kann ein Hauptprojekt anlegen, es auf mehrere Teilprojekte unterteilen, und diese abgeben. Er bestimmt die Teilprojektstruktur unter dem Hauptprojekt. Das Teilprojekt kann dann aber komplett an jemanden anderes abgegeben werden15).

Die Teilprojekte sind völlig unabhängig von einander. Ein Projekt kümmert sich selbst um die Regel, dass man Projekte und Projektvorgänge16) nicht auf einer Ebene mischen darf.

Wenn man nichts ändern darf, ist eine Baumstruktur ohne Zirkel sicher gestellt. Wie ein Baum wächst die Struktur, beginnend mit einem Hauptprojekt, aus dem Teilprojekte entwachsen. Aus jedem Teilprojekt können wieder neue Äste wachsen, usw.

Schwachpunkte

  • Anlage eines Teilprojektes betrifft 2 Aggregates. Im Projekt wird eine Referenz auf das Teilprojekt hinzugefügt, das Teilprojekt selbst muss dazu angelegt werden. Ohne UoW17) könnte es passieren, dass nur einer der beiden Schritte erfolgreich ausgeführt wird. Was passiert mit einer Referenz auf ein Teilprojekt das nicht da ist? Wie wird dafür kompensiert? Wird die Referenz voreilig gelöscht, kann es sein, dass das Teilprojekt angelegt wird, aber ohne übergeordnetes Projekt. Wird sie zu spät gelöscht, glaubt das Projekt ein Teilprojekt zu haben und lässt z.B. eine Anlage von Projektvorgängen nicht zu.

Vergleich zu dem umgedrehten Modell

Das umgedrehte Modell ist eins, in dem ein Projekt nicht seine Teilprojekte kennt, sondern ein Teilprojekt sein übergeordnetes Projekt kennt: Datenbank-technisch baut man so eine Baumstruktur auf. Die Herangehensweise scheint auch recht natürlich: Als Verantwortlicher eines Projektes lege ich ein Teilprojekt an, das auf mich zeigt. Ab dann wird es aber unnatürlich. Ich weiß nämlich nichts mehr vom Teilprojekt sobald die Aktion fertig ist. In dem Modell von oben weiß das Teilprojekt nichts vom übergeordneten Projekt - was ist also besser? Gucken wir uns mal eine Regel an:

Regel: Projekte und Vorgänge dürfen nicht auf einer Ebene vermischt werden

Wenn das Teilprojekt sein übergeordnetes Projekt kennt, kann diese Regel nicht strikt eingehalten werden. Vor der Teilprojektanlage wird geprüft, ob das übergeordnete Projekt bereits Projektvorgänge hat. Wenn ja, wird die Anlage abgelehnt, wenn nicht, kann das Teilprojekt angelegt werden. Diese Prüfung läuft aber außerhalb der Transaktion, mit der Möglichkeit, dass das Ergebnis hinfällig ist sobald das Teilprojekt angelegt ist. Man müsste somit den Fall einer Vermischung mit einarbeiten und dafür kompensieren.

Wenn ein Projekt seine Teilelemente kennt, und separate Referenzen auf Teilprojekte und auf Projektvorgänge hält, dann kann mit absoluter Sicherheit verhindert werden, dass Teilprojekte und Projektvorgänge auf einer Ebene vermischt werden.

Diesen Vergleich gewinnt also die Struktur, in der das Projekt seine Teilprojekte kennt vs. der Struktur in der das Teilprojekt sein übergeordnetes Projekt kennt.

Regel: Ein Projekt hat kein oder ein übergeordnetes Projekt

Das ist ein inhärenter Teil der Definition einer Baumstruktur.

Bidirektionale Referenzen

Ein Teilprojekt kennt sein übergeordnetes Projekt, und ein Projekt kennt seine Teilprojekte: Wie kann das funktionieren? Ist da nicht unendlich viel Potenzial für Konflikte? Gehen wir mal die einzelnen Fälle durch:

Teilprojektanlage

Eine Teilprojektanlage betrifft immer 2 Aggregates. Es wird ein neues Projekt hinzugefügt, welches auf das übergeordnete Projekt zeigt, und dem übergeordneten Projekt wird das neue Teilprojekt hinzugefügt. Man muss in diesem Fall nur die Reihenfolge einhalten, erst das Teilprojekt anzulegen, dann das Teilprojekt der Liste der Teilprojekte des übergeordneten Projektes hinzuzufügen. Der zweite Schritt darf erst angefordert werden, nachdem der erste erfolgreich durchgeführt wurde. Das Read Model benötigt immer beide Schritte, um das neue Teilprojekt als solches zu akzeptieren. Nur wenn die Ereignisse beider Schritte beobachtet/erhalten wurden, wird das neue Teilprojekt angezeigt. Ein Projekt mit Referenz zu einem übergeordneten Projekt, das aber nicht in der Liste der Teilprojekte dieses Projektes enthalten ist, existiert im Grunde nicht.

Teilprojekt umhängen

Das ist schwieriger, und hierfür muss ein Prozess herhalten. Wenn man bedenkt, dass das Read Modell ein Teilprojekt einem übergeordneten Projekt immer nur zuordnet, wenn es von beiden Projekten die Information erhält, dass die Zusammenführung passiert ist, wäre folgender Prozess denkbar, wenn Teilprojekt 1.1 von Projekt 1 unter Projekt 2 gehängt werden soll:

  1. Teilprojekt 1.1 wird der Teilprojektliste von Projekt 2 zugeordnet. Das hat zur Folge, dass Projekt 2 nun von der Zugehörigkeit eines Teilprojektes ausgeht, und seine Regeln sicher stellen kann. Für alle außenstehenden hängt Teilprojekt 1.1 aber noch ausschließlich unter Projekt 1,
  2. In Teilprojekt 1.1 wird Projekt 2 als übergeordnetes Projekt gesetzt. Das hat zur Folge, dass Projekt 2 nun auch im Read Model unter Projekt 2 hängt, und das ausschließlich, weil es zwar in der Teilprojektliste von Projekt 1 hängt, aber nicht auf Projekt 1 zeigt,
  3. Teilprojekt 1.1 wird aus der Teilprojektliste von Projekt 1 entfernt.

Diese Schritte, in dieser Reihenfolge, sind notwendig und ausreichend. Im Lesemodell wird Teilprojekt 1.1 immer unter höchstens18) einem Projekt hängen. Es wird niemals verloren gehen, was z.B. der Fall wäre, wenn es erst aus der Teilprojektliste von Projekt 1 entfernt würde, und der Prozess dann hängen bleibt.

Wenn nun Schritt 1 erfolgreich durchgelaufen ist, Schritt 2 aber abgelehnt wird, würde die Saga kompensieren, indem sie das Teilprojekt wieder aus der Teilprojektliste von Projekt 2 entfernt. Dann ist alles beim Alten. Wenn Schritte 1 und 2 durchlaufen, Schritt 3 aber fehlschlägt, gibt es einen Zustand, der aufzulösen ist. Es sollte keinen Grund geben, dass der letzte Schritt fehlgeschlagen ist, da man vor dem ersten Schritt bereits geprüft haben sollte, ob Projekt 1 ein Entfernen von Teilprojekt 1.1 erlaubt. Im besten Falle merkt man nichts davon, da das Lesemodell Projekt 1.1 nicht mehr in den Teilprojekten von Projekt 1 anzeigt.

Im schlimmsten Fall kann man Projekt 1 aber keinen Vorgang hinzufügen, weil es denkt, dass es noch ein Teilprojekt besitzt. Hier liegt aber die Krux von kompensierenden Aktionen. Es gibt keine transaktionale Sicherheit. Angenommen, Teilprojekt 1.1 wird gleichzeitig unter Projekt 3 gehängt, und folgendes passiert:

  1. Teilprojekt 1.1 wird der Liste der Teilprojekte von Projekt 2 hinzugefügt
  2. Teilprojekt 1.1 wird der Liste der Teilprojekte von Projekt 3 hinzugefügt
  3. In Teilprojekt 1.1 wird Projekt 2 als übergeordnetes Projekt gesetzt
  4. In Teilprojekt 1.1 wird Projekt 3 als übergeordnetes Projekt gesetzt
  5. Teilprojekt 1.1 wird aus der Teilprojektliste von Projekt 1 entfernt
  6. Teilprojekt 1.1 wird aus der Teilprojektliste von Projekt 1 entfernt

Jetzt hängt Teilprojekt 1.1 unter Projekt 3, weil sie sich gegenseitig referenzieren. Teilprojekt 1.1 ist aber auch in der Liste der Teilprojekte von Projekt 2. Kann man es dort einfach wieder löschen? Das ist gefährlich, denn angenommen es handelt sich um den ersten Fall, in dem das Teilprojekt unter Projekt 2 gehängt wird19), und es ist gerade der erste Schritt durchlaufen worden, die nächsten beiden stehen noch an. Wenn man nun Teilprojekt 1.1 händisch aus der Teilprojektliste von Projekt 2 löscht20), würde der Prozess in den nächsten beiden Schritten Projekt 2 als übergeordnetes Projekt von Teilprojekt 1.1 setzen und Teilprojekt 1.1 aus der Teilprojektliste von Projekt 1 entfernen. Das Ergebnis wäre, dass Teilprojekt 1.1 zwar Projekt 2 referenziert, aber in keinem der beiden Projekte21) in der Liste der Teilprojekte enthalten ist.

Der Prozess des Umhängens muss also erweitert werden, damit er keinen inkonsistenten Zustand hinterlässt. Und zwar am besten so, dass keine verteilte Transaktion notwendig wird, und auch keine Reservierungen. Erstens wird es die Möglichkeit geben, ein Teilprojekt einem Projekt als schwebend hinzuzufügen. Das verbessert die Teilprojektanlage:

Teilprojektanlage, Version 2

In dieser Version wird dem übergeordneten Projekt als erstes ein schwebendes Teilprojekt hinzugefügt. Dadurch weiß man, dass das Teilprojekt vom Projekt akzeptiert wird, und das Projekt kann sich auf ein Teilprojekt vorbereiten. Jetzt wird das neue Teilprojekt hinzugefügt, welches auf das übergeordnete Projekt zeigt - und zwar in dem Wissen, dass das übergeordnete Projekt es akzeptieren wird, und es nicht u.U. brach rumliegen wird. Danach wird dem übergeordneten Projekt mitgeteilt, dass aus dem schwebenden Teilprojekt ein echtes werden kann. Es ist im Lesemodell aber bereits als schwebendes Teilprojekt sichtbar - der Grund liegt im Umhängen.

Es wird für ein sicheres Umhängen zudem im Projekt möglich sein, ein Teilprojekt zum Löschen vorzumerken:

Teilprojekt umhängen, Version Zwei

Teilprojekt 1.1 wird mit folgendem Prozess von Projekt 1 unter Projekt 2 gehängt:

  1. Teilprojekt 1.1 wird in der Teilprojektliste von Projekt 1 zum Löschen22) vorgemerkt. Dadurch passiert noch gar nichts, das Teilprojekt bleibt Teil von Projekt 1. Zwei wichtige Dinge werden jedoch vorbereitet. Erstens wird der letzte Schritt mit Sicherheit funktionieren, d.h. das Löschen eines zum Löschen vorgemerkten Teilprojektes wird auf jeden Fall funktionieren. Das Projekt wird dafür sorgen, z.B. indem der Status nicht verändert werden kann solange es zum Löschen vorgemerkte Teilprojekte gibt. Zweitens wird niemand anderes das Teilprojekt zum Löschen vormerken können. Das Problem, dass ein Teilprojekt gleichzeitig umgehängt wird, wird somit verhindert.
  2. Teilprojekt 1.1 wird in der Teilprojektliste von Projekt 2 schwebend hinzugefügt. Wenn Projekt 2 das akzeptiert, garantiert es, dass es Teilprojekt 1.1 aufnehmen wird, und auch, dass es ohne Fehler wieder gelöscht werden kann wenn einer der folgenden Schritte fehlschlägt,
  3. In Teilprojekt 1.1 wird Projekt 2 als übergeordnetes Projekt gesetzt. Das hat zur Folge, dass Projekt 2 nun im Read Model unter Projekt 2 hängt, und das ausschließlich, weil es zwar in der Teilprojektliste von Projekt 1 hängt23), aber nicht mehr auf Projekt 1 zeigt,
  4. Teilprojekt 1.1 wird der Teilprojektliste von Projekt 2 nun fest zugeordnet. Das kann nun nach den ersten 3 Schritten gemacht werden, weil der Prozess jetzt weiß, dass alles funktioniert hat und alle Vormerkungen auflösen kann,
  5. Teilprojekt 1.1 wird aus der Teilprojektliste von Projekt 1 entfernt.

Schritte 1 und 2 garantieren, dass Schritte 4 und 5 funktionieren, bzw. können Schritte 1 und 2 garantiert rückgängig gemacht werden, wenn Schritt 3 fehlschlägt. Schritt 3 muss selbst nicht mit einer Vormerkung durchgeführt werden, weil alle folgenden Schritte durch die vorhergehenden garantiert funktionieren werden.

Der gesamte Prozess wird von den Schritten 1 und 5 ummantelt, d.h. dass man Teilprojekt 1.1 nicht mehrmals gleichzeitig umhängen kann - der Prozess wird vom übergeordneten Projekt geschützt. Wenn ein Umhängen also nur über diesen Prozess laufen kann, wird Teilprojekt 1.1 immer maximal unter einem Projekt hängen, und immer schließlich unter genau einem übergeordneten Projekt.

Ein Projekt zum Hauptprojekt machen

Dieser Prozess läuft genau so ab wie der Prozess des Umhängens, nur dass es die Schritte 2 und 4 nicht gibt.

Ein Hauptprojekt zum Teilprojekt machen

Hierfür reicht der Prozess noch nicht aus. Wenn man Projekt 1 gleichzeitig unter Projekt 2 und unter Projekt 3 hängt, und dafür die Schritte 1, 3 und 5 des Prozesses umsetzt, würden die Schritte alle erfolgreich durchlaufen, und entweder Projekt 2 oder Projekt 3 hätten Projekt 1 in der Teilprojektliste, ohne dass es zurück auf das Projekt zeigt. Somit muss der gesamte Prozess durch ein Vormerken im Teilprojekt geschützt werden, dass es umgehängt werden soll. Damit das auch unter allen Umständen mit ein hundert prozentiger Sicherheit funktioniert, müssen alle Aktionen darüber geschützt werden. Also sieht es letztendlich so aus:

Teilprojektanlage, finale Version

In dieser Version wird dem übergeordneten Projekt als erstes ein schwebendes Teilprojekt hinzugefügt24). Dadurch weiß man, dass das Teilprojekt vom Projekt akzeptiert wird, und das Projekt kann sich auf ein Teilprojekt vorbereiten. Jetzt wird das neue Teilprojekt hinzugefügt, welches unter Vorbehalt auf das übergeordnete Projekt zeigt - und zwar in dem Wissen, dass das übergeordnete Projekt es akzeptieren wird, und es nicht u.U. brach rumliegen wird. Danach wird dem übergeordneten Projekt mitgeteilt, dass aus dem schwebenden Teilprojekt ein echtes werden kann. Es ist im Lesemodell noch nicht als Teilprojekt des Projektes sichtbar. Das wird es erst nach dem letzten Schritt, in dem es fest auf das übergeordnete Projekt zeigt.

Teilprojekt umhängen, finale Version

Teilprojekt 1.1 wird mit folgendem Prozess von Projekt 1 unter Projekt 2 gehängt:

  1. Projekt 2 wird in Teilprojekt 1.1 unter Vorbehalt als übergeordnetes Projekt gesetzt. Das Teilprojekt merkt sich Projekt 1 als übergeordnetes Projekt, auf das im Fall der Fälle zurück gebogen werden kann. Projekt 2 wird hierdurch vor einem gleichzeitigen Umhängen in ein anderes Projekt geschützt,
  2. Teilprojekt 1.1 wird in der Teilprojektliste von Projekt 1 zum Löschen25) vorgemerkt. Dadurch passiert noch gar nichts, das Teilprojekt bleibt Teil von Projekt 1. Zwei wichtige Dinge werden jedoch vorbereitet. Erstens wird der letzte Schritt mit Sicherheit funktionieren, d.h. das Löschen eines zum Löschen vorgemerkten Teilprojektes wird auf jeden Fall funktionieren. Das Projekt wird dafür sorgen, z.B. indem der Status nicht verändert werden kann solange es zum Löschen vorgemerkte Teilprojekte gibt. Zweitens wird niemand anderes das Teilprojekt zum Löschen vormerken können. Das Problem, dass ein Teilprojekt gleichzeitig umgehängt wird, wird somit verhindert.
  3. [Teilprojekt 1.1 wird in der Teilprojektliste von Projekt 2 schwebend hinzugefügt. Wenn Projekt 2 das akzeptiert, garantiert es, dass es Teilprojekt 1.1 aufnehmen wird, und auch, dass es ohne Fehler wieder gelöscht werden kann wenn einer der folgenden Schritte fehlschlägt,]
  4. Teilprojekt 1.1 wird der Teilprojektliste von Projekt 2 nun fest zugeordnet. Das kann nun nach den ersten 3 Schritten gemacht werden, weil der Prozess jetzt weiß, dass alles funktioniert hat und alle Vormerkungen auflösen kann,
  5. Teilprojekt 1.1 wird aus der Teilprojektliste von Projekt 1 entfernt. Es ist jetzt kurzzeitig keinem Projekt zugeordnet. Das geht aber nicht anders, weil das Setzen von Projekt 2 im Teilprojekt 1.1 der letzte Schritt des Prozesses sein muss.
  6. In Teilprojekt 1.1 wird Projekt 2 nun fest als übergeordnetes Projekt gesetzt. Das hat zur Folge, dass Projekt 2 nun im Read Model unter Projekt 2 hängt, und das ausschließlich, weil es zwar in der Teilprojektliste von Projekt 1 hängt26), aber nicht mehr auf Projekt 1 zeigt,

Der Prozess wird nun durch Teilprojekt 1.1 geschützt, was auch Sinn hat, denn ein gleichzeitiges Umhängen eben dieses Teilprojektes soll ja verhindert werden. Wie man erkennt, handelt es sich beim Umhängen auch um einen Prozess und nicht um einen einfachen Befehl an ein Aggregate, weil der Prozess mehrere Aggregates betrifft. Aber:

Wozu diesen komplizierten Prozess mit bidirektionalen Beziehungen?

Warum setzt man nicht einfach im Teilprojekt das übergeordnete Projekt? Ein Befehl, ganz simpel. Es gibt dafür mehrere Gründe:

  • Wenn man die Regel, dass man Projektstrukturelemente27) nicht vermischen darf, strikt einhalten möchte, muss das übergeordnete Projekt über seine Teilelemente bescheid wissen,
  • Wenn man zudem im Lesemodell sicher stellen möchte, dass ein Projekt maximal ein übergeordnetes Projekt hat, muss das Projekt zurück auf das übergeordnete Projekt zeigen, und man muss beide Informationen nutzen. Ein Projekt wird dann nur als Teilprojekt eines anderen angezeigt, wenn es Teil der Teilprojektmenge ist, und wenn es auf dieses Projekt zeigt. Ansonsten könnte es passieren, dass man die Ereignisse der Aggregates in solche einer Reihenfolge erhält, dass das Teilprojekt z.B. gleichzeitig Teilprojekt mehrerer Projekte ist. Dafür müsste man kompensieren. Es gar nicht erst zu ermöglichen macht den Umstand wesentlich einfacher28),
  • Will man das gleichzeitige Umhängen eines Projektes verhindern, muss es über das Projekt laufen. Das neue übergeordnete Projekt reicht nicht aus, weil es zwei unterschiedliche sein können, und das alte übergeordnete reicht nicht aus, weil es keins geben muss29),
  • Ein Teilprojekt kann durchaus wissen wollen, ob es unter einem Projekt hängt. Z.B. gibt es die Eigenschaft im Projekt “Erbe Feiertagskalender vom übergeordneten Projekt”. Wenn es kein übergeordnetes Projekt gibt, sollte eine Regel besagen, dass der Feiertagskalender angegeben sein muss30).

Zirkelbezüge

Zirkelbezüge in der Projektstruktur können auch mit bidirektionalen Beziehungen nicht verhindert werden. Dazu müsste man die Projektstruktur wie in einigen der Modelle weiter oben verwenden. Entweder die System-weite, globale Projektstruktur, oder aber Projektstrukturen pro Hauptprojekt. Das führt aber zu ganz anderen Problemen, vor allem dazu, dass wir zwar Konsistenz haben, aber keine Teilbarkeit des Systems, bzw. keine Verfügbarkeit31).

Der Projektstrukturplan

Hier sind die Regeln, die eingehalten werden müssen, und die zur letztendlichen Struktur geführt haben:

  • Die Projektstruktur muss immer konsistent sein. Es darf also in der Baumstruktur keine Zirkelbezüge geben, und ein Projekt darf nicht mehrmals im Baum erscheinen,
  • Die Projektnummern müssen global eindeutig sein, und es muss die Möglichkeit geben, sie fortlaufend und lückenlos zu erzeugen,
  • Projekte müssen frei verschiebbar sein, innerhalb eines Hauptprojektes, und auch zwischen Hauptprojekten,
  • Ein Projektleiter muss Teilprojekte anlegen können und diese komplett an Teilprojektleiter abgeben können,
  • Wenn Teilprojektleiter an ihren Projekten arbeiten, soll dieses unabhängig voneinander geschehen, d.h. dass der Projektstrukturplan pro Teilprojekt isoliert von den anderen Projektstrukturplänen verwalten und gestalten werden können muss.

Die ersten 3 Regeln sagen nichts anderes, als dass es eine globale Projektstruktur geben muss. Nur so kann man diese Regeln konsistent einhalten. Da eine Projektanlage aber ein Punkt ist, bei dem die Konsistenz absolut vorrangig ist, wird der Anwender verstehen, wenn es dort eventuell zu Zeitverzögerungen kommen kann32).

Die letzten beiden Punkte besagen, dass Änderungen innerhalb eines Projektes aber nicht global serialisiert werden dürfen. Sobald ein Projekt angelegt wurde, sollten Änderungen maximal innerhalb des Projektes serialisiert werden. Die globale Projektstruktur bestimmt also nur die Position des Projektes im Baum. Jedes Projekt hat dann wiederum eine eigene Projektstruktur für Produkte, Arbeitspakete und Aufgaben|Vorgänge. Die mehrstufige Projektstruktur löst das Problem der gleichzeitigen Zugriffe auf unterschiedliche Teilprojekte. Da die Projekt von der Baumstruktur lediglich referenziert werden33), hängen sie nicht alle gemeinsam im Projektbaum Aggregate und kommen sich nicht in die Quere.

Dass der Projektbaum für eine Neuanlage eines Projektes/Teilprojektes komplett serialisiert werden muss fühlt sich noch unschön an. Was für eine schreckliche Aussage! Wenn aber Konsistenz der Schwerpunkt ist, und zwar sofortige, transaktionale Konsistenz, und keine schließliche Konsistenz, muss das so sein. Was man zumindest optimieren kann, ist nicht immer den gesamten Baum laden zu müssen. Wenn z.B. ein neues Projekt angelegt wird, braucht man den Baum gar nicht zu laden, denn durch ein neues Projekt kann es nicht zu Zirkelbezügen kommen. Man muss nur das Projekt dem Baum hinzufügen. ORM34) Tools wie EF35) bieten das mit lazy loading an.

Es ist ja nicht in Stein gemeißelt, und vielleicht kann man die globale Projektstruktur später noch aufteilen. Wenn man z.B. die Regel aufheben kann, dass ein Teilprojekt zum Hauptprojekt werden kann, oder ein Hauptprojekt zum Teilprojekt, dann hat man keinen globalen Projektbaum mehr, sondern einzelne Hauptprojektbäume. Wenn die Projekte in solch einem Hauptprojektbaum diesen nicht verlassen können, braucht man kein globales Aggregate, weil die Hauptprojektbäume vollkommen unabhängig voneinander existieren können.

Projektneuanlage

Die Projektneuanlage wird so ablaufen, dass erst ein neues Projekt angelegt wird, und es danach im Baum an der richtigen Stelle eingesetzt wird. Das Lesemodell wird das Projekt erst anzeigen, wenn beide Aktionen erfolgreich ausgeführt wurden. Ein neues Projekt in den Baum zu hängen sollte immer funktionieren, daher sollte es selten dazu kommen, dass Projekte brach rum liegen. Und auch wenn, haben sie noch keine Projektnummer erhalten, und sind sonst auch nirgendwo verwendet worden36), stören also nicht weiter.

Projekt umhängen

Ein Projekt wird im Projektbaum umgehängt. Das ist ein Vorgang, der einen Schritt benötigt, somit immer konsistent durchgeführt wird. Wenn es bloß so einfach wäre. Es gibt ja noch die Regel auf einer Ebene dürfen Projekte, Produkte, Teilaufgaben, Arbeitspakete, Aktivitäten und Vorgänge nicht vermischt werden. Das Projekt als logische

Nächster Versuch

Es gibt Projekte mit Teilprojekten37). Ein Hauptprojekt ist Teil seiner eigenen Teilprojektliste - ein Projekt muss immer Teil mindestens einer Teilprojektliste sein, zum Schutz der gleichzeitigen Verschiebung des Projektes durch mehrerer User:

Hauptprojektanlage

  1. Lege neues Projekt an,
  2. Füge Hauptprojekt als ID-Referenz in der eigenen Projektliste an.

Beide Schritte müssen ausgeführt worden sein, damit das Projekt im Lesemodell erscheint. Zudem wird der zweite Schritt immer nur nach dem ersten ausgeführt werden können, da das Hauptprojekt für den zweiten Schritt ja das Aggregate Root ist.

Teilprojektanlage

  1. Füge Teilprojekt als schwebende ID-Referenz im übergeordneten Projekt an. Dadurch wird sicher gestellt, dass das übergeordnete Projekt ein Teilprojekt akzeptieren wird, und dass es nichts anderes als ein weiteres Teilprojekt akzeptieren wird. D.h. die Regel der nicht-Vermischung von Elementen unterschiedlicher Art auf einer Eben wird hierdurch geschützt. Zudem garantiert das Projekt, dass es das Teilprojekt akzeptiert, aber auch, dass die schwebende Referenz wieder gelöscht werden kann.
  2. Lege neues Projekt an,
  3. Mache im übergeordneten Projekt aus schwebender ID-Referenz eine feste.

Wie in der Hauptprojektanlage müssen alle Schritte durchgeführt werden bevor das neue Projekt im Lesemodell erscheint.

Projekt umhängen

  1. Markiere ID-Referenz des Projekts im übergeordneten Projekt zum Löschen vormerken. Das übergeordnete Projekt kann das Projekt selbst sein, sollte es sich um ein Hauptprojekt handeln. Dadurch verschwindet das Projekt im übergeordneten Projekt. Es wird auch vom übergeordneten Projekt garantiert, das das Löschen funktionieren wird, und dass die Kennzeichnung rückgängig gemacht werden kann,
  2. Füge Projekt als ID-Referenz im der Teilprojektliste des neuen Projektes hinzu - es kann sich dabei auch um das eigene Projekt handeln, wenn es zum Hauptprojekt werden soll. Das muss nicht schwebend geschehen, denn nach diesem Schritt sind die folgenden Schritte garantiert. Dadurch erscheint das Projekt im Lesemodell unter dem neuen Projekt, bzw. als Hauptprojekt,
  3. Lösche ID-Referenz aus dem letzten übergeordneten Projekt.

Das Umhängen des Projektes wird durch das übergeordnete Projekt geschützt, d.h. mann wird es nicht gleichzeitig mehrmals verschieben können. Der erste Schritt schlägt fehl, wenn das Projekt bereits von einem anderen Prozess zum Löschen markiert wurde. Daher auch der Umstand, dass ein Hauptprojekt unter sich selbst hängt, denn ansonsten könnte man es nicht schützen. Wie man sieht, hat ein Projekt selbst gar keine Wahl ob es umgehängt wird oder nicht. Man kann in den Prozess einbauen, dass es eine Referenz auf das Hauptprojekt hält, und in Schritt 3 umgehängt wird, nachdem das alte und das neue übergeordnete Projekt ihr Einverständnis erklärt haben.

Davon wird aber abgesehen, denn es wird von einem Top-Down Workflow ausgegangen. Der Verantwortliche des übergeordneten Projektes entscheidet, was mit den Teilprojekten passiert, bzw. wo sie zu liegen haben. Der Verantwortliche des Teilprojektes kümmert sich nur um den Inhalt des Teilprojektes, also um weitere Teilprojekte, Produkte, Teilaufgaben, Arbeitspakete, usw.

Zirkelbezüge

Es kann passieren, dass Projekt 1 in der Teilprojektliste von Projekt 2 ist, und umgekehrt. Was wären die Ereignisse?

  1. Projekt 1: Lege Projekt 1 an
  2. Projekt 1: Hänge Projekt 1 unter Projekt 1 (Hauptprojekt)
  3. Projekt 2: Lege Projekt 2 an
  4. Projekt 1: Hänge Projekt 2 unter Projekt 1
  5. Projekt 2: Hänge Projekt 1 unter Projekt 2

Schritt 4 kann sogar vor Schritt 3 passieren, bzw. vom Lesemodell beobachtet werden. Es würde 2 Tabellen im Lesemodell geben. Einmal die Tabelle mit den Projektdetails, dann die Tabelle mit der Projektstruktur:

Project-Id Details
Project-Id Parent-Project-Id

Wie sehen die Tabellen nach den einzelnen Schritten aus?

Nach Schritt 1

Project-Id Details
Projekt 1
Project-Id Parent-Project-Id

Nach Schritt 2

Project-Id Details
Projekt 1
Project-Id Parent-Project-Id
Projekt 1 NULL

Nach Schritt 3

Project-Id Details
Projekt 1
Projekt 2
Project-Id Parent-Project-Id
Projekt 1 NULL

Nach Schritt 4

Project-Id Details
Projekt 1
Projekt 2
Project-Id Parent-Project-Id
Projekt 1 NULL
Projekt 2 Projekt 1

Nach Schritt 5

Project-Id Details
Projekt 1
Projekt 2
Project-Id Parent-Project-Id
Projekt 1 Projekt 2
Projekt 2 Projekt 1

…OK, das reicht, also hier ist die endgültige Entscheidung…

1) Domain Driven Design
2) , 5) , 17) Unit of Work
3) Wenn Projekt 1.1.2 ein Vorfahr von Projekt 1.2 ist, und dann Projekt 1.2 zum Vorfahr von Projekt 1.1.2 gemacht wird, haben wir einen Zirkelbezug
4) Und das ist auch gar nicht 100% sicher, weil jemand anderes parallel einen Vorgang unter Projekt 1.2 hängen könnte
6) gleichzeitiger Zugriff
7) der dann im Grunde vom Hauptprojekt identifiziert wird
8) Änderungen vornimmt
9) Entität
10) Aggregate Root
11) Die Möglichkeit gibt es schon, indem man eine Verteilte Transaktion startet, ein neues Teilprojekt anlegt, alles aus dem Hauptprojekt kopiert, und dann das Hauptprojekt löscht. Alles kopieren kann aber viele abhängige Daten bedeuten, auch bereits erfasste und fakturierte Zeitdaten. Also keine Aktion, die man in dieser Struktur ohne weiteres anbieten sollte
12) Wenn man also Projekte von einer Struktur in eine andere verschieben kann. Wenn das nicht möglich sein soll, trifft das folgende nicht zu
13) Auch wenn alles in einer UoW passiert, betreffen die Schritte 2 unterschiedliche Aggregates, und die Reihenfolge in der diese beobachtet werden, ist nicht immer sicher gestellt
14) z.B. eine UoW, oder kompensierende Aktionen
15) oder es bleibt (zusätzlich) unter seiner Kontrolle
16) oder Projekte, Produkte, Arbeitspakete, Aufgaben - aus der Projektstrukturplanung
18) Wenn die beobachtete Reihenfolge eine andere ist als die veröffentlichte, kann es sein, dass z.B. Schritt 3 vor Schritt 2 beobachtet wird
19) ohne, dass es gleichzeitig unter Projekt 3 gehängt wird
20) weil man denkt, dass man kompensiert
21) 1 und 2
22) , 25) bzw. Umhängen
23) , 26) zum Löschen gekennzeichnet
24) der erste Schritt muss nicht die Teilprojektanlage sein
27) Projekt, Produkt, Arbeitspaket, Aktion, Vorgang
28) eine Wiederholung der Ereignisse in einer beliebigen Reihenfolge kann nun dazu führen, dass diverse Änderungen nicht gesehen werden. Wenn z.B. Teilprojekt 1.1. 10 mal umgehängt wird, und die Ereignisse von Teilprojekt 1.1 als letztes abgespielt werden, würde nur der letzte Zustand angezeigt werden. Alle anderen nicht, weil die Teilprojektliste aller anderen Projekte zwar Teilprojekt 1.1 enthalten würden, es Teilprojekt 1.1 aber noch gar nicht geben würde. Und die ersten 9 der 10 übergeordneten Projekte in der Historie von Teilprojekt 1.1 würden auch nicht als übergeordnete Projekte erscheinen, weil bei der Wiedergabe der Ereignisse von Teilprojekt 1.1 keines dieser Projekte Teilprojekt 1.1 als Teilprojekt enthalten würde
29) Das Projekt, das umgehängt werden soll, ist ein Hauptprojekt
30) Wow, eine Regel besagt aber auch, dass eine Zeiterfassung nur erlaubt sein soll, wenn es einen Feiertagskalender gibt. Das Teilprojekt weiß aber nicht mit Sicherheit, dass einer im darüber liegenden Projekt gesetzt ist. Also doch lieber eine Projektstruktur? Nicht zwingend, das Vorhandensein eines Feiertagskalenders im darüber liegenden Projekt kann bei Projektfreigabe geprüft werden. Und dann jedesmal, wenn ein Zeitdatensatz erfasst wird. Aktuell wird sich auf die transaktionale Prüfsicherheit verlassen, aber das führt dazu, dass auch der Projektstatus transaktional mit den Projekten und deren Statusänderungen gespeichert werden muss - davon wollen wir ja weg.
31) siehe CAP Theorem
32) Gleichzeitiger Zugriff auf die Projektstruktur muss serialisiert werden
33) Schwach referenziert, über eine ID
34) Object Relational Mapping
35) Entity Framework von Microsoft
36) weil noch nicht sichtbar
37) Erweiterbar auf Teilprojektelemente, also auch Produkte, Teilaufgaben, Arbeitspakete, Aktivitäten, usw.
applications/prom/projectstructure.txt · Last modified: 2013/02/27 10:54 by rtavassoli