chapters: Fliesstext auf 57 reine Textseiten kuerzen
Zwei Kompressionsdurchgaenge ueber Kapitel 2-7. Entfernt wurden Redundanzen, Meta-Kommentare, Ueberklaerungen und Fuellsaetze; Fakten, Namen, Daten, Entscheidungen samt Begruendung sowie alle Abbildungen und Tabellen bleiben unveraendert. Reine Textseiten: 78 -> 57 (Woerter 19613 -> 12451). Gesamt-PDF: 138 -> 116 Seiten.
This commit is contained in:
@@ -3,34 +3,34 @@
|
|||||||
|
|
||||||
\subsection{Bewertung gegen die Zielsituation}
|
\subsection{Bewertung gegen die Zielsituation}
|
||||||
|
|
||||||
Die in Abschnitt~\ref{sec:target-situation} beschriebene Zielsituation ist erreicht. Kunden verfügen im Kundenportal über einen zentralen, selbstständig nutzbaren Zugang zu ihren Dokumenten. Die zuvor bestehenden Probleme — fehlende Zentralisierung, kein Self-Service, kein strukturierter Überblick, kein technisch abgesicherter mandantengetrennter Zugriff — sind adressiert.
|
Die in Abschnitt~\ref{sec:target-situation} beschriebene Zielsituation ist erreicht: Kunden verfügen über einen zentralen, selbstständig nutzbaren Zugang zu ihren Dokumenten. Die zuvor bestehenden Probleme — fehlende Zentralisierung, kein Self-Service, kein strukturierter Überblick, kein mandantengetrennter Zugriff — sind adressiert.
|
||||||
|
|
||||||
Bemerkenswert ist dabei weniger die Menge der umgesetzten Funktionen als der Umstand, dass eine seit Ende 2023 im Backlog liegende Bedarfsnotiz innerhalb eines einzigen Projektzeitraums zu einer produktiv genutzten Funktion wurde. Der entscheidende Schritt war dabei nicht die Implementierung, sondern die Konkretisierung: Erst die Ausarbeitung des Features im Juni und die anschließende Analyse machten aus zwei Stichpunkten eine umsetzbare Anforderung.
|
Eine seit Ende 2023 im Backlog liegende Bedarfsnotiz wurde innerhalb eines Projektzeitraums zur produktiven Funktion. Entscheidend war die Konkretisierung: Erst die Ausarbeitung im Juni machte aus zwei Stichpunkten eine umsetzbare Anforderung.
|
||||||
|
|
||||||
\subsection{Fachliche Ziele}
|
\subsection{Fachliche Ziele}
|
||||||
|
|
||||||
Sämtliche fachlich geforderten Funktionen sind umgesetzt. Der Dokumentenbereich bietet über den ursprünglich als Kern definierten Umfang hinaus auch die im Abschnitt \emph{Feature-Creep} der Feature-Beschreibung genannten Komfortfunktionen — Suche, Einzeldownload, Archivdownload, Vorschau, Freigabelinks und Verknüpfungsdateien.
|
Sämtliche fachlich geforderten Funktionen sind umgesetzt — über den Kern hinaus auch die nachrangigen Komfortfunktionen: Suche, Einzeldownload, Archivdownload, Vorschau, Freigabelinks und Verknüpfungsdateien.
|
||||||
|
|
||||||
Dass diese vollständig umgesetzt wurden, war zu Projektbeginn nicht selbstverständlich: Sie waren ausdrücklich als nachrangig eingestuft. Möglich wurde es, weil der Kern — die Auflistung mit Mandantentrennung — bereits nach vier Tagen zusammengeführt war und die darauf aufbauenden Arbeiten dadurch früh beginnen konnten.
|
Der Kern — Auflistung mit Mandantentrennung — war nach vier Tagen zusammengeführt, sodass die Folgearbeiten früh beginnen konnten.
|
||||||
|
|
||||||
\subsection{Technische Ziele}
|
\subsection{Technische Ziele}
|
||||||
|
|
||||||
Die zentrale technische Herausforderung des Projekts war nicht in der ursprünglichen Zielsetzung enthalten. Sie entstand aus der Kombination zweier Entscheidungen — Autorisierung über ein Metadatum und lesbare Ordnernamen für die interne Pflege — und der Eigenschaft des Speichers, keine Suche über Metadaten anzubieten.
|
Die zentrale technische Herausforderung war nicht in der Zielsetzung enthalten, sondern entstand aus der Kombination von Autorisierung über ein Metadatum, lesbaren Ordnernamen und fehlender Metadatensuche des Speichers.
|
||||||
|
|
||||||
Die gefundene Lösung erfüllt die Anforderung, ohne eine zweite Datenhaltung einzuführen, und heilt bestehende Abweichungen selbsttätig. Sie ist damit fachlich wie technisch die tragfähigere Variante gegenüber den zunächst naheliegenden Ansätzen. Dass sie zum Abgabezeitpunkt noch nicht ausgeliefert war, schmälert die konzeptionelle Leistung nicht, ist für die Bewertung des Projektstands aber festzuhalten.
|
Die Lösung erfüllt die Anforderung ohne zweite Datenhaltung und heilt bestehende Abweichungen selbsttätig — tragfähiger als die zunächst naheliegenden Ansätze. Dass sie bei Abgabe noch nicht ausgeliefert war, ist festzuhalten.
|
||||||
|
|
||||||
\subsection{Nicht erreichte Ziele}
|
\subsection{Nicht erreichte Ziele}
|
||||||
|
|
||||||
Drei Punkte sind offen geblieben.
|
Drei Punkte sind offen geblieben.
|
||||||
|
|
||||||
Die \textbf{Nebenläufigkeit im verändernden Teil des Lookups} ist analysiert, dokumentiert und mit Lösungsvorschlägen versehen, aber nicht behoben. Dies ist eine bewusste, abgestimmte Abgrenzung und keine Nachlässigkeit — die Wahl der Gegenmaßnahme hängt von einer Rückfrage beim Betreiber ab, deren Bearbeitungsdauer zuvor mit mehreren Wochen bemessen worden war.
|
Die \textbf{Nebenläufigkeit im verändernden Lookup-Pfad} ist analysiert, dokumentiert und mit Lösungsvorschlägen versehen, aber nicht behoben — bewusst abgegrenzt, da die Wahl der Gegenmaßnahme von einer Rückfrage beim Betreiber abhängt.
|
||||||
|
|
||||||
Die \textbf{Optimierung der Dokumentensuche} bleibt offen. Sie wäre über den Suchdienst des Speicherherstellers lösbar gewesen, der jedoch nicht zur Verfügung steht. Die praktische Auswirkung ist gering, weil die Suche nur mit der Dokumentenzahl eines einzelnen Kunden skaliert.
|
Die \textbf{Optimierung der Dokumentensuche} bleibt offen, da der Suchdienst des Speicherherstellers nicht bereitsteht; die Auswirkung ist gering, weil die Suche nur mit der Dokumentenzahl eines einzelnen Kunden skaliert.
|
||||||
|
|
||||||
Der \textbf{aus der Abnahme hervorgegangene Fehler} zur Einheitlichkeit des Suchfeldes ist erfasst und eingeplant, aber nicht behoben.
|
Der \textbf{Fehlerbericht zur Einheitlichkeit des Suchfeldes} ist erfasst und eingeplant.
|
||||||
|
|
||||||
\subsection{Gesamtbewertung}
|
\subsection{Gesamtbewertung}
|
||||||
|
|
||||||
Das Projektziel ist erreicht. Der Dokumentenbereich ist in seinen Kernfunktionen produktiv, erfüllt die fachlichen Anforderungen und ist in einem Zustand, in dem die verbleibenden Arbeiten klar benannt, priorisiert und im Backlog erfasst sind.
|
Das Projektziel ist erreicht. Der Dokumentenbereich ist in seinen Kernfunktionen produktiv, erfüllt die fachlichen Anforderungen, und die verbleibenden Arbeiten sind klar benannt und im Backlog erfasst.
|
||||||
|
|
||||||
Der aussagekräftigste Einzelbefund des Projekts ist dabei nicht eine umgesetzte Funktion, sondern die Erkenntnis über die Nebenläufigkeit. Sie wurde durch eigenes Nachprüfen des bereits geschriebenen Codes gefunden — nicht durch einen Fehlerbericht, nicht durch einen Test und nicht durch ein Review. Ein Projekt, das eine solche Schwäche selbst findet, benennt und einordnet, steht qualitativ besser da als eines, in dem sie unentdeckt bliebe.
|
Der aussagekräftigste Befund ist die Nebenläufigkeit: Sie wurde durch eigenes Nachprüfen des bereits geschriebenen Codes gefunden — nicht durch Test, Fehlerbericht oder Review. Ein Projekt, das eine solche Schwäche selbst findet und einordnet, steht besser da als eines, in dem sie unentdeckt bliebe.
|
||||||
|
|||||||
@@ -3,46 +3,42 @@
|
|||||||
|
|
||||||
\subsection{Abschluss der begonnenen Arbeiten}
|
\subsection{Abschluss der begonnenen Arbeiten}
|
||||||
|
|
||||||
Die nächstliegenden Schritte ergeben sich unmittelbar aus dem in Abschnitt~\ref{sec:target-comparison} beschriebenen Stand.
|
Die nächsten Schritte ergeben sich aus Abschnitt~\ref{sec:target-comparison}.
|
||||||
|
|
||||||
Zusammenzuführen ist der Pull Request zur Ordnerverwaltung. Damit werden zugleich die automatische Anlage der Ordnerstruktur und der Lookup mit konstanter Aufrufzahl wirksam. Da die Lösung bestehende Ordner selbsttätig auf den kanonischen Namen umbenennt, ist kein gesonderter Migrationsschritt erforderlich — der Bestand wird durch den laufenden Betrieb überführt.
|
Zusammenzuführen ist der Pull Request zur Ordnerverwaltung, womit die automatische Ordneranlage und der Lookup mit konstanter Aufrufzahl wirksam werden. Da die Lösung bestehende Ordner selbsttätig umbenennt, entfällt ein Migrationsschritt.
|
||||||
|
|
||||||
Zu beheben ist ferner der aus der Abnahme hervorgegangene Fehler zur Einheitlichkeit des Suchfeldes, der für den folgenden Sprint eingeplant ist.
|
Zu beheben ist der Abnahmefehler zur Einheitlichkeit des Suchfeldes, eingeplant für den folgenden Sprint.
|
||||||
|
|
||||||
\subsection{Absicherung des verändernden Lookup-Pfads}
|
\subsection{Absicherung des verändernden Lookup-Pfads}
|
||||||
|
|
||||||
Die in Abschnitt~\ref{sec:race-conditions} beschriebene Nebenläufigkeit ist das inhaltlich gewichtigste offene Thema. Der zugehörige Backlog Item ist freigegeben und mit einem Zeitrahmen von vier bis sechs Stunden versehen.
|
Die in Abschnitt~\ref{sec:race-conditions} beschriebene Nebenläufigkeit ist das gewichtigste offene Thema. Das Backlog Item ist freigegeben und mit vier bis sechs Stunden veranschlagt.
|
||||||
|
|
||||||
Welche der vorgeschlagenen Maßnahmen umgesetzt wird, hängt davon ab, ob der Speicher bedingte Schreibvorgänge unterstützt.\autocite{aws-conditional-writes} Diese Frage ist Teil derselben Rückfrage beim Betreiber, die auch den Suchdienst betrifft. Steht die Funktion zur Verfügung, ist die Absicherung mit geringem Aufwand möglich; andernfalls bleiben die Absicherung des Rücknahmeschritts und eine übergreifende Sperre.
|
Welche Maßnahme umgesetzt wird, hängt davon ab, ob der Speicher bedingte Schreibvorgänge unterstützt.\autocite{aws-conditional-writes} Steht die Funktion bereit, ist die Absicherung einfach; andernfalls bleiben die Absicherung des Rücknahmeschritts und eine übergreifende Sperre.
|
||||||
|
|
||||||
Unabhängig von dieser Entscheidung ist zu erwägen, den verändernden Anteil aus dem Anfragepfad herauszulösen. Ein eigener Vorgang, der die Ordnerbestände abgleicht, hätte den Vorteil, dass er einmalig und ohne Nebenläufigkeit ausgeführt wird. Der Lookup könnte dann auf reines Lesen zurückgeführt werden — die Selbstheilung entfiele, würde aber auch nicht mehr benötigt.
|
Unabhängig davon ließe sich der verändernde Anteil aus dem Anfragepfad in einen eigenen, einmalig ausgeführten Vorgang lösen; der Lookup könnte dann auf reines Lesen zurückgeführt werden.
|
||||||
|
|
||||||
\subsection{Suchdienst des Speicherherstellers}
|
\subsection{Suchdienst des Speicherherstellers}
|
||||||
|
|
||||||
Der in Abschnitt~\ref{sec:lookup-research} betrachtete Suchdienst\autocite{storagegrid-search-integration} wurde vom Betreiber nicht bereitgestellt. Die Anfrage wurde an dessen Produktverantwortliche weitergegeben; ein Folgetermin war zum Ende des Projektzeitraums vorgeschlagen, aber noch nicht durchgeführt.
|
Der in Abschnitt~\ref{sec:lookup-research} betrachtete Suchdienst\autocite{storagegrid-search-integration} wurde vom Betreiber nicht bereitgestellt; ein Folgetermin war vorgeschlagen, aber nicht durchgeführt.
|
||||||
|
|
||||||
Sollte der Dienst künftig verfügbar sein, eröffnet er zwei Möglichkeiten. Zum einen ließe sich der Kundenordner-Lookup unmittelbar über eine Abfrage auf das Metadatum lösen — die aufwendigere eigene Konstruktion würde damit entbehrlich, wäre aber weiterhin funktionsfähig und könnte als Rückfallebene bestehen bleiben. Zum anderen ließe sich die Dokumentensuche serverseitig ausführen, statt wie bisher sämtliche Schlüssel eines Kunden zu laden und anwendungsseitig zu filtern.
|
Sollte der Dienst verfügbar werden, ließe sich der Kundenordner-Lookup über eine Metadatenabfrage lösen — die eigene Konstruktion bliebe als Rückfallebene. Zudem könnte die Dokumentensuche serverseitig erfolgen statt anwendungsseitig. Beides sind Optimierungen; die bestehende Umsetzung funktioniert, skaliert aber schlechter.
|
||||||
|
|
||||||
Beide Verbesserungen sind Optimierungen, keine Fehlerbehebungen. Die bestehende Umsetzung funktioniert; sie skaliert lediglich schlechter, als sie es mit dem Dienst täte.
|
|
||||||
|
|
||||||
\subsection{Fachliche Erweiterungen}
|
\subsection{Fachliche Erweiterungen}
|
||||||
|
|
||||||
In der Feature-Beschreibung sind zwei Erweiterungen genannt, die nicht in Backlog Items überführt wurden und damit für einen späteren Zeitpunkt offenstehen.
|
In der Feature-Beschreibung sind zwei Erweiterungen genannt, die nicht in Backlog Items überführt wurden.
|
||||||
|
|
||||||
Die erste betrifft die \textbf{Einbindung in die Übersichtsseite} des Kundenportals. Denkbar sind eine Kachel mit den zuletzt hinzugefügten Dokumenten sowie eine gesonderte Darstellung von Sicherheitsbewertungen. Für Letztere wäre allerdings zusätzliche Information nötig: Um etwa den Zeitpunkt der letzten Bewertung anzuzeigen, müssten die entsprechenden Dokumente ein auswertbares Merkmal tragen. Das ist mit der bestehenden Struktur, die den Typ ausschließlich über den Ordner ausdrückt, nicht abbildbar und würde ein Metadatum pro Dokument erfordern.
|
Die erste betrifft die \textbf{Einbindung in die Übersichtsseite} — etwa eine Kachel mit zuletzt hinzugefügten Dokumenten oder Sicherheitsbewertungen. Für Letztere wäre ein auswertbares Merkmal pro Dokument nötig, das mit der bestehenden Struktur (Typ über den Ordner) nicht abbildbar ist.
|
||||||
|
|
||||||
Die zweite betrifft die \textbf{Bereitstellung von Abrechnungsdaten}. Hier ist zunächst fachlich zu klären, ob diese über den Dokumentenbereich oder über eine gesonderte Ansicht bereitgestellt werden sollen, da sie sich in Herkunft und Aktualisierungsrhythmus deutlich von den übrigen Dokumenttypen unterscheiden.
|
Die zweite betrifft die \textbf{Bereitstellung von Abrechnungsdaten}. Fachlich ist zu klären, ob diese über den Dokumentenbereich oder eine gesonderte Ansicht erfolgen, da sie sich in Herkunft und Aktualisierungsrhythmus von den übrigen Dokumenttypen unterscheiden.
|
||||||
|
|
||||||
\subsection{Bedienung durch die interne Fachseite}
|
\subsection{Bedienung durch die interne Fachseite}
|
||||||
|
|
||||||
Die Entscheidung, die Dokumentenpflege über den bestehenden externen Dateibrowser abzuwickeln und keine eigene Verwaltungsoberfläche zu bauen, war für den Projektzeitraum richtig: Sie sparte erheblichen Aufwand und ermöglichte die Konzentration auf die Kundensicht.
|
Die Entscheidung, die Dokumentenpflege über den externen Dateibrowser abzuwickeln, war für den Projektzeitraum richtig, aber bewusst als Zwischenlösung getroffen. Sollte die Pflege häufiger oder von mehr Personen übernommen werden, wäre eine Verwaltungsoberfläche im Portal zu bewerten — sie könnte die Ordnerstruktur erzwingen statt auf manuelle Einhaltung zu setzen.
|
||||||
|
|
||||||
Sie ist jedoch bewusst als Zwischenlösung getroffen worden. Sollte sich im Betrieb zeigen, dass die Pflege häufiger erfolgt oder von einem größeren Personenkreis übernommen wird als angenommen, wäre eine Verwaltungsoberfläche innerhalb des Kundenportals neu zu bewerten. Sie hätte zudem den Vorteil, die Ordnerstruktur nicht mehr manuell einhalten zu müssen — die Zuordnung von Kunde und Typ könnte dann durch die Anwendung erzwungen werden, statt sich auf die richtige Ablage durch die pflegende Person zu verlassen.
|
|
||||||
|
|
||||||
\subsection{Übertragbarkeit}
|
\subsection{Übertragbarkeit}
|
||||||
|
|
||||||
Über den Dokumentenbereich hinaus hat das Projekt zwei Bausteine hervorgebracht, die auch in anderem Zusammenhang verwendbar sind.
|
Das Projekt hat zwei wiederverwendbare Bausteine hervorgebracht.
|
||||||
|
|
||||||
Der \textbf{Zugriff auf den Objektspeicher} ist als eigener Dienst gekapselt und nicht auf Dokumente zugeschnitten. Weitere Anwendungsfälle, die größere Dateien außerhalb der Datenbank ablegen müssen, können darauf aufsetzen.
|
Der \textbf{Zugriff auf den Objektspeicher} ist als eigener Dienst gekapselt und nicht auf Dokumente zugeschnitten; weitere Anwendungsfälle können darauf aufsetzen.
|
||||||
|
|
||||||
Die \textbf{Muster für Freigabelinks und Vorschaubilder} — die Kodierung des relativen Pfades, die bewusst eingeschränkte Auslieferung von Vorschaubildern auf einem nicht angemeldeten Endpunkt und die Bereitstellung in einem von Kommunikationsanwendungen unterstützten Bildformat — sind ebenfalls unabhängig vom Dokumentenbereich und lassen sich für andere teilbare Inhalte des Kundenportals wiederverwenden.
|
Die \textbf{Muster für Freigabelinks und Vorschaubilder} — Pfadkodierung, eingeschränkte Auslieferung auf einem nicht angemeldeten Endpunkt, Bereitstellung in einem von Kommunikationsanwendungen unterstützten Format — lassen sich für andere teilbare Inhalte wiederverwenden.
|
||||||
|
|||||||
@@ -3,48 +3,46 @@
|
|||||||
|
|
||||||
\subsection{Externe Abhängigkeiten früher klären}
|
\subsection{Externe Abhängigkeiten früher klären}
|
||||||
|
|
||||||
Die deutlichste Lehre betrifft den Umgang mit Abhängigkeiten außerhalb des eigenen Einflussbereichs. Zwischen dem Antrag auf den Speicher und der abschließenden Auskunft über die verfügbaren Funktionen lagen vier Wochen — bei einem Projektzeitraum von wenigen Wochen ein erheblicher Anteil.
|
Zwischen Antrag und abschließender Auskunft über die verfügbaren Speicherfunktionen lagen vier Wochen — bei einem Projektzeitraum von wenigen Wochen ein erheblicher Anteil.
|
||||||
|
|
||||||
Der Fehler lag nicht in der Bearbeitungsdauer, die für einen Vorgang über einen externen Betreiber nicht ungewöhnlich ist, sondern im Zeitpunkt der Anfrage. Die fachliche Klärung war seit dem 18.~Juni abgeschlossen; dass ein Speicher benötigt wird, stand damit fest. Der Antrag folgte erst über vier Wochen später. Hätte er unmittelbar nach der Feature-Analyse vorgelegen, wäre auch die Frage nach den verfügbaren Funktionen früher beantwortet gewesen — und die Recherche zum Lookup hätte nicht auf eine Antwort warten müssen, die schließlich negativ ausfiel.
|
Der Fehler lag im Zeitpunkt: Die fachliche Klärung war seit dem 18.~Juni abgeschlossen; der Antrag folgte über vier Wochen später. Hätte er unmittelbar nach der Feature-Analyse vorgelegen, wäre die Frage nach den Speicherfunktionen früher beantwortet gewesen — die Lookup-Recherche hätte nicht auf eine letztlich negative Antwort warten müssen.
|
||||||
|
|
||||||
Die Lehre daraus ist allgemeiner Natur: Vorgänge, deren Dauer man nicht selbst bestimmt, gehören an den Anfang der Planung, unabhängig davon, wann ihr Ergebnis benötigt wird. Sie kosten in der Regel keine eigene Arbeitszeit, aber Wartezeit — und Wartezeit lässt sich nur durch frühes Beginnen verkürzen.
|
Vorgänge, deren Dauer man nicht selbst bestimmt, gehören an den Anfang der Planung. Sie kosten keine Arbeitszeit, aber Wartezeit — und Wartezeit lässt sich nur durch frühes Beginnen verkürzen.
|
||||||
|
|
||||||
\subsection{Eine Architekturentscheidung ist die Summe ihrer Randbedingungen}
|
\subsection{Eine Architekturentscheidung ist die Summe ihrer Randbedingungen}
|
||||||
|
|
||||||
Das Lookup-Problem war die technisch anspruchsvollste Aufgabe des Projekts. Aufschlussreich ist weniger die Lösung als der Weg dorthin.
|
Das Lookup-Problem war die technisch anspruchsvollste Aufgabe des Projekts.
|
||||||
|
|
||||||
Die zunächst betrachteten Ansätze — Zwischenspeicher, Anmeldetoken, Feld im ITSM-System — waren alle technisch tragfähig und hätten das Problem messbar entschärft. Verworfen wurden sie, weil sie jeweils eine zweite Stelle einführen, an der dieselbe Information gepflegt wird. Die schließlich gewählte Lösung ist nicht die naheliegendste; sie ergab sich erst aus der genauen Betrachtung der eigentlichen Randbedingung.
|
Die zunächst betrachteten Ansätze — Zwischenspeicher, Anmeldetoken, Feld im ITSM-System — waren technisch tragfähig, wurden aber verworfen, weil sie jeweils eine zweite Stelle einführen, an der dieselbe Information gepflegt wird. Die gewählte Lösung ergab sich aus der genauen Betrachtung der Randbedingungen.
|
||||||
|
|
||||||
Ausschlaggebend war die Unterscheidung zwischen der Anforderung und ihrer vermuteten Umsetzung: Die interne Pflege verlangte eine \emph{Sortierung nach Kundennamen}, nicht zwingend Ordnernamen, die \emph{ausschließlich} aus dem Kundennamen bestehen. Erst diese Präzisierung machte die Lösung sichtbar. Ohne die Rückfrage bei der internen Fachseite wäre die Anforderung als „Ordnername = Kundenname" verstanden und der Lösungsraum entsprechend enger geblieben.
|
Ausschlaggebend war die Unterscheidung zwischen Anforderung und vermuteter Umsetzung: Die interne Pflege verlangte eine \emph{Sortierung nach Kundennamen}, nicht zwingend Ordnernamen, die \emph{ausschließlich} aus dem Kundennamen bestehen. Diese Präzisierung machte die Lösung sichtbar; ohne die Rückfrage bei der internen Fachseite wäre der Lösungsraum enger geblieben.
|
||||||
|
|
||||||
Die Erkenntnis: Wenn alle betrachteten Lösungen unbefriedigend sind, lohnt sich die Prüfung, ob eine der Randbedingungen genauer formuliert werden kann, als sie zunächst verstanden wurde.
|
Wenn alle betrachteten Lösungen unbefriedigend sind, lohnt die Prüfung, ob eine Randbedingung genauer formuliert werden kann als zunächst verstanden.
|
||||||
|
|
||||||
\subsection{Eigene Arbeit gegenlesen}
|
\subsection{Eigene Arbeit gegenlesen}
|
||||||
|
|
||||||
Die Nebenläufigkeitsproblematik wurde nicht durch einen Test, ein Review oder einen Fehlerbericht gefunden, sondern durch das erneute Durchgehen des eigenen, bereits fertiggestellten Codes.
|
Die Nebenläufigkeit wurde nicht durch Test, Review oder Fehlerbericht gefunden, sondern durch erneutes Durchgehen des eigenen, fertiggestellten Codes.
|
||||||
|
|
||||||
Das ist insofern bemerkenswert, als der betreffende Pull Request zu diesem Zeitpunkt geschrieben und eingereicht war. Die Fehlerbehandlung des Umbenennens war ausdrücklich geschrieben worden, um Datenverlust zu verhindern — und genau sie verursacht ihn unter Nebenläufigkeit. Ein Sicherheitsnetz, das man selbst eingebaut hat, prüft man nicht ohne Weiteres erneut; die Annahme, dass es wirkt, ist die eigene.
|
Der Pull Request war eingereicht. Die Fehlerbehandlung des Umbenennens war geschrieben worden, um Datenverlust zu verhindern — und genau sie verursacht ihn unter Nebenläufigkeit. Ein selbst eingebautes Sicherheitsnetz prüft man nicht ohne Weiteres erneut.
|
||||||
|
|
||||||
Ausschlaggebend war ein Perspektivwechsel: nicht zu fragen „funktioniert das?", sondern „was passiert, wenn das hier zweimal gleichzeitig läuft?". Diese Frage ist an einer Anwendung, die in mehreren Instanzen betrieben wird, für jeden verändernden Codepfad zu stellen — und sie war beim Schreiben des Codes nicht gestellt worden, weil der Pfad als seltener Sonderfall galt. Genau diese Einordnung als Sonderfall ist es, die die Prüfung unterbleiben lässt.
|
Ausschlaggebend war ein Perspektivwechsel: nicht „funktioniert das?", sondern „was passiert, wenn das zweimal gleichzeitig läuft?". Diese Frage ist für jeden verändernden Codepfad einer Anwendung mit mehreren Instanzen zu stellen — sie war beim Schreiben unterblieben, weil der Pfad als seltener Sonderfall galt. Genau diese Einordnung ließ die Prüfung unterbleiben.
|
||||||
|
|
||||||
\subsection{Pull Requests kleiner und sequenziell schneiden}
|
\subsection{Pull Requests kleiner und sequenziell schneiden}
|
||||||
|
|
||||||
Die in Abschnitt~\ref{sec:code-reviews} beschriebene Ballung der Zusammenführungen war die Folge einer bewussten, im Rückblick aber falschen Entscheidung. Vier gleichzeitig eröffnete Pull Requests sollten verhindern, dass Arbeitszeit mit Warten auf Reviews vergeht.
|
Die in Abschnitt~\ref{sec:code-reviews} beschriebene Ballung war Folge einer bewussten, im Rückblick falschen Entscheidung: Vier gleichzeitig eröffnete Pull Requests sollten Wartezeiten auf Reviews vermeiden.
|
||||||
|
|
||||||
Der erwartete Vorteil trat nicht ein, weil die Arbeiten inhaltlich aufeinander aufbauten. Ein Reviewer konnte den zweiten Pull Request nicht sinnvoll abschließen, solange der erste offen war. Statt Parallelität entstand eine Warteschlange, deren Auflösung sich in die Woche vor Abnahme und Produktivsetzung verschob.
|
Der erwartete Vorteil trat nicht ein, weil die Arbeiten aufeinander aufbauten: Statt Parallelität entstand eine Warteschlange, deren Auflösung sich in die Woche vor Abnahme und Produktivsetzung verschob. Die Verkettung der Zielbranches brachte eigene Kosten: Jede Umstellung auf den Hauptbranch setzte Freigaben zurück. Bei abhängigen Aufgaben ist sequenzielles Vorgehen vorzuziehen.
|
||||||
|
|
||||||
Die Verkettung der Zielbranches, die das Reviewproblem löste, brachte zudem eigene Kosten mit sich: Jede Umstellung auf den Hauptbranch setzte abgegebene Freigaben zurück. Für künftige Arbeiten wäre der Schluss, bei inhaltlich abhängigen Aufgaben sequenziell vorzugehen und Parallelität nur dort zu suchen, wo tatsächliche Unabhängigkeit besteht.
|
|
||||||
|
|
||||||
\subsection{Konsistenz braucht einen Vergleich, keine Beschreibung}
|
\subsection{Konsistenz braucht einen Vergleich, keine Beschreibung}
|
||||||
|
|
||||||
Der einzige aus der Abnahme hervorgegangene Fehler betrifft eine Anforderung, die in mehreren Backlog Items sinngemäß enthalten war: Die Bedienung soll sich an den übrigen Seiten der Anwendung orientieren.
|
Der einzige Abnahmefehler betrifft eine Anforderung aus mehreren Backlog Items: Die Bedienung soll sich an den übrigen Seiten orientieren.
|
||||||
|
|
||||||
Diese Anforderung wurde nicht verletzt, weil sie übersehen worden wäre, sondern weil sie sich nicht vollständig aus einer Beschreibung ableiten lässt. Eine Schaltfläche zum Leeren des Suchfelds ist eine sinnvolle Ergänzung — sie widerspricht keiner Vorgabe. Erst der unmittelbare Vergleich mit den bestehenden Seiten macht die Abweichung sichtbar.
|
Sie wurde nicht verletzt, weil sie übersehen wurde, sondern weil sie sich nicht aus einer Beschreibung ableiten lässt. Eine Schaltfläche zum Leeren des Suchfelds widerspricht keiner Vorgabe — erst der Vergleich mit den bestehenden Seiten macht die Abweichung sichtbar.
|
||||||
|
|
||||||
Für Anforderungen dieser Art ist ein Abnahmetest durch eine Person, die die übrige Anwendung kennt, nicht durch vorgelagerte Prüfstufen ersetzbar. Das ist zugleich ein Argument dafür, solche Tests früher anzusetzen als hier geschehen.
|
Für solche Anforderungen ist ein Abnahmetest durch eine Person, die die Anwendung kennt, unersetzbar — und sollte früher angesetzt werden als hier geschehen.
|
||||||
|
|
||||||
\subsection{Persönliche Einordnung}
|
\subsection{Persönliche Einordnung}
|
||||||
|
|
||||||
Gegenüber den vorangegangenen Praktika lag der Schwerpunkt dieses Projekts deutlicher auf Entwurfsentscheidungen als auf der Implementierung. Der Anteil der Zeit, der auf Recherche, Abwägung und Begründung entfiel, war erheblich — die Bewertung von sechs Lösungsansätzen für den Lookup und die Analyse der Nebenläufigkeit erzeugten für sich genommen keine einzige Zeile ausgelieferten Codes.
|
Gegenüber den vorangegangenen Praktika lag der Schwerpunkt auf Entwurfsentscheidungen. Der Anteil für Recherche, Abwägung und Begründung war erheblich — die sechs Lösungsansätze für den Lookup und die Nebenläufigkeitsanalyse erzeugten keine einzige Zeile ausgelieferten Codes.
|
||||||
|
|
||||||
Zugleich waren es genau diese Anteile, die den Verlauf des Projekts bestimmten. Die Erfahrung, dass eine sauber begründete und dokumentierte Abgrenzung — etwa die eines erkannten Problems in ein eigenes Backlog Item — fachlich mehr wert ist als eine schnelle, unvollständige Behebung, ist die wesentliche persönliche Erkenntnis aus diesem Projekt.
|
Zugleich bestimmten diese Anteile den Projektverlauf. Die Erfahrung, dass eine sauber begründete Abgrenzung mehr wert ist als eine schnelle, unvollständige Behebung, ist die wesentliche Erkenntnis dieses Projekts.
|
||||||
|
|||||||
@@ -30,24 +30,24 @@ Nebenläufigkeit (10070) & --- & offen & nach Projektzeitraum \\
|
|||||||
Bugfix aus Abnahme (10134) & --- & Sprint 17.2026 & nach Projektzeitraum \\
|
Bugfix aus Abnahme (10134) & --- & Sprint 17.2026 & nach Projektzeitraum \\
|
||||||
\end{longtable}
|
\end{longtable}
|
||||||
|
|
||||||
Die Analyse- und Planungsphase verlief planmäßig. Die Abweichungen treten ausschließlich in der zweiten Projekthälfte auf und haben zwei klar benennbare Ursachen.
|
Die Analyse- und Planungsphase verlief planmäßig; die Abweichungen treten in der zweiten Projekthälfte auf und haben zwei Ursachen.
|
||||||
|
|
||||||
\subsection{Ursache 1: Nicht eingeplanter Infrastrukturvorlauf}
|
\subsection{Ursache 1: Nicht eingeplanter Infrastrukturvorlauf}
|
||||||
|
|
||||||
Die Beschaffung des Speichers war in der ursprünglichen Planung nicht als eigener Vorgang enthalten. Sie wurde erst am 22.~Juli beantragt, obwohl die fachliche Klärung bereits seit dem 18.~Juni abgeschlossen war. Die eigentliche Bereitstellung dauerte dann mit fünf Arbeitstagen nicht ungewöhnlich lange — der Verlust entstand dadurch, dass der Antrag erst spät gestellt wurde.
|
Die Beschaffung des Speichers war nicht als eigener Vorgang eingeplant und wurde erst am 22.~Juli beantragt, obwohl die fachliche Klärung seit dem 18.~Juni abgeschlossen war. Die Bereitstellung dauerte fünf Arbeitstage — der Verlust entstand durch den späten Antrag.
|
||||||
|
|
||||||
Unmittelbare Verzögerung entstand daraus nicht, weil die Umsetzung ohnehin erst am 27.~Juli begann. Anders verhielt es sich mit der Klärung, welche Speicherfunktionen zur Verfügung stehen: Diese zog sich vom 30.~Juli bis zum 17.~August, also über zweieinhalb Wochen, und fiel damit mitten in die Umsetzungsphase. Die Recherche zum Kundenordner-Lookup war während dieser Zeit blockiert, soweit sie von der Antwort abhing.
|
Unmittelbare Verzögerung entstand nicht, da die Umsetzung erst am 27.~Juli begann. Die Klärung der Speicherfunktionen zog sich jedoch vom 30.~Juli bis zum 17.~August und fiel in die Umsetzungsphase; die Lookup-Recherche war währenddessen blockiert.
|
||||||
|
|
||||||
\subsection{Ursache 2: Nachgeschobene Backlog Items}
|
\subsection{Ursache 2: Nachgeschobene Backlog Items}
|
||||||
|
|
||||||
Die zweite Abweichung ergibt sich aus dem Umfang. Von den 14 Backlog Items waren acht zu Beginn geschnitten; sechs kamen erst während der Umsetzung hinzu — Typfilter, Paginierung, die Recherche zur Speicherabfrage, der neue Lookup, die automatische Ordneranlage und die Nebenläufigkeit.
|
Von den 14 Backlog Items waren acht zu Beginn geschnitten; sechs kamen während der Umsetzung hinzu — Typfilter, Paginierung, Recherche zur Speicherabfrage, neuer Lookup, automatische Ordneranlage und Nebenläufigkeit.
|
||||||
|
|
||||||
Diese Items entstanden nicht aus einer Ausweitung des fachlichen Umfangs. Sie entstanden aus technischen Problemen, die erst bei der Implementierung sichtbar wurden, sowie aus der Zuarbeit des Entwurfs im Juli. Die ursprüngliche Schätzung von 35 Aufwandspunkten wuchs damit auf 46 Punkte — ein Zuwachs von rund einem Drittel.
|
Sie entstanden nicht aus fachlicher Ausweitung, sondern aus technischen Problemen, die erst bei der Implementierung sichtbar wurden. Die Schätzung wuchs von 35 auf 46 Aufwandspunkte — rund ein Drittel Zuwachs.
|
||||||
|
|
||||||
\subsection{Abgleich mit den Anforderungen}
|
\subsection{Abgleich mit den Anforderungen}
|
||||||
|
|
||||||
Von den 13 funktionalen Anforderungen sind zwölf ausgeliefert und im Betrieb. Die dreizehnte — die automatische Anlage der Ordnerstruktur — ist implementiert, aber Bestandteil des noch nicht zusammengeführten Pull Requests.
|
Von 13 funktionalen Anforderungen sind zwölf ausgeliefert. Die dreizehnte — automatische Anlage der Ordnerstruktur — ist implementiert, aber im noch nicht zusammengeführten Pull Request.
|
||||||
|
|
||||||
Bei den nichtfunktionalen Anforderungen ist das Bild ähnlich. Fünf der sechs sind erfüllt. Die Skalierbarkeit des Lookups ist gelöst, die Lösung aber ebenfalls noch nicht ausgeliefert; bis dahin gilt weiterhin das lineare Verfahren. Die Anforderung ist damit konzeptionell und implementatorisch erfüllt, betrieblich jedoch noch nicht wirksam.
|
Bei den nichtfunktionalen Anforderungen sind fünf von sechs erfüllt. Die Skalierbarkeit des Lookups ist gelöst, aber nicht ausgeliefert; bis dahin gilt das lineare Verfahren — konzeptionell erfüllt, betrieblich noch nicht wirksam.
|
||||||
|
|
||||||
Hinzu kommt eine Einschränkung, die zu Projektbeginn nicht bekannt war: die in Abschnitt~\ref{sec:race-conditions} beschriebene Nebenläufigkeit im verändernden Teil des Lookups. Sie ist keine unerfüllte Anforderung, sondern eine im Zuge der Umsetzung neu erkannte Eigenschaft, die als eigenes Backlog Item erfasst und freigegeben ist.
|
Hinzu kommt die Nebenläufigkeit (Abschnitt~\ref{sec:race-conditions}) — keine unerfüllte Anforderung, sondern eine neu erkannte Eigenschaft, als eigenes Backlog Item erfasst und freigegeben.
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
\section{Autorisierungskonzept}
|
\section{Autorisierungskonzept}
|
||||||
\label{sec:authorization}
|
\label{sec:authorization}
|
||||||
|
|
||||||
Der Dokumentenbereich verarbeitet Vertragsunterlagen, Abrechnungsdaten und Security Assessments. Ein fehlerhafter Zugriff hätte damit unmittelbar die Offenlegung vertraulicher Kundendaten zur Folge. Das Autorisierungskonzept arbeitet deshalb zweistufig: Eine Rollenprüfung entscheidet, \emph{ob} ein Benutzer den Bereich überhaupt betreten darf, und eine Mandantenprüfung entscheidet, \emph{welche} Dokumente er darin sieht.
|
Der Dokumentenbereich verarbeitet Vertragsunterlagen, Abrechnungsdaten und Security Assessments. Das Autorisierungskonzept arbeitet zweistufig: Eine Rollenprüfung entscheidet, \emph{ob} ein Benutzer den Bereich betreten darf, eine Mandantenprüfung, \emph{welche} Dokumente er sieht.
|
||||||
|
|
||||||
\subsection{Authentifizierung und Claims}
|
\subsection{Authentifizierung und Claims}
|
||||||
|
|
||||||
Die Authentifizierung erfolgt wie in der übrigen Houston-Anwendung über Microsoft Entra~ID. Nach erfolgreicher Anmeldung erhält Houston ein Token, das neben der Identität des Benutzers zwei für dieses Projekt wesentliche Angaben enthält:
|
Die Authentifizierung erfolgt über Microsoft Entra~ID. Das Token enthält zwei für dieses Projekt wesentliche Angaben:
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item die \textbf{Efecte-Organisations-ID} des Benutzers, über die seine Zugehörigkeit zu einem Kunden bestimmt wird,
|
\item die \textbf{Efecte-Organisations-ID} des Benutzers, über die seine Zugehörigkeit zu einem Kunden bestimmt wird,
|
||||||
\item den \textbf{Efecte-Firmennamen}, der über den Claim \texttt{efecte:company\_name} bereitgestellt wird.
|
\item den \textbf{Efecte-Firmennamen}, der über den Claim \texttt{efecte:company\_name} bereitgestellt wird.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
Beide Werte lagen bereits vor Projektbeginn im Token vor und mussten nicht neu eingeführt werden. Dass der Firmenname verfügbar ist, wurde erst im Rahmen der Recherche zum Kundenordner-Lookup als relevant erkannt — er ermöglicht es, den Ordnernamen ohne zusätzlichen Efecte-Aufruf zu bestimmen (siehe Abschnitt~\ref{sec:lookup-decision}).
|
Beide Werte lagen bereits vor Projektbeginn im Token vor. Dass der Firmenname verfügbar ist, wurde erst bei der Recherche zum Kundenordner-Lookup erkannt — er ermöglicht die Bestimmung des Ordnernamens ohne zusätzlichen Efecte-Aufruf (siehe Abschnitt~\ref{sec:lookup-decision}).
|
||||||
|
|
||||||
\subsection{Anwendungsrolle Documents.Read}
|
\subsection{Anwendungsrolle Documents.Read}
|
||||||
|
|
||||||
Für den Zugriff auf den Dokumentenbereich wurde in Entra~ID die Anwendungsrolle \texttt{Documents.Read} angelegt. In der Feature-Analyse war ausdrücklich geklärt worden, dass keine feingranulareren Berechtigungen unterhalb der Menüpunkt-Ebene erforderlich sind: Ein Benutzer sieht entweder alle Dokumente seiner Organisation oder gar keine. Eine Differenzierung nach Dokumententyp — etwa ein Zugriff auf Monitoring-Reports ohne Zugriff auf Vertragsunterlagen — wurde bewusst nicht vorgesehen.
|
Für den Zugriff auf den Dokumentenbereich wurde in Entra~ID die Anwendungsrolle \texttt{Documents.Read} angelegt. Eine Differenzierung nach Dokumententyp ist nicht vorgesehen: Ein Benutzer sieht entweder alle Dokumente seiner Organisation oder gar keine.
|
||||||
|
|
||||||
Die Rolle wirkt an zwei Stellen. Erstens steuert sie die Sichtbarkeit des Navigationspunkts: Ohne die Rolle erscheint „Dokumente" nicht im Menü. Zweitens schützt sie die Seite selbst gegen den direkten Aufruf über die URL. Die zweite Prüfung ist die eigentlich sicherheitsrelevante; das Ausblenden des Menüpunkts ist reine Benutzerführung und darf nicht als Schutzmaßnahme betrachtet werden.
|
Die Rolle wirkt an zwei Stellen: Sie steuert die Sichtbarkeit des Navigationspunkts und schützt die Seite gegen direkten URL-Aufruf. Die zweite Prüfung ist sicherheitsrelevant; das Ausblenden des Menüpunkts ist reine Benutzerführung.
|
||||||
|
|
||||||
\subsection{403 statt 404}
|
\subsection{403 statt 404}
|
||||||
|
|
||||||
Ein diskutierter Punkt war die Frage, welche Antwort ein Benutzer ohne Berechtigung beim direkten Aufruf von \texttt{/documents} erhalten soll. Die ursprüngliche Fassung der Anforderung sah eine 404-Antwort vor. Die Überlegung dahinter ist verbreitet: Eine 404-Antwort verrät nicht, dass die Ressource überhaupt existiert, und verhindert damit Rückschlüsse auf vorhandene Funktionen.
|
Ein diskutierter Punkt war die Antwort bei unberechtigtem Zugriff auf \texttt{/documents}. Die ursprüngliche Anforderung sah 404 vor, um die Existenz der Ressource zu verbergen.
|
||||||
|
|
||||||
Im Verlauf der Umsetzung wurde die Anforderung auf eine 403-Antwort geändert. Ausschlaggebend waren zwei Argumente. Zum einen ist die Existenz des Dokumentenbereichs kein Geheimnis — es handelt sich um eine allgemein bekannte Funktion des Kundenportals, nicht um eine verborgene Ressource. Der Informationsgewinn eines Angreifers ist damit gleich null. Zum anderen erschwert eine 404-Antwort die Fehlersuche erheblich: Ein Benutzer, dem versehentlich die Rolle fehlt, erhält dieselbe Antwort wie bei einem Tippfehler in der Adresse. Genau dieser Fall trat später im Abnahmetest tatsächlich ein (siehe Abschnitt~\ref{sec:acceptance-testing}) und bestätigte die Entscheidung.
|
Die Anforderung wurde auf 403 geändert. Die Existenz des Dokumentenbereichs ist kein Geheimnis. Zudem erschwert 404 die Fehlersuche: Ein Benutzer ohne Rolle erhält dieselbe Antwort wie bei einem Tippfehler. Dieser Fall trat später im Abnahmetest ein (siehe Abschnitt~\ref{sec:acceptance-testing}) und bestätigte die Entscheidung.
|
||||||
|
|
||||||
Der Trade-off lautet also: minimaler, hier praktisch nicht vorhandener Informationsgewinn für einen Angreifer gegen deutlich bessere Diagnostizierbarkeit im Betrieb. Die Entscheidung fiel zugunsten der Diagnostizierbarkeit.
|
Der Trade-off: minimaler Informationsgewinn für einen Angreifer gegen deutlich bessere Diagnostizierbarkeit.
|
||||||
|
|
||||||
\subsection{Mandantentrennung}
|
\subsection{Mandantentrennung}
|
||||||
|
|
||||||
Die zweite Stufe stellt sicher, dass ein berechtigter Benutzer ausschließlich die Dokumente seiner eigenen Organisation sieht. Sie beruht vollständig auf dem Ablagekonzept aus Abschnitt~\ref{sec:s3-layout}: Aus der Organisations-ID im Token wird der zugehörige Kundenordner aufgelöst, und sämtliche Abfragen an den Speicher werden auf dieses Präfix eingeschränkt.
|
Die zweite Stufe stellt sicher, dass ein berechtigter Benutzer ausschließlich die Dokumente seiner Organisation sieht. Aus der Organisations-ID wird der zugehörige Kundenordner aufgelöst, und alle Abfragen werden auf dieses Präfix eingeschränkt (siehe Abschnitt~\ref{sec:s3-layout}).
|
||||||
|
|
||||||
Entscheidend ist dabei, dass die Einschränkung nicht nachträglich auf ein bereits geladenes Ergebnis angewendet wird, sondern bereits Bestandteil der Abfrage ist. Houston lädt zu keinem Zeitpunkt Dokumente fremder Kunden in den Speicher, um sie anschließend herauszufiltern. Ein Fehler in der Darstellungsschicht kann damit nicht zu einer Offenlegung führen.
|
Entscheidend ist, dass die Einschränkung bereits Bestandteil der Abfrage ist — Houston lädt nie Dokumente fremder Kunden, um sie nachträglich herauszufiltern. Ein Fehler in der Darstellungsschicht kann damit nicht zu einer Offenlegung führen.
|
||||||
|
|
||||||
Abbildung~\ref{fig:auth-sequence} stellt den vollständigen Ablauf dar.
|
Abbildung~\ref{fig:auth-sequence} stellt den vollständigen Ablauf dar.
|
||||||
|
|
||||||
@@ -45,8 +45,8 @@ Abbildung~\ref{fig:auth-sequence} stellt den vollständigen Ablauf dar.
|
|||||||
|
|
||||||
\subsection{Absicherung der Einzelzugriffe}
|
\subsection{Absicherung der Einzelzugriffe}
|
||||||
|
|
||||||
Die Auflistung ist nicht der einzige Zugriffspfad. Auch der Download einzelner Dokumente, der ZIP-Download, die PDF-Vorschau und die Freigabelinks nehmen jeweils einen Dokumentschlüssel entgegen. Für jeden dieser Pfade gilt dieselbe Regel: Der angeforderte Schlüssel wird gegen das aufgelöste Kundenpräfix geprüft, bevor auf den Speicher zugegriffen wird.
|
Auch Download, ZIP-Download, PDF-Vorschau und Freigabelinks nehmen jeweils einen Dokumentschlüssel entgegen. Für jeden Pfad wird der angeforderte Schlüssel gegen das aufgelöste Kundenpräfix geprüft, bevor auf den Speicher zugegriffen wird.
|
||||||
|
|
||||||
Zusätzlich wird der übergebene Pfad daraufhin untersucht, ob er Bestandteile enthält, mit denen sich der Kundenordner verlassen ließe. Ohne diese Prüfung könnte ein Benutzer durch Manipulation des Parameters auf fremde Ordner zugreifen, obwohl das Präfix zunächst korrekt gesetzt war. Diese Anforderung (NFA-6) wurde im Approval-Termin ausdrücklich in die Akzeptanzkriterien aufgenommen.
|
Zusätzlich wird der Pfad auf Bestandteile untersucht, mit denen sich der Kundenordner verlassen ließe (NFA-6). Ohne diese Prüfung könnte ein Benutzer durch Manipulation des Parameters auf fremde Ordner zugreifen.
|
||||||
|
|
||||||
Die Freigabelinks stellen dabei keine Ausnahme dar: Sie enthalten keine Anmeldeinformationen und gewähren keinen eigenständigen Zugriff. Ein Empfänger, der nicht über die Rolle und die passende Organisationszugehörigkeit verfügt, erhält beim Aufruf dieselbe 403-Antwort wie bei jedem anderen Zugriffsversuch. Der Link dient ausschließlich dazu, innerhalb des berechtigten Personenkreises auf ein bestimmtes Dokument zu verweisen.
|
Freigabelinks stellen keine Ausnahme dar: Sie enthalten keine Anmeldeinformationen und gewähren keinen eigenständigen Zugriff. Ein Empfänger ohne Rolle und passende Organisationszugehörigkeit erhält dieselbe 403-Antwort. Der Link verweist lediglich innerhalb des berechtigten Personenkreises auf ein Dokument.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
\subsection{Der Typenkatalog}
|
\subsection{Der Typenkatalog}
|
||||||
|
|
||||||
Die sieben Dokumententypen wurden fachlich in der Feature-Beschreibung festgelegt und im Projektverlauf nicht verändert. Sie bilden die Kategorien ab, in denen WorkSimple gegenüber Kunden regelmäßig Unterlagen bereitstellt. Tabelle~\ref{tab:document-types} zeigt den Katalog mit dem jeweiligen Ordnernamen im Speicher.
|
Die sieben Dokumententypen wurden in der Feature-Beschreibung festgelegt und im Projektverlauf nicht verändert. Tabelle~\ref{tab:document-types} zeigt den Katalog mit dem jeweiligen Ordnernamen im Speicher.
|
||||||
|
|
||||||
\begin{table}[H]
|
\begin{table}[H]
|
||||||
\centering
|
\centering
|
||||||
@@ -26,24 +26,24 @@ Die sieben Dokumententypen wurden fachlich in der Feature-Beschreibung festgeleg
|
|||||||
|
|
||||||
\subsection{Feste Kodierung statt Konfiguration}
|
\subsection{Feste Kodierung statt Konfiguration}
|
||||||
|
|
||||||
Im Approval-Termin stellte \emph{Timo Walter} die Frage, ob die Kategorien konfigurierbar sein sollen und ob im Betrieb weitere Typen hinzukommen können. Die Entscheidung fiel auf eine feste Kodierung in Houston. Dafür sprechen mehrere Überlegungen.
|
Im Approval-Termin stellte \emph{Timo Walter} die Frage, ob die Kategorien konfigurierbar sein sollen. Die Entscheidung fiel auf eine feste Kodierung in Houston.
|
||||||
|
|
||||||
Der Typenkatalog ist fachlich stabil. Er leitet sich aus den Leistungen ab, die WorkSimple erbringt, und ändert sich allenfalls im Rhythmus von Jahren. Eine Konfigurierbarkeit würde für diesen Änderungsrhythmus eine Verwaltungsoberfläche, ein Speicherformat und eine Migrationsstrategie erfordern — ein Aufwand, der in keinem Verhältnis zum Nutzen steht.
|
Der Typenkatalog ist fachlich stabil und ändert sich allenfalls im Rhythmus von Jahren. Konfigurierbarkeit würde Verwaltungsoberfläche, Speicherformat und Migrationsstrategie erfordern — ein unverhältnismäßiger Aufwand.
|
||||||
|
|
||||||
Hinzu kommt, dass die Typen nicht nur als Beschriftung auftreten. Jeder Typ benötigt ein Icon und eine Übersetzung, und beide müssten bei einem frei konfigurierbaren Katalog ebenfalls hinterlegbar sein. Ein neuer Typ ohne Icon fiele in der Oberfläche unangenehm auf. Die feste Kodierung stellt sicher, dass Ordnername, Anzeigetext und Icon stets gemeinsam gepflegt werden.
|
Hinzu kommt, dass jeder Typ ein Icon und eine Übersetzung benötigt, die bei einem frei konfigurierbaren Katalog ebenfalls hinterlegbar sein müssten. Die feste Kodierung stellt sicher, dass Ordnername, Anzeigetext und Icon gemeinsam gepflegt werden.
|
||||||
|
|
||||||
Der Preis dieser Entscheidung ist, dass das Hinzufügen eines Typs eine Codeänderung und ein Release erfordert. Angesichts der erwarteten Änderungshäufigkeit ist das vertretbar.
|
Der Preis: Das Hinzufügen eines Typs erfordert eine Codeänderung und ein Release. Angesichts der erwarteten Änderungshäufigkeit ist das vertretbar.
|
||||||
|
|
||||||
\subsection{Verhalten bei unbekannten Ordnern}
|
\subsection{Verhalten bei unbekannten Ordnern}
|
||||||
|
|
||||||
Aus der festen Kodierung ergibt sich unmittelbar die Frage, wie mit Unterordnern umzugehen ist, die nicht im Katalog stehen. Ein Kundenbetreuer könnte in Filestash versehentlich einen Ordner \texttt{Sonstiges} anlegen oder sich beim Namen vertippen.
|
Aus der festen Kodierung ergibt sich die Frage, wie mit Unterordnern umzugehen ist, die nicht im Katalog stehen — etwa einem Ordner \texttt{Sonstiges} in Filestash.
|
||||||
|
|
||||||
Das Konzept sieht vor, dass Dokumente in solchen Ordnern nicht verschwinden. Sie werden angezeigt und erhalten ein neutrales Standard-Icon; lediglich die Typzuordnung entfällt. Die Alternative — solche Dokumente auszublenden — wurde verworfen, weil sie ein stilles Fehlverhalten erzeugen würde: Ein hochgeladenes Dokument wäre für den Kunden unsichtbar, ohne dass dies für den Betreuer erkennbar wäre.
|
Dokumente in solchen Ordnern werden angezeigt und erhalten ein neutrales Standard-Icon; lediglich die Typzuordnung entfällt. Ausblenden wurde verworfen, weil es stilles Fehlverhalten erzeugen würde: Ein Dokument wäre unsichtbar, ohne dass der Betreuer dies bemerkt.
|
||||||
|
|
||||||
Zu unterscheiden ist dieser Fall vom Umgang mit unbekannten Ordnern auf der \emph{obersten} Ebene. Dort führt ein fehlendes Marker-Metadatum zum vollständigen Ausschluss (siehe Abschnitt~\ref{sec:s3-layout}). Der Unterschied ist beabsichtigt: Auf oberster Ebene entscheidet die Struktur über die Mandantentrennung, hier gilt im Zweifel Ausschluss. Innerhalb eines Kundenordners ist die Zugehörigkeit dagegen bereits geklärt, und ein unbekannter Unterordner ist lediglich ein Darstellungsproblem.
|
Zu unterscheiden ist dieser Fall vom Umgang mit unbekannten Ordnern auf der \emph{obersten} Ebene, wo ein fehlendes Marker-Metadatum zum vollständigen Ausschluss führt (siehe Abschnitt~\ref{sec:s3-layout}). Der Unterschied ist beabsichtigt: Auf oberster Ebene entscheidet die Struktur über die Mandantentrennung; innerhalb eines Kundenordners ist die Zugehörigkeit bereits geklärt.
|
||||||
|
|
||||||
\subsection{Übersetzung der Anzeigenamen}
|
\subsection{Übersetzung der Anzeigenamen}
|
||||||
|
|
||||||
Die Ordnernamen im Speicher dienen zwei verschiedenen Zielgruppen. Für die Kundenbetreuer, die in Filestash arbeiten, müssen sie lesbar und eindeutig sein. Für den Kunden in Houston sollen sie sich in die Oberfläche einfügen.
|
Die Ordnernamen im Speicher dienen zwei Zielgruppen: Für die Kundenbetreuer in Filestash müssen sie lesbar und eindeutig sein, für den Kunden in Houston sollen sie sich in die Oberfläche einfügen.
|
||||||
|
|
||||||
Aus diesem Grund wird der Ordnername nicht unverändert angezeigt, sondern über eine Ressourcendatei auf einen Anzeigetext abgebildet. Dieses Vorgehen entspricht dem in Houston bereits etablierten Muster zur Lokalisierung und erlaubt es, den Anzeigetext zu ändern, ohne die Struktur im Speicher anzufassen — eine Umbenennung dort würde bedeuten, sämtliche betroffenen Objekte umzukopieren.
|
Der Ordnername wird nicht unverändert angezeigt, sondern über eine Ressourcendatei auf einen Anzeigetext abgebildet. Dies entspricht dem in Houston etablierten Lokalisierungsmuster und erlaubt Änderungen am Anzeigetext, ohne die Speicherstruktur anzufassen — eine Umbenennung dort würde das Umkopieren sämtlicher betroffenen Objekte erfordern.
|
||||||
|
|||||||
@@ -3,31 +3,31 @@
|
|||||||
|
|
||||||
\subsection{Die zugrunde liegende Idee}
|
\subsection{Die zugrunde liegende Idee}
|
||||||
|
|
||||||
Alle in Abschnitt~\ref{sec:lookup-research} betrachteten Ansätze versuchen, die Zuordnung zwischen Organisations-ID und Ordnername \emph{zusätzlich} irgendwo zu hinterlegen — in einem Cache, in einem Token, in Efecte oder in einer Zuordnungsdatei. Jeder dieser Ansätze führt damit eine zweite Datenhaltung ein, die mit der ersten konsistent gehalten werden muss.
|
Alle in Abschnitt~\ref{sec:lookup-research} betrachteten Ansätze hinterlegen die Zuordnung \emph{zusätzlich} irgendwo — in Cache, Token, Efecte oder einer Zuordnungsdatei. Jeder führt eine zweite, konsistent zu haltende Datenhaltung ein.
|
||||||
|
|
||||||
Die schließlich gewählte Lösung dreht die Fragestellung um. Statt die Zuordnung nachzuschlagen, wird sie \emph{berechenbar} gemacht: Wenn der Ordnername sich deterministisch aus dem Firmennamen ableiten lässt und dieser Firmenname bereits im Anmeldetoken steht, kann Houston den erwarteten Ordnernamen ohne jeden Nachschlagevorgang bestimmen. Aus der Suche wird ein direkter Zugriff.
|
Die gewählte Lösung dreht die Fragestellung um: Lässt sich der Ordnername deterministisch aus dem Firmennamen ableiten und steht dieser im Token, kann Houston den Ordnernamen ohne Nachschlagevorgang bestimmen. Aus der Suche wird ein direkter Zugriff.
|
||||||
|
|
||||||
Voraussetzung dafür ist, dass die Ordner im Speicher tatsächlich so heißen, wie Houston es erwartet. Genau hier setzt die eigentliche Entscheidung an: \textbf{Houston wird zur führenden Instanz für die Namensgebung der Kundenordner.} Weicht ein Ordner vom erwarteten Namen ab, wird nicht der Erwartungswert angepasst, sondern der Ordner umbenannt.
|
Voraussetzung ist, dass die Ordner tatsächlich so heißen, wie Houston es erwartet. \textbf{Houston wird zur führenden Instanz für die Namensgebung der Kundenordner.} Weicht ein Ordner ab, wird er umbenannt.
|
||||||
|
|
||||||
Diese Festlegung ist deshalb vertretbar, weil Houston die Ordnerstruktur ohnehin selbst anlegt (FA-13). Wenn die Anwendung neue Kundenordner erzeugt, ist es folgerichtig, dass sie auch deren Benennung verantwortet. Die Kundenbetreuer verlieren dadurch nichts: Der Ordner heißt weiterhin nach dem Kunden, nur eben in einer normalisierten Form.
|
Diese Festlegung ist vertretbar, weil Houston die Ordnerstruktur ohnehin selbst anlegt (FA-13). Die Kundenbetreuer verlieren nichts: Der Ordner heißt weiterhin nach dem Kunden, nur normalisiert.
|
||||||
|
|
||||||
\subsection{Ableitung des Ordnernamens}
|
\subsection{Ableitung des Ordnernamens}
|
||||||
|
|
||||||
Der Firmenname aus dem Token kann nicht unverändert als Ordnername verwendet werden. Er kann Zeichen enthalten, die im Schlüssel problematisch sind, führende oder abschließende Leerzeichen aufweisen oder beliebig lang sein. Er wird deshalb durch eine Normalisierungsfunktion geführt, die im weiteren Verlauf als \emph{Slug} bezeichnet wird.
|
Der Firmenname aus dem Token kann nicht unverändert als Ordnername dienen — er kann problematische Zeichen, führende Leerzeichen oder beliebige Länge aufweisen. Er wird durch eine Normalisierungsfunktion geführt, im Folgenden \emph{Slug} genannt.
|
||||||
|
|
||||||
Die Normalisierung folgt zwei Leitgedanken. Sie muss \textbf{deterministisch} sein — derselbe Firmenname muss stets denselben Ordnernamen ergeben, sonst funktioniert der direkte Zugriff nicht. Und sie muss das Ergebnis \textbf{lesbar} halten, weil der Ordner in Filestash von Menschen bedient wird. Aus dem zweiten Punkt folgt eine bewusste Abweichung von der üblichen Praxis: Umlaute werden \emph{nicht} ersetzt. Eine Firma „Müller GmbH" erhält den Ordner \texttt{Müller GmbH} und nicht \texttt{Mueller GmbH}, weil der Betreuer sie andernfalls in der alphabetischen Liste an unerwarteter Stelle suchen müsste. Die Zulässigkeit solcher Zeichen in Objektschlüsseln wurde anhand der Herstellerdokumentation geprüft \autocite{aws-s3-naming, ibm-s3-naming}; die Frage war auch Gegenstand des Code-Reviews.
|
Die Normalisierung muss \textbf{deterministisch} sein — derselbe Firmenname muss stets denselben Ordnernamen ergeben — und das Ergebnis \textbf{lesbar} halten, weil der Ordner in Filestash von Menschen bedient wird. Umlaute werden bewusst \emph{nicht} ersetzt: „Müller GmbH" erhält den Ordner \texttt{Müller GmbH}, nicht \texttt{Mueller GmbH}, weil der Betreuer die Firma sonst in der alphabetischen Liste an unerwarteter Stelle suchen müsste. Die Zulässigkeit solcher Zeichen in Objektschlüsseln wurde anhand der Herstellerdokumentation geprüft \autocite{aws-s3-naming, ibm-s3-naming}.
|
||||||
|
|
||||||
\subsection{Umgang mit Namenskollisionen}
|
\subsection{Umgang mit Namenskollisionen}
|
||||||
|
|
||||||
Firmennamen sind nicht garantiert eindeutig. Efecte lässt zwei Organisationen mit identischem Namen grundsätzlich zu, und sobald der Ordnername aus dem Firmennamen abgeleitet wird, treffen beide auf denselben Zielnamen. Da eine Verwechslung hier unmittelbar bedeuten würde, dass ein Kunde die Dokumente eines anderen sieht, muss dieser Fall abgedeckt sein.
|
Firmennamen sind nicht garantiert eindeutig. Efecte lässt zwei Organisationen mit identischem Namen zu, und beide würden auf denselben Ordnernamen treffen. Da eine Verwechslung bedeuten würde, dass ein Kunde fremde Dokumente sieht, muss der Fall abgedeckt sein.
|
||||||
|
|
||||||
Das Konzept löst ihn nach dem Prinzip „wer zuerst kommt, mahlt zuerst": Die erste Organisation, die den Namen beansprucht, erhält ihn. Jede weitere erhält einen Ordner, dem die Organisations-ID in Klammern angehängt wird, also etwa \texttt{Beispielkunde GmbH (77)}. Dieser Name ist eindeutig, bleibt lesbar und sortiert weiterhin unmittelbar neben dem gleichnamigen Ordner.
|
Die erste Organisation, die den Namen beansprucht, erhält ihn. Jede weitere erhält einen Ordner mit angehängter Organisations-ID, etwa \texttt{Beispielkunde GmbH (77)} — eindeutig, lesbar und alphabetisch neben dem gleichnamigen Ordner.
|
||||||
|
|
||||||
Maßgeblich ist dabei eine Sicherheitsregel: Ein Zielname wird nur dann beansprucht, wenn er entweder frei ist oder ausweislich seines Markers bereits der eigenen Organisation gehört. Ein fremder Kundenordner wird unter keinen Umständen überschrieben oder umbenannt. Die Zuordnung entscheidet also immer das Metadatum, niemals der Name — der Name ist nur die Optimierung.
|
Sicherheitsregel: Ein Zielname wird nur beansprucht, wenn er frei ist oder ausweislich seines Markers der eigenen Organisation gehört. Ein fremder Kundenordner wird unter keinen Umständen überschrieben. Die Zuordnung entscheidet immer das Metadatum, nie der Name.
|
||||||
|
|
||||||
\subsection{Der resultierende Ablauf}
|
\subsection{Der resultierende Ablauf}
|
||||||
|
|
||||||
Aus diesen Überlegungen ergibt sich ein dreistufiges Verfahren, das in Abbildung~\ref{fig:lookup-flow} dargestellt ist.
|
Aus diesen Überlegungen ergibt sich ein dreistufiges Verfahren (Abbildung~\ref{fig:lookup-flow}).
|
||||||
|
|
||||||
\begin{figure}[H]
|
\begin{figure}[H]
|
||||||
\centering
|
\centering
|
||||||
@@ -37,21 +37,21 @@ Aus diesen Überlegungen ergibt sich ein dreistufiges Verfahren, das in Abbildun
|
|||||||
\end{figure}
|
\end{figure}
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
\item \textbf{Direktzugriff.} Houston bildet den erwarteten Ordnernamen aus dem Firmennamen und ruft die Metadaten des zugehörigen Markers ab. Existiert er und trägt er die richtige Organisations-ID, ist die Auflösung mit einem einzigen Aufruf abgeschlossen. Dies ist der Regelfall.
|
\item \textbf{Direktzugriff.} Houston bildet den erwarteten Ordnernamen aus dem Firmennamen und ruft die Metadaten des Markers ab. Stimmt die Organisations-ID, ist die Auflösung mit einem Aufruf abgeschlossen. Dies ist der Regelfall.
|
||||||
\item \textbf{Kollisionsprüfung.} Schlägt der erste Schritt fehl — weil der Marker fehlt oder eine fremde Organisations-ID trägt — wird derselbe Zugriff mit dem um die Organisations-ID ergänzten Namen wiederholt. Damit ist der Kollisionsfall mit zwei Aufrufen abgedeckt.
|
\item \textbf{Kollisionsprüfung.} Fehlt der Marker oder trägt er eine fremde ID, wird der Zugriff mit dem um die Organisations-ID ergänzten Namen wiederholt — zwei Aufrufe für den Kollisionsfall.
|
||||||
\item \textbf{Rückfallebene.} Erst wenn auch das nicht greift, kommt das ursprüngliche, lineare Verfahren zum Einsatz. Wird dabei ein Ordner mit passender Organisations-ID gefunden, trägt er offenbar einen abweichenden Namen und wird auf den kanonischen Namen umbenannt. Wird kein Ordner gefunden, existiert der Kunde im Speicher noch nicht und die vollständige Struktur wird angelegt.
|
\item \textbf{Rückfallebene.} Erst dann kommt das lineare Verfahren zum Einsatz. Wird ein Ordner mit passender ID gefunden, wird er auf den kanonischen Namen umbenannt. Wird keiner gefunden, wird die vollständige Struktur angelegt.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
\subsection{Selbstheilung}
|
\subsection{Selbstheilung}
|
||||||
|
|
||||||
Die entscheidende Eigenschaft dieses Verfahrens liegt im dritten Schritt. Die Rückfallebene beseitigt nicht nur den unmittelbaren Fehlschlag, sondern auch dessen Ursache: Nach der Umbenennung trägt der Ordner den erwarteten Namen, und der nächste Zugriff derselben Organisation wird wieder über den Direktzugriff abgewickelt.
|
Die entscheidende Eigenschaft liegt im dritten Schritt: Die Rückfallebene beseitigt nicht nur den Fehlschlag, sondern auch dessen Ursache. Nach der Umbenennung trägt der Ordner den erwarteten Namen, und der nächste Zugriff läuft direkt.
|
||||||
|
|
||||||
Der teure Pfad wird damit pro Kunde höchstens einmal durchlaufen. Bestehende Ordner, die vor Einführung des Verfahrens angelegt wurden und beliebige Namen tragen, migrieren sich beim ersten Zugriff des jeweiligen Kunden von selbst. Eine gesonderte Migration ist nicht erforderlich.
|
Der teure Pfad wird pro Kunde höchstens einmal durchlaufen. Bestehende Ordner migrieren sich beim ersten Zugriff von selbst — eine gesonderte Migration entfällt.
|
||||||
|
|
||||||
Ebenso wenig erfordert das Verfahren eine zusätzliche Datenhaltung. Es gibt keinen Cache, der invalidiert werden müsste, kein Feld in einem Fremdsystem und keine Zuordnungsdatei. Der Zustand liegt vollständig im Speicher selbst, und die Anwendung ist zu jedem Zeitpunkt in der Lage, aus einem beliebigen Ausgangszustand den Sollzustand herzustellen.
|
Das Verfahren erfordert keine zusätzliche Datenhaltung: keinen Cache, kein Feld in einem Fremdsystem, keine Zuordnungsdatei. Der Zustand liegt vollständig im Speicher, und die Anwendung kann aus jedem Ausgangszustand den Sollzustand herstellen.
|
||||||
|
|
||||||
\subsection{Bewusst offen gelassener Bereich}
|
\subsection{Bewusst offen gelassener Bereich}
|
||||||
|
|
||||||
Das Verfahren hat einen Preis, der bei der Entscheidung bekannt war: Die Rückfallebene ist nicht mehr nur lesend. Sie benennt Ordner um und legt sie an — und das im Rahmen einer gewöhnlichen Seitenanfrage. Da Houston in mehreren Instanzen betrieben wird, können zwei solche Anfragen gleichzeitig auf denselben Ordner treffen.
|
Die Rückfallebene ist nicht nur lesend: Sie benennt Ordner um und legt sie an — im Rahmen einer gewöhnlichen Seitenanfrage. Da Houston in mehreren Instanzen läuft, können zwei Anfragen gleichzeitig denselben Ordner betreffen.
|
||||||
|
|
||||||
Dieser Umstand wurde während der Umsetzung erkannt, in seinen Auswirkungen analysiert und als eigenes Backlog Item vom laufenden Pull Request abgegrenzt. Die Entscheidung, die Abgrenzung so vorzunehmen, wurde mit dem Team abgestimmt. Abschnitt~\ref{sec:race-conditions} behandelt die konkreten Fehlerszenarien und die erwogenen Gegenmaßnahmen im Detail.
|
Dieser Umstand wurde analysiert und als eigenes Backlog Item vom laufenden Pull Request abgegrenzt. Abschnitt~\ref{sec:race-conditions} behandelt die Szenarien und erwogenen Gegenmaßnahmen.
|
||||||
|
|||||||
@@ -3,52 +3,46 @@
|
|||||||
|
|
||||||
\subsection{Entstehung}
|
\subsection{Entstehung}
|
||||||
|
|
||||||
Der Document Explorer war zum Zeitpunkt seines Merges am 31.~Juli 2026 funktionsfähig, enthielt aber eine Schwäche, die bereits im Code-Review angesprochen worden war. Um den Kundenordner zu einer Organisations-ID zu finden, listete Houston sämtliche Ordner auf der obersten Ebene des Buckets auf, rief für jeden davon die Metadaten ab und verglich die hinterlegte \texttt{efecte-org-id} mit der des angemeldeten Benutzers.
|
Der Document Explorer war zum Zeitpunkt seines Merges am 31.~Juli 2026 funktionsfähig, enthielt aber eine im Code-Review angesprochene Schwäche. Um den Kundenordner zu einer Organisations-ID zu finden, listete Houston sämtliche Ordner auf oberster Ebene auf, rief für jeden die Metadaten ab und verglich die \texttt{efecte-org-id} mit der des Benutzers.
|
||||||
|
|
||||||
Die Ursache liegt in der bereits beschriebenen Eigenschaft von S3: Objekte lassen sich nicht anhand ihrer benutzerdefinierten Metadaten suchen. Die Operation \texttt{ListObjectsV2} liefert Schlüssel, Größe und Änderungszeitpunkt, aber keine benutzerdefinierten Metadaten \autocite{aws-listobjectsv2}. Um an das Marker-Metadatum zu gelangen, ist für jeden Kandidaten ein eigener Aufruf nötig.
|
Die Ursache: S3 bietet keine Suche nach benutzerdefinierten Metadaten. \texttt{ListObjectsV2} liefert Schlüssel, Größe und Änderungszeitpunkt, aber keine benutzerdefinierten Metadaten \autocite{aws-listobjectsv2}. Für jeden Kandidaten ist ein eigener Aufruf nötig.
|
||||||
|
|
||||||
Damit wächst der Aufwand für die Auflösung des Kundenordners linear mit der Anzahl der Kunden — und zwar bei \emph{jedem} Seitenaufruf jedes Benutzers. Bei einer zweistelligen Kundenzahl fällt das kaum auf; bei einigen hundert Kunden bedeutet es einige hundert Netzwerkaufrufe, bevor überhaupt das erste Dokument geladen wird. Das Problem ist also nicht akut, aber strukturell: Es verschlechtert sich mit dem Erfolg des Produkts.
|
Der Aufwand wächst linear mit der Kundenzahl — bei \emph{jedem} Seitenaufruf. Bei einigen hundert Kunden bedeutet das einige hundert Netzwerkaufrufe, bevor das erste Dokument geladen wird. Das Problem ist nicht akut, aber strukturell.
|
||||||
|
|
||||||
Aus dieser Erkenntnis entstand am 28.~Juli ein eigenes Backlog Item. Bemerkenswert ist, dass es bewusst nicht als Umsetzungs-, sondern als \emph{Recherche}-Item angelegt wurde. Das Akzeptanzkriterium lautete nicht „das Problem ist behoben", sondern „es ist sich für einen Lösungsansatz entschieden worden". Diese Zuschnitt-Entscheidung trennt die Frage, welche Lösung die richtige ist, von der Frage, wie sie umzusetzen ist — und macht den Rechercheaufwand als eigenständige Leistung sichtbar, statt ihn in einer Implementierungsaufgabe zu verstecken.
|
Am 28.~Juli entstand daraus ein eigenes Backlog Item, bewusst als \emph{Recherche}-Item. Das Akzeptanzkriterium lautete nicht „das Problem ist behoben", sondern „es ist sich für einen Lösungsansatz entschieden worden". So wird der Rechercheaufwand als eigenständige Leistung sichtbar.
|
||||||
|
|
||||||
\subsection{Untersuchte Lösungsansätze}
|
\subsection{Untersuchte Lösungsansätze}
|
||||||
|
|
||||||
Im Zuge der Recherche wurden sechs Ansätze betrachtet und mit \emph{Timo Walter} sowie den Ansprechpartnern für die Speicherinfrastruktur diskutiert. Tabelle~\ref{tab:lookup-tradeoffs} im Anhang fasst die Bewertung zusammen; im Folgenden werden die Ansätze und ihre jeweiligen Schwächen erläutert.
|
Sechs Ansätze wurden betrachtet und mit \emph{Timo Walter} sowie den Ansprechpartnern für die Speicherinfrastruktur diskutiert. Tabelle~\ref{tab:lookup-tradeoffs} im Anhang fasst die Bewertung zusammen.
|
||||||
|
|
||||||
\subsubsection{In-Memory-Cache}
|
\subsubsection{In-Memory-Cache}
|
||||||
|
|
||||||
Der naheliegendste Ansatz ist, das Ergebnis der Auflösung im Arbeitsspeicher der Anwendung zwischenzuspeichern. Der erste Aufruf bleibt teuer, alle folgenden sind kostenlos.
|
Der naheliegendste Ansatz: das Auflösungsergebnis im Arbeitsspeicher zwischenspeichern. Der erste Aufruf bleibt teuer, alle folgenden sind kostenlos.
|
||||||
|
|
||||||
Der Ansatz behebt das Problem jedoch nur oberflächlich. Houston läuft in mehr als einer Instanz, sodass jede Instanz ihren eigenen Cache aufbauen müsste. Nach jedem Neustart oder jeder Bereitstellung ist der Cache leer, und es entsteht ein Ansturm teurer Auflösungen. Vor allem aber bleibt der zugrunde liegende lineare Aufwand unverändert bestehen — er wird lediglich seltener bezahlt. Ein Cache ist eine Optimierung, keine Lösung.
|
Houston läuft jedoch in mehreren Instanzen, sodass jede ihren Cache aufbauen müsste. Nach jedem Neustart ist der Cache leer, und es entsteht ein Ansturm teurer Auflösungen. Der lineare Aufwand bleibt unverändert — er wird lediglich seltener bezahlt.
|
||||||
|
|
||||||
\subsubsection{Speicherung im Anmeldetoken}
|
\subsubsection{Speicherung im Anmeldetoken}
|
||||||
|
|
||||||
Alternativ ließe sich der Ordnername bei der Anmeldung ermitteln und als zusätzlicher Claim im Token ablegen. Gegenüber dem Cache verschiebt das den Aufwand von jedem Seitenaufruf auf jede Anmeldung.
|
Der Ordnername könnte bei der Anmeldung ermittelt und als Claim im Token abgelegt werden. Der lineare Aufwand bliebe erhalten. Hinzu kommt: Ein Token ist über seine Laufzeit unveränderlich. Wird der Ordner umbenannt, zeigt der Claim auf einen nicht mehr existierenden Ordner, bis das Token erneuert wird.
|
||||||
|
|
||||||
Auch hier bleibt der lineare Aufwand erhalten. Hinzu kommt ein Konsistenzproblem: Ein Token ist über seine Laufzeit unveränderlich. Wird der Ordner im Speicher umbenannt, während ein Benutzer angemeldet ist, zeigt der Claim auf einen nicht mehr existierenden Ordner, bis das Token erneuert wird.
|
|
||||||
|
|
||||||
\subsubsection{Speicherung als Feld in Efecte}
|
\subsubsection{Speicherung als Feld in Efecte}
|
||||||
|
|
||||||
Der Ordnername könnte auch als zusätzliches Feld an der Organisation in Efecte gepflegt werden. Houston würde ihn dann gemeinsam mit den übrigen Organisationsdaten laden.
|
Der Ordnername könnte als Feld an der Organisation in Efecte gepflegt werden. Das löst das Problem technisch, führt aber eine Abhängigkeit ein: Die Zuordnung läge an zwei Stellen — im Efecte-Feld und im Marker-Metadatum — die auseinanderlaufen können. Zudem müsste das Feld für jeden Bestandskunden nachgepflegt werden.
|
||||||
|
|
||||||
Dieser Ansatz löst das Problem technisch, führt aber eine neue Abhängigkeit ein und verlagert die Pflege in ein weiteres System. Die Zuordnung zwischen Kunde und Ordner läge damit an zwei Stellen — im Efecte-Feld und im Marker-Metadatum — die auseinanderlaufen können. Zudem müsste das Feld für jeden Bestandskunden nachgepflegt werden.
|
|
||||||
|
|
||||||
\subsubsection{S3 Select}
|
\subsubsection{S3 Select}
|
||||||
|
|
||||||
Die Operation \texttt{SelectObjectContent} erlaubt es, den \emph{Inhalt} eines Objekts serverseitig per SQL-ähnlicher Abfrage zu filtern \autocite{aws-s3-select}. Denkbar wäre gewesen, eine Zuordnungstabelle als Datei im Bucket abzulegen und den passenden Eintrag serverseitig herauszufiltern.
|
\texttt{SelectObjectContent} filtert den \emph{Inhalt} eines Objekts serverseitig per SQL-ähnlicher Abfrage \autocite{aws-s3-select}. Denkbar wäre eine Zuordnungstabelle als Datei im Bucket, aus der der passende Eintrag gefiltert wird.
|
||||||
|
|
||||||
Die Freischaltung wurde beantragt und am 7.~August durch den Betreiber bestätigt (siehe Abschnitt~\ref{sec:infrastructure}). Bei genauerer Betrachtung erwies sich der Ansatz jedoch als unpassend: S3 Select filtert Inhalte einzelner Objekte, nicht Metadaten mehrerer Objekte. Eine Zuordnungsdatei wäre eine zusätzliche, manuell oder programmatisch zu pflegende Struktur — mit demselben Konsistenzproblem wie beim Efecte-Feld, nur ohne dessen Werkzeugunterstützung.
|
Die Freischaltung wurde beantragt und am 7.~August bestätigt (siehe Abschnitt~\ref{sec:infrastructure}). S3 Select filtert jedoch Inhalte einzelner Objekte, nicht Metadaten mehrerer Objekte. Eine Zuordnungsdatei wäre eine zusätzlich zu pflegende Struktur mit demselben Konsistenzproblem wie beim Efecte-Feld, nur ohne dessen Werkzeugunterstützung.
|
||||||
|
|
||||||
\subsubsection{StorageGRID Search Integration}
|
\subsubsection{StorageGRID Search Integration}
|
||||||
|
|
||||||
Der Search Integration Service spiegelt Objektmetadaten in einen Elasticsearch-Index und ermöglicht damit genau das, was S3 selbst nicht bietet: eine echte Suche über Metadaten \autocite{storagegrid-search-integration}. Fachlich wäre dies die sauberste Lösung gewesen, da sie das Problem an der Wurzel behebt, ohne eine zweite Datenhaltung einzuführen.
|
Der Search Integration Service spiegelt Objektmetadaten in einen Elasticsearch-Index und ermöglicht eine echte Metadatensuche \autocite{storagegrid-search-integration}. Fachlich die sauberste Lösung, da sie das Problem an der Wurzel behebt, ohne eine zweite Datenhaltung einzuführen. Die Anfrage ergab am 17.~August, dass die Funktion derzeit nicht angeboten wird; der Ansatz wurde in den Ausblick übernommen (siehe Abschnitt~\ref{sec:outlook}).
|
||||||
|
|
||||||
Die Anfrage über den Betreiber ergab am 17.~August, dass die Funktion derzeit nicht angeboten wird. Damit schied der Ansatz aus. Er wurde in den Ausblick übernommen (siehe Abschnitt~\ref{sec:outlook}).
|
|
||||||
|
|
||||||
\subsubsection{Organisations-ID im Ordnernamen}
|
\subsubsection{Organisations-ID im Ordnernamen}
|
||||||
|
|
||||||
Der technisch einfachste Ansatz wäre gewesen, die Organisations-ID zum Bestandteil des Ordnernamens zu machen — etwa als Präfix \texttt{42\_Beispielkunde GmbH}. Der Lookup reduzierte sich dann auf eine einzige Präfixabfrage.
|
Der einfachste Ansatz: die Organisations-ID zum Bestandteil des Ordnernamens machen — etwa \texttt{42\_Beispielkunde GmbH}. Der Lookup reduzierte sich auf eine Präfixabfrage.
|
||||||
|
|
||||||
Dieser Ansatz scheiterte an einer fachlichen Anforderung. In der Abstimmung mit \emph{Ralf Schulte} wurde deutlich, dass die Kundenbetreuer die Ordner in Filestash nach Kundennamen sortiert vorfinden müssen, um damit arbeiten zu können. Ein vorangestellter Zahlenschlüssel zerstört diese Sortierung. Der Konflikt ist damit klar benannt: Houston autorisiert über die Organisations-ID, die Betreuer arbeiten über den Kundennamen, und der Speicher bietet keinen Mechanismus, zwischen beiden zu vermitteln.
|
In der Abstimmung mit \emph{Ralf Schulte} wurde deutlich, dass die Kundenbetreuer die Ordner in Filestash nach Kundennamen sortiert vorfinden müssen. Ein vorangestellter Zahlenschlüssel zerstört diese Sortierung. Der Konflikt: Houston autorisiert über die Organisations-ID, die Betreuer arbeiten über den Kundennamen, und der Speicher vermittelt nicht zwischen beiden.
|
||||||
|
|
||||||
Die entscheidende Beobachtung war jedoch, dass die Anforderung eine Sortierung nach Kundennamen verlangt — nicht zwingend, dass der Ordnername \emph{ausschließlich} aus dem Kundennamen besteht. Diese Unterscheidung eröffnete den Weg zu der in Abschnitt~\ref{sec:lookup-decision} beschriebenen Lösung.
|
Die entscheidende Beobachtung war, dass die Anforderung eine Sortierung nach Kundennamen verlangt — nicht zwingend, dass der Ordnername \emph{ausschließlich} aus dem Kundennamen besteht. Diese Unterscheidung eröffnete den Weg zur in Abschnitt~\ref{sec:lookup-decision} beschriebenen Lösung.
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
\section{Ablagekonzept im S3-Speicher}
|
\section{Ablagekonzept im S3-Speicher}
|
||||||
\label{sec:s3-layout}
|
\label{sec:s3-layout}
|
||||||
|
|
||||||
Das Ablagekonzept bildet die Grundlage für alle weiteren Entwurfsentscheidungen. Es muss zwei Aufgaben gleichzeitig erfüllen: Es muss festlegen, welche Dokumente zu welchem Kunden gehören, und es muss den fachlichen Typ eines Dokuments abbilden. Beides geschieht ausschließlich über die Struktur im Speicher, ohne eine zusätzliche Datenbank.
|
Das Ablagekonzept muss zwei Aufgaben erfüllen: die Zuordnung von Dokumenten zu Kunden und die Abbildung des fachlichen Dokumententyps. Beides geschieht ausschließlich über die Struktur im Speicher, ohne eine zusätzliche Datenbank.
|
||||||
|
|
||||||
\subsection{Präfixe statt Ordner}
|
\subsection{Präfixe statt Ordner}
|
||||||
|
|
||||||
S3 kennt technisch keine Ordner. Jedes Objekt wird über einen Schlüssel adressiert, der als Zeichenkette in einem flachen Namensraum liegt. Die scheinbare Hierarchie entsteht erst beim Auflisten: Übergibt man der Operation \texttt{ListObjectsV2} ein Präfix und ein Trennzeichen, liefert der Dienst nur die Objekte unterhalb dieses Präfixes zurück sowie die gemeinsamen Teilpräfixe der nächsten Ebene \autocite{aws-listobjectsv2}. Ein Schlüssel wie
|
S3 kennt technisch keine Ordner. Jedes Objekt wird über einen Schlüssel in einem flachen Namensraum adressiert. Die scheinbare Hierarchie entsteht beim Auflisten: \texttt{ListObjectsV2} liefert mit Präfix und Trennzeichen nur die Objekte unterhalb eines Präfixes sowie die gemeinsamen Teilpräfixe der nächsten Ebene \autocite{aws-listobjectsv2}. Ein Schlüssel wie
|
||||||
|
|
||||||
\begin{quote}
|
\begin{quote}
|
||||||
\texttt{Beispielkunde GmbH/Service-Protokoll/Protokoll-2026-07.pdf}
|
\texttt{Beispielkunde GmbH/Service-Protokoll/Protokoll-2026-07.pdf}
|
||||||
\end{quote}
|
\end{quote}
|
||||||
|
|
||||||
wird in einem Dateibrowser wie Filestash als zweistufige Ordnerhierarchie dargestellt, ist im Speicher jedoch nur eine einzelne Zeichenkette. Diese Eigenschaft ist für das Konzept vorteilhaft, weil sie das Auflisten eines Kundenordners auf eine einzige Präfixabfrage reduziert. Sie hat aber auch eine Konsequenz, die im weiteren Verlauf noch bedeutsam wird: Ein „Ordner" existiert erst dann, wenn mindestens ein Objekt mit dem entsprechenden Präfix vorhanden ist. Ein leerer Ordner lässt sich nur simulieren, indem ein Platzhalterobjekt angelegt wird, dessen Schlüssel auf das Trennzeichen endet.
|
erscheint in Filestash als zweistufige Ordnerhierarchie, ist im Speicher aber nur eine Zeichenkette. Ein „Ordner" existiert erst, wenn mindestens ein Objekt mit dem entsprechenden Präfix vorhanden ist; ein leerer Ordner lässt sich nur durch ein Platzhalterobjekt simulieren, dessen Schlüssel auf das Trennzeichen endet.
|
||||||
|
|
||||||
Abbildung~\ref{fig:s3-layout} zeigt die resultierende Struktur.
|
Abbildung~\ref{fig:s3-layout} zeigt die resultierende Struktur.
|
||||||
|
|
||||||
@@ -24,36 +24,36 @@ Abbildung~\ref{fig:s3-layout} zeigt die resultierende Struktur.
|
|||||||
|
|
||||||
\subsection{Kundenzuordnung über ein Marker-Objekt}
|
\subsection{Kundenzuordnung über ein Marker-Objekt}
|
||||||
|
|
||||||
Auf der obersten Ebene des Buckets liegt für jeden Kunden genau ein Ordner. Um diesen Ordner einer Organisation zuzuordnen, wird an dem Platzhalterobjekt, das den Ordner repräsentiert, ein benutzerdefiniertes Metadatum \texttt{efecte-org-id} hinterlegt. Dieses Objekt wird im Folgenden als \emph{Marker} bezeichnet.
|
Auf der obersten Ebene des Buckets liegt für jeden Kunden ein Ordner. Dessen Platzhalterobjekt trägt ein benutzerdefiniertes Metadatum \texttt{efecte-org-id} zur Zuordnung an eine Organisation und wird im Folgenden als \emph{Marker} bezeichnet.
|
||||||
|
|
||||||
Der Marker erfüllt zwei Zwecke gleichzeitig. Erstens sorgt er dafür, dass der Kundenordner auch dann existiert, wenn noch kein einziges Dokument abgelegt wurde — andernfalls wäre ein frisch angelegter Kunde im Speicher unsichtbar. Zweitens trägt er die Information, welcher Organisation der Ordner gehört. Damit ist die Zuordnung an genau einer Stelle hinterlegt und muss nicht an jedem einzelnen Dokument wiederholt werden.
|
Er sorgt dafür, dass der Kundenordner auch ohne Dokumente existiert, und trägt die Organisationszugehörigkeit an genau einer Stelle.
|
||||||
|
|
||||||
Die Entscheidung, die Organisations-ID als Metadatum und nicht als Bestandteil des Ordnernamens zu führen, wurde in der Feature-Analyse getroffen (siehe Abschnitt~\ref{sec:feature-analysis}). Sie ist fachlich motiviert: Die Ordner werden von den Kundenbetreuern über Filestash gepflegt, und ein Ordnername wie \texttt{42} oder \texttt{Beispielkunde GmbH (42)} wäre dort schwer zu handhaben. Der lesbare Firmenname bleibt daher der Ordnername, die technische Zuordnung wandert in die Metadaten.
|
Die Organisations-ID wird als Metadatum statt als Bestandteil des Ordnernamens geführt (siehe Abschnitt~\ref{sec:feature-analysis}). Die Ordner werden von Kundenbetreuern über Filestash gepflegt; ein Name wie \texttt{42} wäre dort schwer handhabbar. Der lesbare Firmenname bleibt daher der Ordnername, die technische Zuordnung wandert in die Metadaten.
|
||||||
|
|
||||||
Genau diese Entscheidung erzeugt allerdings das zentrale technische Problem des Projekts, das in Abschnitt~\ref{sec:lookup-research} behandelt wird: Houston kennt aus dem Authentifizierungstoken die Organisations-ID, muss daraus aber den Ordnernamen ermitteln — und S3 bietet keine Möglichkeit, Objekte nach Metadaten zu durchsuchen.
|
Genau diese Entscheidung erzeugt das zentrale technische Problem des Projekts (Abschnitt~\ref{sec:lookup-research}): Houston kennt die Organisations-ID, muss daraus den Ordnernamen ermitteln — und S3 bietet keine Suche nach Metadaten.
|
||||||
|
|
||||||
\subsection{Typisierung über Unterordner}
|
\subsection{Typisierung über Unterordner}
|
||||||
|
|
||||||
Innerhalb des Kundenordners liegt für jeden der sieben fachlichen Dokumententypen ein Unterordner. Der Typ eines Dokuments ergibt sich daraus, in welchem Unterordner es abgelegt ist. Dokumente, die direkt im Kundenordner liegen, gelten als untypisiert.
|
Innerhalb des Kundenordners liegt für jeden der sieben Dokumententypen ein Unterordner. Der Typ ergibt sich aus dem Unterordner; direkt im Kundenordner liegende Dokumente gelten als untypisiert.
|
||||||
|
|
||||||
Auch diese Festlegung stammt aus der Feature-Analyse. Die Alternative wäre gewesen, den Typ als Metadatum an jeder einzelnen Datei zu hinterlegen. Der Vergleich beider Ansätze fällt eindeutig aus:
|
Die Alternative — den Typ als Metadatum an jeder Datei zu hinterlegen — wurde verglichen:
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \textbf{Pflegeaufwand:} Ein Metadatum müsste bei jedem Upload manuell gesetzt werden. Filestash bietet hierfür keine komfortable Unterstützung. Das Ablegen in einem Ordner ist dagegen die natürliche Bedienhandlung.
|
\item \textbf{Pflegeaufwand:} Ein Metadatum müsste bei jedem Upload manuell gesetzt werden; Filestash bietet hierfür keine komfortable Unterstützung. Das Ablegen in einem Ordner ist die natürliche Bedienhandlung.
|
||||||
\item \textbf{Abfragbarkeit:} Der Typ ist als Präfixbestandteil unmittelbar aus dem Schlüssel ablesbar. Beim Auflisten fällt er ohne Zusatzkosten mit an. Ein Metadatum müsste dagegen für jedes Objekt einzeln abgerufen werden, da \texttt{ListObjectsV2} benutzerdefinierte Metadaten nicht mitliefert. Bei $n$ Dokumenten wären das $n$ zusätzliche Aufrufe.
|
\item \textbf{Abfragbarkeit:} Der Typ ist als Präfixbestandteil unmittelbar aus dem Schlüssel ablesbar. Ein Metadatum müsste dagegen für jedes Objekt einzeln abgerufen werden, da \texttt{ListObjectsV2} benutzerdefinierte Metadaten nicht mitliefert — bei $n$ Dokumenten also $n$ zusätzliche Aufrufe.
|
||||||
\item \textbf{Filterung:} Eine Filterung nach Typ reduziert sich auf eine Präfixabfrage und ist damit serverseitig umsetzbar.
|
\item \textbf{Filterung:} Eine Filterung nach Typ reduziert sich auf eine Präfixabfrage und ist damit serverseitig umsetzbar.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
Dem stehen zwei Nachteile gegenüber. Zum einen kann ein Dokument nur genau einen Typ haben, da es nur an einer Stelle liegen kann; eine Mehrfachzuordnung ist ausgeschlossen. Zum anderen erfordert die Typisierung, dass die Ordner überhaupt existieren — was die automatische Anlage der Ordnerstruktur zu einer eigenen Anforderung macht (FA-13). Beide Einschränkungen wurden bewusst in Kauf genommen.
|
Dem stehen zwei Nachteile gegenüber: Ein Dokument kann nur einen Typ haben, und die Ordner müssen existieren — die automatische Anlage wird damit zu einer eigenen Anforderung (FA-13). Beide Einschränkungen wurden bewusst in Kauf genommen.
|
||||||
|
|
||||||
\subsection{Sichtbarkeitsregeln}
|
\subsection{Sichtbarkeitsregeln}
|
||||||
|
|
||||||
Für die Darstellung gelten drei Regeln, die sich aus der Feature-Beschreibung ergeben:
|
Für die Darstellung gelten drei Regeln:
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
\item \textbf{Ordner werden nicht als Einträge angezeigt.} Die Liste zeigt ausschließlich Dokumente; die Ordnerstruktur wird über Typ-Icons und Filter abgebildet, nicht über eine navigierbare Hierarchie. Der Benutzer sieht also eine flache Liste aller seiner Dokumente.
|
\item \textbf{Ordner werden nicht als Einträge angezeigt.} Die Liste zeigt ausschließlich Dokumente; die Ordnerstruktur wird über Typ-Icons und Filter abgebildet. Der Benutzer sieht eine flache Liste aller seiner Dokumente.
|
||||||
\item \textbf{Leere Typordner erscheinen nicht.} Ein Kunde, für den noch keine Monitoring-Reports abgelegt wurden, sieht diesen Typ nicht als leere Kategorie.
|
\item \textbf{Leere Typordner erscheinen nicht.}
|
||||||
\item \textbf{Ordner ohne gültiges Marker-Metadatum werden ignoriert.} Legt jemand versehentlich einen Ordner auf oberster Ebene an, ohne eine Organisations-ID zu hinterlegen, bleibt dieser für alle Kunden unsichtbar. Der Fall wird protokolliert, führt aber nicht zu einem Fehler und niemals zu einem Zugriff.
|
\item \textbf{Ordner ohne gültiges Marker-Metadatum werden ignoriert.} Fehlt die Organisations-ID, bleibt der Ordner unsichtbar. Der Fall wird protokolliert, führt aber nicht zu einem Fehler.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
Die dritte Regel ist eine Sicherheitsmaßnahme: Sie stellt sicher, dass ein Ordner nur dann für einen Kunden sichtbar wird, wenn seine Zugehörigkeit ausdrücklich hinterlegt ist. Ein fehlendes Metadatum führt zum Ausschluss, nicht zu einer Vermutung.
|
Die dritte Regel ist eine Sicherheitsmaßnahme: Ein fehlendes Metadatum führt zum Ausschluss, nicht zu einer Vermutung.
|
||||||
|
|||||||
@@ -3,17 +3,17 @@
|
|||||||
|
|
||||||
\subsection{Zuarbeit über einen Clickdummy}
|
\subsection{Zuarbeit über einen Clickdummy}
|
||||||
|
|
||||||
Das Oberflächenkonzept wurde nicht als Mockup in einem Entwurfswerkzeug erstellt, sondern von \emph{Hanna Ebner} als lauffähiger Clickdummy in einem eigenen Branch des Houston-Repositories umgesetzt und am 22.~Juli 2026 am Feature verlinkt.
|
Das Oberflächenkonzept wurde von \emph{Hanna Ebner} als lauffähiger Clickdummy in einem eigenen Branch des Houston-Repositories umgesetzt und am 22.~Juli 2026 am Feature verlinkt.
|
||||||
|
|
||||||
Diese Form der Zuarbeit hat gegenüber einem Bildentwurf spürbare Vorteile. Der Clickdummy verwendet die bestehenden Komponenten und Stile der Anwendung, wodurch das Ergebnis von vornherein zum übrigen Portal passt. Fragen zu Abständen, Schriftgrößen oder Farben stellen sich gar nicht erst, weil sie durch das vorhandene Stylesheet beantwortet werden. Zudem ist das Ergebnis unmittelbar bedienbar: Interaktionen wie das Ein- und Ausklappen von Filtern lassen sich ausprobieren, statt sie aus einer statischen Abbildung erschließen zu müssen. Für die Umsetzung bedeutete das, dass der Clickdummy als Referenz für das Markup dienen konnte und in mehreren Product Backlog Items ausdrücklich als solche benannt wurde.
|
Der Clickdummy verwendet die bestehenden Komponenten und Stile der Anwendung, wodurch Fragen zu Abständen, Schriftgrößen oder Farben durch das vorhandene Stylesheet beantwortet werden. Interaktionen wie das Ein- und Ausklappen von Filtern lassen sich ausprobieren statt aus einer statischen Abbildung erschlossen werden. Der Clickdummy diente als Referenz für das Markup und wurde in mehreren Product Backlog Items als solche benannt.
|
||||||
|
|
||||||
\subsection{Flache Liste statt navigierbarer Hierarchie}
|
\subsection{Flache Liste statt navigierbarer Hierarchie}
|
||||||
|
|
||||||
Die auffälligste Entwurfsentscheidung ist, dass der Dokumentenbereich trotz seiner Bezeichnung als „Document Explorer" keine navigierbare Ordnerhierarchie darstellt. Der Benutzer sieht eine flache Liste aller seiner Dokumente; die Typzugehörigkeit wird über ein Icon und über Filter ausgedrückt, nicht über ein Hineinnavigieren in Ordner.
|
Der Dokumentenbereich stellt trotz seiner Bezeichnung als „Document Explorer" keine navigierbare Ordnerhierarchie dar. Der Benutzer sieht eine flache Liste aller Dokumente; die Typzugehörigkeit wird über Icons und Filter ausgedrückt.
|
||||||
|
|
||||||
Der Grund liegt im erwarteten Nutzungsverhalten. Ein Kunde sucht in aller Regel ein bestimmtes Dokument — den letzten Monitoring-Report oder einen konkreten Vertrag. Bei einer Hierarchie müsste er zunächst wissen, in welcher Kategorie es abgelegt ist, und sich dorthin durchklicken. Die flache Liste erlaubt es dagegen, unmittelbar zu suchen oder zu filtern. Da die Hierarchie ohnehin nur zwei Ebenen tief ist und die zweite Ebene aus sieben festen Kategorien besteht, wäre der Navigationsaufwand in keinem Verhältnis zum Nutzen gestanden.
|
Ein Kunde sucht in der Regel ein bestimmtes Dokument. Bei einer Hierarchie müsste er zunächst die Kategorie kennen und sich dorthin durchklicken. Die flache Liste erlaubt unmittelbares Suchen und Filtern. Da die Hierarchie nur zwei Ebenen mit sieben festen Kategorien umfasst, stünde der Navigationsaufwand in keinem Verhältnis zum Nutzen.
|
||||||
|
|
||||||
Diese Entscheidung schlug sich auch in der Formulierung der Anforderungen nieder: Die ursprüngliche Beschreibung sprach von Dokumenten als Kacheln, wurde im Verlauf jedoch auf eine Zeilendarstellung in einer Liste geändert. Eine Zeile bietet Platz für Icon, Name, Auswahlbox und Aktionsschaltflächen und lässt sich später um weitere Spalten erweitern.
|
Die ursprüngliche Beschreibung sah Dokumentkacheln vor, wurde jedoch auf Zeilendarstellung geändert. Eine Zeile bietet Platz für Icon, Name, Auswahlbox und Aktionsschaltflächen und lässt sich um Spalten erweitern.
|
||||||
|
|
||||||
\subsection{Aufbau der Seite}
|
\subsection{Aufbau der Seite}
|
||||||
|
|
||||||
@@ -23,15 +23,15 @@ Die Seite gliedert sich von oben nach unten in vier Bereiche:
|
|||||||
\item Eine \textbf{Suchleiste} am oberen Rand, über die nach dem Dokumentnamen gesucht wird.
|
\item Eine \textbf{Suchleiste} am oberen Rand, über die nach dem Dokumentnamen gesucht wird.
|
||||||
\item Darunter eine Reihe von \textbf{Filterelementen}, je eines pro Dokumententyp, mit denen sich Typen ein- und ausblenden lassen.
|
\item Darunter eine Reihe von \textbf{Filterelementen}, je eines pro Dokumententyp, mit denen sich Typen ein- und ausblenden lassen.
|
||||||
\item Die \textbf{Dokumentenliste} als Tabelle. Jede Zeile enthält das Typ-Icon, den Dokumentnamen sowie die Aktionen Herunterladen, Teilen und — bei PDF-Dateien — Vorschau. Eine Auswahlbox am Zeilenanfang dient der Mehrfachauswahl für den ZIP-Download.
|
\item Die \textbf{Dokumentenliste} als Tabelle. Jede Zeile enthält das Typ-Icon, den Dokumentnamen sowie die Aktionen Herunterladen, Teilen und — bei PDF-Dateien — Vorschau. Eine Auswahlbox am Zeilenanfang dient der Mehrfachauswahl für den ZIP-Download.
|
||||||
\item Am unteren Rand die \textbf{Blätterelemente} zum Wechsel zwischen den Seiten sowie die Auswahl der Seitengröße.
|
\item Am unteren Rand die \textbf{Blätterelemente} zum Seitenwechsel sowie die Auswahl der Seitengröße.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
Die Tabellenstruktur wurde bewusst erweiterbar angelegt. In der Feature-Beschreibung ist ausdrücklich festgehalten, dass weitere Spalten — etwa für eine Vorschau oder zusätzliche Auswahlmöglichkeiten — ergänzt werden können, ohne den Aufbau zu verändern.
|
Die Tabellenstruktur ist erweiterbar: Weitere Spalten können ergänzt werden, ohne den Aufbau zu verändern.
|
||||||
|
|
||||||
\subsection{Konsistenz zur bestehenden Anwendung}
|
\subsection{Konsistenz zur bestehenden Anwendung}
|
||||||
|
|
||||||
Eine durchgängige Vorgabe war, dass sich der Dokumentenbereich wie die übrigen Houston-Seiten bedienen lassen soll (NFA-4). Das betrifft insbesondere die Paginierung, die wie an anderer Stelle eine benutzerseitig wählbare Seitengröße anbietet, und die Suche, die dem gewohnten Verhalten folgen soll.
|
Eine durchgängige Vorgabe war, dass sich der Dokumentenbereich wie die übrigen Houston-Seiten bedienen lässt (NFA-4).
|
||||||
|
|
||||||
Wie genau diese Vorgabe zu verstehen ist, zeigte sich erst im Abnahmetest: Die zunächst umgesetzte Suchleiste blendete nach einer Eingabe eine Schaltfläche zum Leeren des Feldes ein — eine für sich genommen sinnvolle Funktion, die es auf den übrigen Seiten jedoch nicht gibt. Der Unterschied wurde als Fehler gemeldet (siehe Abschnitt~\ref{sec:acceptance-testing}). Das Beispiel verdeutlicht, dass eine Konsistenzanforderung sich nicht vollständig aus einer Beschreibung ableiten lässt, sondern letztlich am Vergleich mit dem Bestand geprüft werden muss.
|
Wie genau diese Vorgabe zu verstehen ist, zeigte sich erst im Abnahmetest: Die Suchleiste blendete nach einer Eingabe eine Schaltfläche zum Leeren ein — eine sinnvolle Funktion, die es auf den übrigen Seiten nicht gibt. Der Unterschied wurde als Fehler gemeldet (siehe Abschnitt~\ref{sec:acceptance-testing}). Das Beispiel verdeutlicht, dass eine Konsistenzanforderung am Vergleich mit dem Bestand geprüft werden muss.
|
||||||
|
|
||||||
Ein zweiter Punkt betrifft das Zusammenspiel von Freigabelinks und Paginierung. Ein Link, der auf ein bestimmtes Dokument verweist, muss auch dann funktionieren, wenn dieses Dokument nicht auf der ersten Seite liegt. Das Konzept sieht deshalb vor, dass der Freigabelink nicht nur das Dokument benennt, sondern beim Weiterleiten auch die passenden Abfrageparameter setzt, sodass die richtige Seite geladen und an die entsprechende Stelle gesprungen wird.
|
Ein Freigabelink, der auf ein bestimmtes Dokument verweist, muss auch funktionieren, wenn dieses nicht auf der ersten Seite liegt. Der Link setzt daher beim Weiterleiten die passenden Abfrageparameter, sodass die richtige Seite geladen und an die entsprechende Stelle gesprungen wird.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
\subsection{Schichtung}
|
\subsection{Schichtung}
|
||||||
|
|
||||||
Das Dokumentenmodul folgt der in Houston bereits etablierten Schichtung und führt keine neuen Strukturmuster ein. Abbildung~\ref{fig:module-components} zeigt die Komponenten und ihre Beziehungen.
|
Das Dokumentenmodul folgt der in Houston etablierten Schichtung. Abbildung~\ref{fig:module-components} zeigt die Komponenten.
|
||||||
|
|
||||||
\begin{figure}[H]
|
\begin{figure}[H]
|
||||||
\centering
|
\centering
|
||||||
@@ -12,30 +12,28 @@ Das Dokumentenmodul folgt der in Houston bereits etablierten Schichtung und füh
|
|||||||
\label{fig:module-components}
|
\label{fig:module-components}
|
||||||
\end{figure}
|
\end{figure}
|
||||||
|
|
||||||
Die oberste Schicht bilden drei Razor Pages. \texttt{Documents} stellt die eigentliche Liste samt Suche, Filtern und Blätterelementen dar. \texttt{Document/Download} nimmt Downloadanforderungen entgegen, sowohl für einzelne Dokumente als auch für ZIP-Archive. \texttt{Document/Share} liefert die Vorschauseite für Freigabelinks. Die Aufteilung auf getrennte Seiten statt auf mehrere Handler einer einzigen Seite folgt daraus, dass Download und Freigabe eigene Routen mit eigenen Antwortformaten benötigen.
|
Die oberste Schicht bilden drei Razor Pages: \texttt{Documents} für Liste, Suche, Filter und Blätterelemente, \texttt{Document/Download} für Downloadanforderungen (einzeln und als ZIP) und \texttt{Document/Share} für Freigabelinks. Die Aufteilung folgt daraus, dass Download und Freigabe eigene Routen mit eigenen Antwortformaten benötigen.
|
||||||
|
|
||||||
Darunter liegt der \texttt{DocumentsService} als fachliche Schicht. Er kennt die Regeln des Ablagekonzepts: wie ein Kundenordner aufgelöst wird, welche Objekte als Dokumente gelten, wie der Typ aus dem Pfad abgeleitet wird und welche Einträge auszublenden sind. Der \texttt{S3DocumentsClient} kapselt darunter den technischen Zugriff auf den Speicher und ist die einzige Stelle, an der \texttt{IAmazonS3} unmittelbar verwendet wird.
|
Darunter liegt der \texttt{DocumentsService} als fachliche Schicht mit den Regeln des Ablagekonzepts: Auflösung des Kundenordners, Typableitung aus dem Pfad, Ausblendung technischer Einträge. Der \texttt{S3DocumentsClient} kapselt den technischen Speicherzugriff und ist die einzige Stelle, an der \texttt{IAmazonS3} unmittelbar verwendet wird.
|
||||||
|
|
||||||
\subsection{Eigene Typen statt Zeichenketten}
|
\subsection{Eigene Typen statt Zeichenketten}
|
||||||
|
|
||||||
Eine Entwurfsentscheidung, die sich erst im Verlauf der Umsetzung herausbildete, betrifft den Umgang mit Pfaden. In der ersten Fassung wurden Dokumentschlüssel als gewöhnliche Zeichenketten durch die Schichten gereicht. Im Review des Downloads führte das zu wiederholten Rückfragen zur Pfadvalidierung — und zwar deshalb, weil einer Zeichenkette nicht anzusehen ist, ob sie bereits geprüft wurde.
|
Eine Entwurfsentscheidung, die sich im Verlauf herausbildete, betrifft den Umgang mit Pfaden. Anfangs wurden Dokumentschlüssel als Zeichenketten durch die Schichten gereicht. Im Review des Downloads führte das zu wiederholten Rückfragen zur Pfadvalidierung, weil einer Zeichenkette nicht anzusehen ist, ob sie bereits geprüft wurde.
|
||||||
|
|
||||||
Daraufhin wurden eigene Typen eingeführt, die diese Unterscheidung im Typsystem sichtbar machen. Ein \emph{Schlüssel} bezeichnet den vollständigen, bereits gegen das Kundenpräfix validierten Pfad im Speicher; ein \emph{relativer Pfad} bezeichnet den Anteil unterhalb des Kundenordners, wie er in Freigabelinks und in der ZIP-Struktur auftritt. Beide Typen können nur über Konstruktionswege entstehen, die die jeweilige Prüfung durchführen.
|
Daraufhin wurden eigene Typen eingeführt: Ein \emph{Schlüssel} bezeichnet den vollständigen, validierten Pfad; ein \emph{relativer Pfad} den Anteil unterhalb des Kundenordners. Beide können nur über Konstruktionswege entstehen, die die jeweilige Prüfung durchführen. Eine vergessene Prüfung führt zu einem Übersetzungsfehler statt zu einer Sicherheitslücke.
|
||||||
|
|
||||||
Der Nutzen liegt darin, dass eine vergessene Prüfung nicht mehr zu einer Sicherheitslücke, sondern zu einem Übersetzungsfehler führt. Der zugehörige Pull Request durchlief 23 Iterationen und bestand zu einem erheblichen Teil aus genau diesem Refactoring — ein Aufwand, der sich in den folgenden Anforderungen mehrfach auszahlte, weil ZIP-Download, PDF-Vorschau und Freigabelinks dieselben Typen wiederverwenden konnten.
|
Der zugehörige Pull Request durchlief 23 Iterationen und bestand zu einem erheblichen Teil aus diesem Refactoring — ein Aufwand, der sich auszahlte, weil ZIP-Download, PDF-Vorschau und Freigabelinks dieselben Typen wiederverwenden konnten. Nach demselben Muster entstanden \texttt{DocumentType}, \texttt{DocumentName} und \texttt{OrgSlug}.
|
||||||
|
|
||||||
Nach demselben Muster entstanden \texttt{DocumentType} für die Typisierung, \texttt{DocumentName} für die Behandlung von Anzeigenamen und Dateiendungen sowie \texttt{OrgSlug} für die Normalisierung des Firmennamens.
|
|
||||||
|
|
||||||
\subsection{Zentrale Autorisierung}
|
\subsection{Zentrale Autorisierung}
|
||||||
|
|
||||||
Die erste Fassung des Document Explorers prüfte die Berechtigung im PageModel selbst. Im Review wies \emph{Hanna Ebner} darauf hin, dass die Autorisierung in Houston zentral in der Anwendungskonfiguration eingerichtet wird und eine zusätzliche Prüfung an der Seite deshalb überflüssig ist.
|
Die erste Fassung des Document Explorers prüfte die Berechtigung im PageModel. Im Review wies \emph{Hanna Ebner} darauf hin, dass die Autorisierung in Houston zentral in der Anwendungskonfiguration erfolgt und eine zusätzliche Prüfung an der Seite überflüssig ist.
|
||||||
|
|
||||||
Der Einwand betrifft mehr als nur doppelten Code. Eine an der Seite hinterlegte Prüfung ist leicht zu übersehen, wenn später eine weitere Seite hinzukommt — genau das wäre bei \texttt{Download} und \texttt{Share} passiert. Die zentrale Registrierung stellt dagegen sicher, dass alle Routen des Moduls derselben Richtlinie unterliegen, ohne dass dies an jeder einzelnen Stelle wiederholt werden muss.
|
Der Einwand betrifft mehr als doppelten Code: Eine seitenspezifische Prüfung ist leicht zu übersehen, wenn weitere Seiten hinzukommen — genau das wäre bei \texttt{Download} und \texttt{Share} passiert. Die zentrale Registrierung stellt sicher, dass alle Routen des Moduls derselben Richtlinie unterliegen.
|
||||||
|
|
||||||
\subsection{Gestapelte Pull Requests}
|
\subsection{Gestapelte Pull Requests}
|
||||||
|
|
||||||
Ein Vorgehen, das sich durch die gesamte Umsetzung zieht und das Verständnis der Reviewhistorie erleichtert, ist die Verkettung der Pull Requests. Der erste Pull Request des Document Explorers ging gegen den Hauptbranch. Der darauf aufbauende Pull Request für die Icons wurde jedoch nicht ebenfalls gegen den Hauptbranch geführt, sondern gegen den Branch des Explorers. Der Suchen-Pull-Request wiederum ging gegen den Icon-Branch, der Download-Pull-Request gegen den Suchen-Branch und der Freigabelink-Pull-Request gegen den Download-Branch.
|
Ein Vorgehen, das sich durch die gesamte Umsetzung zieht, ist die Verkettung der Pull Requests. Der erste ging gegen den Hauptbranch; jeder darauf aufbauende gegen den Branch seines Vorgängers.
|
||||||
|
|
||||||
Der Grund ist die Reviewbarkeit. Da die Arbeiten inhaltlich aufeinander aufbauen, hätte ein direkter Vergleich gegen den Hauptbranch bei jedem Pull Request auch sämtliche Änderungen der Vorgänger enthalten. Ein Reviewer hätte die für das jeweilige Backlog Item relevanten Änderungen aus einem stetig wachsenden Gesamtunterschied heraussuchen müssen. Durch die Verkettung enthält jeder Pull Request genau die Änderungen seines eigenen Backlog Items. Auf diesen Umstand wurde jeweils im ersten Kommentar des Pull Requests hingewiesen.
|
Der Grund ist die Reviewbarkeit. Da die Arbeiten aufeinander aufbauen, hätte ein direkter Vergleich gegen den Hauptbranch auch sämtliche Vorgängeränderungen enthalten. Durch die Verkettung enthält jeder Pull Request genau die Änderungen seines Backlog Items.
|
||||||
|
|
||||||
Der Preis dieses Vorgehens zeigte sich beim Zusammenführen. Sobald ein Vorgänger in den Hauptbranch übernommen war, musste der Nachfolger auf den Hauptbranch umgestellt werden. Diese Umstellung setzt in Azure DevOps die bereits abgegebenen Freigaben zurück, sodass mehrere Reviewer ein zweites Mal zustimmen mussten. Bei zwei Pull Requests ist dieser Effekt im Ereignisprotokoll dokumentiert. Für künftige Arbeiten wäre abzuwägen, ob der Gewinn an Reviewbarkeit diesen zusätzlichen Abstimmungsaufwand rechtfertigt; bei der vorliegenden Zahl aufeinander aufbauender Backlog Items überwog er deutlich.
|
Der Preis zeigte sich beim Zusammenführen: Sobald ein Vorgänger in den Hauptbranch übernommen war, musste der Nachfolger umgestellt werden. Diese Umstellung setzt in Azure DevOps die bereits abgegebenen Freigaben zurück. Für künftige Arbeiten wäre abzuwägen, ob der Gewinn an Reviewbarkeit diesen Abstimmungsaufwand rechtfertigt; bei der vorliegenden Zahl aufeinander aufbauender Items überwog er deutlich.
|
||||||
|
|||||||
@@ -3,30 +3,28 @@
|
|||||||
|
|
||||||
\subsection{Umfang}
|
\subsection{Umfang}
|
||||||
|
|
||||||
Der Document Explorer war das erste umgesetzte Backlog Item und mit acht Aufwandspunkten zugleich das umfangreichste. Er umfasst die Grundstruktur des gesamten Moduls: die Seite selbst, den Dienst, den Speicherclient, die Modelle sowie die Einbindung in Navigation und Autorisierung. Alle nachfolgenden Backlog Items bauen darauf auf.
|
Der Document Explorer war das erste umgesetzte Backlog Item und mit acht Aufwandspunkten das umfangreichste. Er umfasst die Grundstruktur des gesamten Moduls: Seite, Dienst, Speicherclient, Modelle sowie Einbindung in Navigation und Autorisierung.
|
||||||
|
|
||||||
\subsection{Vom Kachel- zum Listenentwurf}
|
\subsection{Vom Kachel- zum Listenentwurf}
|
||||||
|
|
||||||
Die ursprüngliche Fassung der Anforderung sah vor, Dokumente als Kacheln darzustellen. Im Verlauf wurde dies auf eine Zeilendarstellung geändert. Ausschlaggebend war weniger eine gestalterische Vorliebe als die absehbare Entwicklung der Anforderungen: Bereits im Backlog standen Auswahlboxen für den ZIP-Download, Download- und Teilen-Schaltflächen sowie eine Vorschaufunktion. Eine Kachel hätte für diese Elemente keinen natürlichen Platz geboten, eine Tabellenzeile dagegen schon. Die Feature-Beschreibung hält ausdrücklich fest, dass die Tabelle flexibel um weitere Spalten erweiterbar sein soll.
|
Die ursprüngliche Fassung sah eine Kacheldarstellung vor. Im Verlauf wurde auf Zeilendarstellung geändert, da Auswahlboxen für den ZIP-Download, Download- und Teilen-Schaltflächen sowie eine Vorschau im Backlog standen. Eine Kachel hätte für diese Elemente keinen natürlichen Platz geboten. Die Tabelle soll flexibel um weitere Spalten erweiterbar sein.
|
||||||
|
|
||||||
\subsection{Ableitung des Dokumententyps}
|
\subsection{Ableitung des Dokumententyps}
|
||||||
|
|
||||||
Der Typ eines Dokuments ergibt sich aus dem Namen des Unterordners, in dem es liegt. In der Umsetzung wird der Ordnername gegen eine Aufzählung der bekannten Typen abgeglichen. Im Review schlug \emph{Timo Walter} vor, hierfür die Zeichenketten-Umwandlung der Aufzählung mit Nichtbeachtung der Groß- und Kleinschreibung zu verwenden, statt eine eigene Zuordnung zu pflegen.
|
Der Typ eines Dokuments ergibt sich aus dem Unterordnernamen und wird gegen eine Aufzählung abgeglichen. Im Review schlug \emph{Timo Walter} vor, die Zeichenketten-Umwandlung mit Nichtbeachtung der Groß- und Kleinschreibung zu verwenden statt einer eigenen Zuordnung. Beim Hinzufügen eines Typs muss so nur die Aufzählung ergänzt werden. Schlägt die Umwandlung fehl, gilt das Dokument als untypisiert.
|
||||||
|
|
||||||
Der Vorschlag wurde übernommen. Er hat den Vorteil, dass beim Hinzufügen eines Typs nur die Aufzählung ergänzt werden muss und die Zuordnung nicht an einer zweiten Stelle nachgeführt werden kann. Schlägt die Umwandlung fehl, liefert die Methode kein Ergebnis, und das Dokument gilt als untypisiert — genau das gewünschte Verhalten für unbekannte Ordner.
|
Eine bewusste Ausnahme betrifft die Übersetzung: Fehlt für einen bekannten Typ der Eintrag in der Ressourcendatei, wird eine Ausnahme ausgelöst. Jeder Wert der Aufzählung ist per Definition gültig; fehlt seine Übersetzung, ist das eine unvollständige Implementierung. Ein stiller Rückfall auf den Ordnernamen würde diesen Fehler verdecken.
|
||||||
|
|
||||||
Eine bewusste Ausnahme betrifft die Übersetzung der Typbezeichnungen. Fehlt für einen bekannten Typ der Eintrag in der Ressourcendatei, wird kein Ersatzwert verwendet, sondern eine Ausnahme ausgelöst. Im Review wurde hinterfragt, ob das nicht ein zulässiger Fall sei. Die Begründung dagegen: Jeder Wert der Aufzählung ist per Definition gültig; fehlt seine Übersetzung, ist das kein Laufzeitzustand, sondern eine unvollständige Implementierung. Ein stiller Rückfall auf den technischen Ordnernamen würde diesen Fehler verdecken, statt ihn sichtbar zu machen.
|
|
||||||
|
|
||||||
\subsection{Leerer Zustand und Fehlerbehandlung}
|
\subsection{Leerer Zustand und Fehlerbehandlung}
|
||||||
|
|
||||||
Zwei Anforderungen betreffen Situationen, in denen keine Dokumente angezeigt werden können, und sie sind ausdrücklich voneinander zu unterscheiden.
|
Zwei Anforderungen betreffen Situationen ohne anzeigbare Dokumente und sind zu unterscheiden.
|
||||||
|
|
||||||
Der \textbf{leere Zustand} tritt ein, wenn der Kunde berechtigt ist, aber noch keine Dokumente für ihn abgelegt wurden. Er ist ein normaler Betriebszustand und wird mit einem entsprechenden Hinweis dargestellt. Dieser Fall tritt insbesondere unmittelbar nach der automatischen Anlage der Ordnerstruktur auf.
|
Der \textbf{leere Zustand} tritt ein, wenn der Kunde berechtigt ist, aber keine Dokumente vorliegen — insbesondere unmittelbar nach der automatischen Ordneranlage. Er wird mit einem Hinweis dargestellt.
|
||||||
|
|
||||||
Ein \textbf{Fehler} liegt dagegen vor, wenn die Dokumente nicht geladen werden konnten — etwa weil der Speicher nicht erreichbar ist oder die Zugangsdaten nicht stimmen. Im Review wurde ausdrücklich angemerkt, dass dieser Fall nicht in einer stillschweigend leeren Seite münden darf. Der Unterschied ist für den Kunden erheblich: Im einen Fall gibt es nichts zu sehen, im anderen funktioniert die Anwendung nicht. Würden beide Fälle gleich dargestellt, bliebe eine Störung unbemerkt, bis jemand sie zufällig meldet.
|
Ein \textbf{Fehler} liegt vor, wenn die Dokumente nicht geladen werden konnten. Im Review wurde angemerkt, dass dieser Fall nicht in einer stillschweigend leeren Seite münden darf: Im einen Fall gibt es nichts zu sehen, im anderen funktioniert die Anwendung nicht.
|
||||||
|
|
||||||
\subsection{Die bekannte Schwäche}
|
\subsection{Die bekannte Schwäche}
|
||||||
|
|
||||||
Bereits im Review dieses Pull Requests fragte \emph{Hanna Ebner}, warum für jeden Kunden eine eigene Abfrage abgesetzt werde und ob sich das nicht über einen Filter lösen lasse. Die Antwort war, dass die Schnittstelle dies nicht zulässt, verbunden mit dem Verweis auf das eigens angelegte Recherche-Item.
|
Bereits im Review fragte \emph{Hanna Ebner}, warum für jeden Kunden eine eigene Abfrage nötig sei. Die Antwort war, dass die Schnittstelle dies nicht anders zulässt, verbunden mit dem Verweis auf das eigens angelegte Recherche-Item.
|
||||||
|
|
||||||
Diese Stelle ist aus zwei Gründen bemerkenswert. Erstens zeigt sie, dass die Schwäche nicht übersehen, sondern erkannt und bewusst zurückgestellt wurde: Der Explorer sollte nicht daran scheitern, dass eine Optimierung noch nicht gefunden war. Zweitens dokumentiert der Verweis auf das Folgeitem die Entscheidung nachvollziehbar — der Reviewer musste sie nicht glauben, sondern konnte sie im Backlog nachvollziehen. Die tatsächliche Lösung dieses Problems beschreibt Abschnitt~\ref{sec:folder-management}.
|
Die Schwäche wurde erkannt und bewusst zurückgestellt — der Explorer sollte nicht daran scheitern, dass eine Optimierung noch nicht gefunden war. Die tatsächliche Lösung beschreibt Abschnitt~\ref{sec:folder-management}.
|
||||||
|
|||||||
@@ -3,19 +3,15 @@
|
|||||||
|
|
||||||
\subsection{Einzeldownload über zeitlich begrenzte Zugriffs-URLs}
|
\subsection{Einzeldownload über zeitlich begrenzte Zugriffs-URLs}
|
||||||
|
|
||||||
Für den Download einzelner Dokumente wäre es möglich gewesen, den Inhalt aus dem Speicher zu lesen und durch die Anwendung hindurch an den Browser weiterzureichen. Stattdessen wird eine zeitlich begrenzte Zugriffs-URL erzeugt, mit der der Browser das Dokument unmittelbar vom Speicher abruft \autocite{aws-presigned-urls}.
|
Für den Einzeldownload wird eine zeitlich begrenzte Zugriffs-URL erzeugt, mit der der Browser das Dokument unmittelbar vom Speicher abruft \autocite{aws-presigned-urls}. Die Datenübertragung läuft nicht über die Anwendung; ein großes Dokument belegt weder Arbeitsspeicher noch Verbindungen des Webservers. Die URL ist nur wenige Minuten gültig.
|
||||||
|
|
||||||
Der Vorteil liegt darin, dass die eigentliche Datenübertragung nicht über die Anwendung läuft. Ein großes Dokument belegt weder Arbeitsspeicher noch eine Verbindung des Webservers; die Anwendung ist nach dem Erzeugen der URL nicht mehr beteiligt. Die URL ist nur wenige Minuten gültig und danach wertlos.
|
Wichtig ist, dass die Berechtigungsprüfung \emph{vor} dem Erzeugen der URL stattfindet. Die URL selbst trägt keine Prüfung — wer sie besitzt, kann zugreifen. Sie darf deshalb nur für einen Schlüssel erzeugt werden, der nachweislich im Kundenordner liegt. Genau hier setzte der überwiegende Teil des Reviews an, und die Rückfragen führten zur in Abschnitt~\ref{sec:architecture} beschriebenen Einführung eigener Pfadtypen.
|
||||||
|
|
||||||
Wichtig ist dabei, dass die Berechtigungsprüfung \emph{vor} dem Erzeugen der URL stattfindet. Die URL selbst trägt keine Prüfung mehr in sich — wer sie besitzt, kann innerhalb ihrer Gültigkeitsdauer auf das Dokument zugreifen. Sie darf deshalb nur für einen Schlüssel erzeugt werden, der nachweislich innerhalb des Kundenordners liegt.
|
Zusätzlich musste sichergestellt werden, dass das Dokument unter seinem ursprünglichen Namen ankommt, da der Schlüssel den vollständigen Pfad enthält.
|
||||||
|
|
||||||
Genau an dieser Stelle setzte der überwiegende Teil des Reviews an. Die Prüfung, dass ein angeforderter Pfad den Kundenordner nicht verlässt, war die inhaltlich kritischste Einzelanforderung des Moduls, und die wiederholten Rückfragen dazu führten zu der in Abschnitt~\ref{sec:architecture} beschriebenen Einführung eigener Pfadtypen. Der Pull Request durchlief 23 Iterationen; ein erheblicher Anteil davon entfiel auf dieses Refactoring und nicht auf die Downloadfunktion selbst.
|
|
||||||
|
|
||||||
Zusätzlich musste sichergestellt werden, dass das Dokument unter seinem ursprünglichen Namen ankommt. Da der Schlüssel im Speicher den vollständigen Pfad enthält, würde ein unbehandelter Download unter einem technischen Namen gespeichert. Der gewünschte Dateiname wird deshalb ausdrücklich mitgegeben.
|
|
||||||
|
|
||||||
\subsection{ZIP-Download}
|
\subsection{ZIP-Download}
|
||||||
|
|
||||||
Für den Download mehrerer Dokumente erhält jede Zeile eine Auswahlbox. Die Schaltfläche für den ZIP-Download ist deaktiviert, solange nichts ausgewählt ist, und wird erst mit der ersten Auswahl freigegeben. Damit ist die Anforderung, dass ohne Auswahl kein Download ausgelöst werden kann, unmittelbar in der Bedienoberfläche abgebildet und muss nicht durch eine Fehlermeldung nachgereicht werden.
|
Jede Zeile erhält eine Auswahlbox für den ZIP-Download. Die Schaltfläche ist deaktiviert, solange nichts ausgewählt ist.
|
||||||
|
|
||||||
Abbildung~\ref{fig:zip-stream} zeigt den Ablauf.
|
Abbildung~\ref{fig:zip-stream} zeigt den Ablauf.
|
||||||
|
|
||||||
@@ -26,14 +22,8 @@ Abbildung~\ref{fig:zip-stream} zeigt den Ablauf.
|
|||||||
\label{fig:zip-stream}
|
\label{fig:zip-stream}
|
||||||
\end{figure}
|
\end{figure}
|
||||||
|
|
||||||
Das Archiv wird erst beim Klick erzeugt und nicht im Voraus vorgehalten — auch dies eine ausdrückliche Anforderung. Entscheidend für die Umsetzung ist, dass es dabei zu keinem Zeitpunkt vollständig im Arbeitsspeicher oder auf der Festplatte entsteht: Die Anwendung öffnet die Antwort als Archivstrom, liest die ausgewählten Dokumente nacheinander aus dem Speicher und schreibt sie unmittelbar als Einträge hinein \autocite{dotnet-zip}. Die komprimierten Daten fließen dabei direkt zum Browser.
|
Das Archiv wird erst beim Klick erzeugt. Entscheidend ist, dass es nie vollständig im Arbeitsspeicher entsteht: Die Anwendung öffnet die Antwort als Archivstrom, liest die Dokumente nacheinander aus dem Speicher und schreibt sie unmittelbar als Einträge hinein \autocite{dotnet-zip}. Da die Gesamtgröße unbegrenzt ist, würde ein vollständiger Aufbau den Speicherbedarf von der größten Auswahl abhängig machen. Beim Strömen bleibt er konstant.
|
||||||
|
|
||||||
Diese Arbeitsweise ist notwendig, weil die Gesamtgröße einer Auswahl nicht begrenzt ist. Würde das Archiv zunächst vollständig aufgebaut, bestimmte die größte denkbare Auswahl den Speicherbedarf des Servers. Beim Strömen bleibt dieser dagegen unabhängig von der Auswahlgröße konstant. Die Struktur innerhalb des Archivs entspricht der Ablagestruktur, sodass die Typordner erhalten bleiben.
|
|
||||||
|
|
||||||
\subsection{ZIP auch bei einer einzelnen Datei}
|
\subsection{ZIP auch bei einer einzelnen Datei}
|
||||||
|
|
||||||
Im Review stellte \emph{Sarah Hinzmann} die Frage, ob auch bei nur einer ausgewählten Datei ein Archiv erzeugt werden solle, statt die Datei direkt auszuliefern.
|
Im Review stellte \emph{Sarah Hinzmann} die Frage, ob bei nur einer ausgewählten Datei ein Archiv erzeugt werden solle. Die Entscheidung fiel für das Archiv: Die Schaltfläche ist mit „Als ZIP herunterladen" beschriftet und soll vorhersagbar ein Archiv liefern. Wer eine einzelne Datei unverpackt benötigt, verwendet die Download-Schaltfläche in der Zeile.
|
||||||
|
|
||||||
Die Entscheidung fiel für das Archiv. Der Grund ist die Vorhersagbarkeit der Bedienung: Die Schaltfläche ist mit „Als ZIP herunterladen" beschriftet, und wer sie betätigt, soll ein Archiv erhalten — unabhängig davon, wie viele Dokumente ausgewählt sind. Wer eine einzelne Datei unverpackt benötigt, verwendet die Download-Schaltfläche in der jeweiligen Zeile. Eine automatische Umschaltung des Verhaltens abhängig von der Auswahlgröße wäre für den Benutzer schwerer zu antizipieren und hätte zudem bedeutet, dass ein Skript auf der Seite die beiden Fälle unterscheiden müsste.
|
|
||||||
|
|
||||||
Der Fall zeigt beispielhaft, wie eine scheinbar kleine Bedienfrage eine begründete Entscheidung erfordert. Beide Varianten sind vertretbar; ausschlaggebend war, dass jede Schaltfläche genau eine Bedeutung behält.
|
|
||||||
|
|||||||
@@ -5,28 +5,20 @@
|
|||||||
|
|
||||||
Unterhalb der Suchleiste steht je Dokumententyp ein Bedienelement zum Ein- und Ausblenden. Die Auswahl wird als Abfrageparameter übertragen und im PageModel ausgewertet; die Filterung selbst erfolgt anschließend im Dienst.
|
Unterhalb der Suchleiste steht je Dokumententyp ein Bedienelement zum Ein- und Ausblenden. Die Auswahl wird als Abfrageparameter übertragen und im PageModel ausgewertet; die Filterung selbst erfolgt anschließend im Dienst.
|
||||||
|
|
||||||
Die Auswertung des Parameters war Gegenstand des Reviews. Da die Werte aus der Adresszeile stammen, muss die Anwendung damit rechnen, dass sie manipuliert oder veraltet sind — etwa wenn ein gespeicherter Link auf einen inzwischen entfernten Typ verweist. \emph{Robin Noack} schlug vor, die Umwandlung fehlertolerant vorzunehmen und nicht erkennbare Werte stillschweigend zu verwerfen, statt die Anfrage scheitern zu lassen.
|
Die Auswertung des Parameters war Gegenstand des Reviews. Da die Werte aus der Adresszeile stammen, muss die Anwendung mit manipulierten oder veralteten Werten rechnen. \emph{Robin Noack} schlug vor, nicht erkennbare Werte stillschweigend zu verwerfen. Ein unbekannter Filterwert ist kein Angriff, sondern in der Regel ein veralteter Link. Die Mandantentrennung geschieht über das Präfix, nicht über den Filter.
|
||||||
|
|
||||||
Die Begründung überzeugt: Ein unbekannter Filterwert ist kein Angriff und kein Fehler des Benutzers, sondern in aller Regel ein veralteter Link. Die Anwendung ignoriert ihn und zeigt die verbleibenden Typen an. Ein Fehlerbild wäre hier unangemessen, zumal keine Sicherheitsentscheidung von diesem Wert abhängt — die Mandantentrennung geschieht über das Präfix, nicht über den Filter.
|
Ein zweiter Reviewhinweis betraf die Benennung: Der Typ für eine Menge von Dokumententypen unterschied sich vom Einzeltyp nur durch ein Zeichen. Da eine Verwechslung leicht möglich ist, wurde die Benennung geschärft.
|
||||||
|
|
||||||
Ein zweiter Reviewhinweis betraf die Benennung. Der Typ für eine Menge von Dokumententypen unterschied sich vom Typ für einen einzelnen Dokumententyp lediglich durch ein angehängtes Zeichen. Da eine Verwechslung beim Lesen leicht möglich und beim Übersetzen nicht auffällig ist, wurde die Benennung geschärft.
|
|
||||||
|
|
||||||
\subsection{Paginierung mit Fortsetzungsmerkmalen}
|
\subsection{Paginierung mit Fortsetzungsmerkmalen}
|
||||||
|
|
||||||
Die Paginierung folgt der Arbeitsweise des Speichers. Dieser liefert Ergebnisse blockweise und gibt, sofern weitere Treffer vorliegen, ein Fortsetzungsmerkmal zurück, mit dem der jeweils nächste Block angefordert werden kann \autocite{aws-listobjectsv2}.
|
Die Paginierung folgt der Arbeitsweise des Speichers, der Ergebnisse blockweise mit Fortsetzungsmerkmalen liefert \autocite{aws-listobjectsv2}. Ein Fortsetzungsmerkmal setzt die Auflistung dort fort, wo sie endete. Ein direkter Sprung auf eine beliebige Seite ist nicht möglich, ohne alle vorhergehenden Blöcke abzurufen. Die Akzeptanzkriterien verlangten dies auch nicht — gefordert waren Vor- und Zurück-Navigation.
|
||||||
|
|
||||||
Dieses Verfahren unterscheidet sich grundlegend von der sonst üblichen Paginierung über eine Positionsangabe. Ein Fortsetzungsmerkmal beschreibt keine Position im Ergebnis, sondern setzt die Auflistung an der Stelle fort, an der sie zuletzt endete. Daraus folgt eine Einschränkung, die sich nicht umgehen lässt: Ein direkter Sprung auf eine beliebige Seite ist nicht möglich, ohne alle vorhergehenden Blöcke abzurufen. Die Akzeptanzkriterien verlangten dies auch nicht — gefordert waren eine Vor- und eine Zurück-Navigation.
|
|
||||||
|
|
||||||
\subsection{Übernommene Annahmen}
|
\subsection{Übernommene Annahmen}
|
||||||
|
|
||||||
Ein aufschlussreicher Reviewfund betraf einen Zahlenwert für die maximale Seitengröße. Er war aus den bestehenden Houston-Seiten übernommen worden, um die geforderte Konsistenz herzustellen (NFA-4). \emph{Robin Noack} wies darauf hin, dass dieser Wert dort aus einer Beschränkung des ITSM-Systems stammt und für den Speicher keine Bedeutung hat.
|
Ein Zahlenwert für die maximale Seitengröße war aus den bestehenden Houston-Seiten übernommen worden (NFA-4). \emph{Robin Noack} wies darauf hin, dass dieser Wert dort aus einer Beschränkung des ITSM-Systems stammt und für den Speicher keine Bedeutung hat. Was wie eine Konvention aussah, war eine technische Grenze eines anderen Systems. Der Wert wurde angepasst.
|
||||||
|
|
||||||
Der Fund ist typisch für das Übernehmen bestehender Muster: Was wie eine Konvention aussah, war tatsächlich eine technische Grenze eines ganz anderen Systems. Konsistenz in der Bedienung bedeutet nicht, dass auch die zugrunde liegenden technischen Werte zu übernehmen sind. Der Wert wurde an die Gegebenheiten des Speichers angepasst.
|
Diskutiert wurde auch, ob die vom Benutzer wählbare Seitengröße nach oben begrenzt werden sollte, da ein sehr großer Wert aus der Adresszeile eine entsprechend große Antwort erzwingen könnte.
|
||||||
|
|
||||||
Diskutiert wurde in diesem Zusammenhang auch, ob die vom Benutzer wählbare Seitengröße nach oben begrenzt werden sollte. Da der Wert aus der Adresszeile stammt, könnte ein sehr großer Wert eine entsprechend große Antwort erzwingen.
|
|
||||||
|
|
||||||
\subsection{Zusammenspiel mit Freigabelinks}
|
\subsection{Zusammenspiel mit Freigabelinks}
|
||||||
|
|
||||||
Die Paginierung wirkt sich unmittelbar auf die Freigabelinks aus. Ein Link, der auf ein bestimmtes Dokument verweist, ist wertlos, wenn er stets auf der ersten Seite landet und das Dokument auf der vierten liegt.
|
Die Paginierung wirkt sich auf die Freigabelinks aus: Ein Link auf ein Dokument ist wertlos, wenn er stets auf der ersten Seite landet. Die Weiterleitung setzt deshalb nicht nur die Sprungmarke, sondern auch die Abfrageparameter für die richtige Seite. Diese Abhängigkeit war bei der Formulierung des Items berücksichtigt worden.
|
||||||
|
|
||||||
Die Weiterleitung eines Freigabelinks setzt deshalb nicht nur die Sprungmarke auf das Dokument, sondern auch die Abfrageparameter, die zur richtigen Seite führen. Diese Anforderung war bereits bei der Formulierung des Paginierungs-Items berücksichtigt worden — ein Hinweis darauf, dass die wechselseitigen Abhängigkeiten der nachgeschobenen Items zu diesem Zeitpunkt bereits im Blick waren.
|
|
||||||
|
|||||||
@@ -3,36 +3,30 @@
|
|||||||
|
|
||||||
\subsection{Gemeinsame Umsetzung zweier Backlog Items}
|
\subsection{Gemeinsame Umsetzung zweier Backlog Items}
|
||||||
|
|
||||||
Die automatische Anlage der Ordnerstruktur und der neue Kundenordner-Lookup wurden ursprünglich als getrennte Backlog Items geführt und in einem gemeinsamen Pull Request umgesetzt. Der Grund ist in der Beschreibung des Pull Requests festgehalten: Der Lookup benötigt in seiner Rückfallebene ohnehin den Fall, dass kein Ordner existiert und die Struktur angelegt werden muss. Die Ordneranlage getrennt zu implementieren hätte bedeutet, an dieser Stelle zunächst einen Platzhalter einzufügen, um ihn im unmittelbar folgenden Pull Request wieder zu entfernen.
|
Die automatische Ordneranlage und der neue Kundenordner-Lookup wurden in einem gemeinsamen Pull Request umgesetzt. Der Lookup benötigt in seiner Rückfallebene den Fall, dass kein Ordner existiert und die Struktur angelegt werden muss. Eine getrennte Implementierung hätte einen Platzhalter erfordert, der im nächsten Pull Request entfernt worden wäre. Die Zusammenlegung wurde begründet und beide Items verknüpft.
|
||||||
|
|
||||||
Die Zusammenlegung wurde nicht stillschweigend vorgenommen, sondern in der Beschreibung begründet und beide Items wurden verknüpft. Damit bleibt der Zusammenhang zwischen Planung und Umsetzung nachvollziehbar, auch wenn die Aufteilung nicht eingehalten wurde.
|
|
||||||
|
|
||||||
\subsection{Normalisierung des Firmennamens}
|
\subsection{Normalisierung des Firmennamens}
|
||||||
|
|
||||||
Der erwartete Ordnername entsteht aus dem Firmennamen des angemeldeten Benutzers. Da dieser Name aus einem Fremdsystem stammt und beliebige Zeichen enthalten kann, wird er zuvor normalisiert. Die Normalisierung entfernt umschließende Leerzeichen, ersetzt Zeichen, die im Schlüssel eine strukturelle Bedeutung haben oder nicht darstellbar sind, verhindert führende und abschließende Punkte und begrenzt die Länge.
|
Der erwartete Ordnername entsteht aus dem Firmennamen des Benutzers. Da dieser aus einem Fremdsystem stammt und beliebige Zeichen enthalten kann, wird er normalisiert: umschließende Leerzeichen entfernt, strukturell bedeutsame oder nicht darstellbare Zeichen ersetzt, führende und abschließende Punkte verhindert, die Länge begrenzt.
|
||||||
|
|
||||||
Zwei Eigenschaften sind dabei entscheidend. Die Normalisierung ist \textbf{deterministisch} — derselbe Eingabename ergibt stets denselben Ordnernamen, was die Voraussetzung dafür ist, dass der Direktzugriff überhaupt funktionieren kann. Und sie erhält \textbf{Umlaute}, weil der Ordner in Filestash von Menschen gelesen wird; die Zumutung, eine Firma „Müller GmbH" unter \texttt{Mueller GmbH} zu suchen, wäre der Bedienbarkeit abträglich.
|
Zwei Eigenschaften sind entscheidend. Die Normalisierung ist \textbf{deterministisch} — derselbe Eingabename ergibt stets denselben Ordnernamen, Voraussetzung für den Direktzugriff. Sie erhält \textbf{Umlaute}, weil der Ordner in Filestash von Menschen gelesen wird.
|
||||||
|
|
||||||
Im Review fragte \emph{Robin Noack}, ob die gewählten Einschränkungen ausreichen. Die Frage ist berechtigt, denn eine zu schwache Normalisierung führt zu Schlüsseln, die der Speicher zurückweist oder anders interpretiert als erwartet — und beides wäre nicht bei der Entwicklung, sondern erst bei einem Kunden mit einem ungewöhnlichen Firmennamen aufgefallen. Die Antwort verwies auf die Herstellerdokumentation zu den Namensbeschränkungen \autocite{ibm-s3-naming, aws-s3-naming}. Der Punkt ist erwähnenswert, weil er zeigt, dass eine solche Zusicherung sich belegen lassen muss und nicht auf einer Vermutung beruhen darf.
|
Im Review fragte \emph{Robin Noack}, ob die Einschränkungen ausreichen. Eine zu schwache Normalisierung führt zu Schlüsseln, die der Speicher zurückweist — das wäre erst bei einem Kunden mit ungewöhnlichem Firmennamen aufgefallen. Die Antwort verwies auf die Herstellerdokumentation \autocite{ibm-s3-naming, aws-s3-naming}.
|
||||||
|
|
||||||
\subsection{Umsetzung des Lookups}
|
\subsection{Umsetzung des Lookups}
|
||||||
|
|
||||||
Die in Abschnitt~\ref{sec:lookup-decision} beschriebene dreistufige Auflösung wurde in einer Methode des Dienstes zusammengefasst. Sie ist die einzige Stelle, an der ein Kundenpräfix entsteht; alle übrigen Zugriffe erhalten das Ergebnis als bereits aufgelösten Wert.
|
Die in Abschnitt~\ref{sec:lookup-decision} beschriebene dreistufige Auflösung wurde in einer Methode zusammengefasst — der einzigen Stelle, an der ein Kundenpräfix entsteht. Der erste Schritt fragt die Metadaten des erwarteten Markers ab. Der zweite wiederholt dies mit dem um die Organisations-ID ergänzten Namen (Kollisionsfall). Erst der dritte greift auf das lineare Verfahren zurück.
|
||||||
|
|
||||||
Der erste Schritt fragt die Metadaten des erwarteten Markers ab. Ist er vorhanden und trägt die Organisations-ID des Benutzers, endet die Auflösung. Der zweite Schritt wiederholt dies mit dem um die Organisations-ID ergänzten Namen und deckt damit den Kollisionsfall ab. Erst der dritte Schritt greift auf das ursprüngliche, lineare Verfahren zurück.
|
Beim Prüfen, ob ein Zielname frei ist, wird nicht nur der Marker betrachtet, sondern auch geprüft, ob bereits Objekte unterhalb des Zielpräfixes liegen. Ein Zielname gilt nur als frei, wenn weder ein fremder Marker noch Inhalte vorhanden sind.
|
||||||
|
|
||||||
Beim Prüfen, ob ein Zielname beansprucht werden darf, wird nicht nur der Marker betrachtet, sondern zusätzlich geprüft, ob unterhalb des Zielpräfixes bereits Objekte liegen. Dazu genügt eine Auflistung, die nach dem ersten Treffer abbricht. Ein Zielname gilt nur dann als frei, wenn dort weder ein fremder Marker liegt noch bereits Inhalte vorhanden sind.
|
|
||||||
|
|
||||||
\subsection{Umbenennen als Kopiervorgang}
|
\subsection{Umbenennen als Kopiervorgang}
|
||||||
|
|
||||||
Der Speicher kennt keine Umbenennung. Ein Objekt lässt sich nur unter einem neuen Schlüssel kopieren und anschließend unter dem alten löschen. Für einen Ordner mit vielen Dokumenten bedeutet das entsprechend viele Einzelvorgänge.
|
Der Speicher kennt keine Umbenennung. Ein Objekt lässt sich nur kopieren und anschließend löschen. Eine Umbenennung ist daher nicht unteilbar — sie kann abbrechen, und Objekte liegen teils am alten, teils am neuen Ort.
|
||||||
|
|
||||||
Daraus folgt, dass eine Umbenennung nicht unteilbar ist. Sie kann in der Mitte abbrechen — etwa durch einen Netzwerkfehler —, und dann liegen die Objekte teils am alten, teils am neuen Ort. Die Akzeptanzkriterien verlangten für diesen Fall ausdrücklich, dass keine Dokumente verloren gehen.
|
Umgesetzt wurde dies über zwei Maßnahmen. Erstens wird der \textbf{Marker zuletzt kopiert}: Solange er fehlt, wird das Ziel von einer parallelen Anfrage nicht als Kundenordner erkannt. Zweitens entfernt die Fehlerbehandlung bereits erzeugte Teilkopien, damit der Vorgang wiederholbar ist.
|
||||||
|
|
||||||
Umgesetzt wurde dies über zwei Maßnahmen. Erstens wird der \textbf{Marker zuletzt kopiert}. Solange er fehlt, gilt das Ziel als unvollständig und wird von einer parallelen Anfrage nicht als bestehender Kundenordner erkannt. Der Zielordner wird also erst in dem Moment „gültig", in dem er vollständig ist. Zweitens versucht die Fehlerbehandlung, die bereits erzeugten Teilkopien wieder zu entfernen, damit das Ziel leer bleibt und der Vorgang wiederholbar ist.
|
Die zweite Maßnahme gilt nur, solange kein zweiter Vorgang dieselbe Umbenennung ausführt. Dieser Fall ist Gegenstand des folgenden Abschnitts.
|
||||||
|
|
||||||
Die zweite Maßnahme beruht auf einer Annahme, die sich später als nicht allgemeingültig erwies — sie gilt nur, solange kein zweiter Vorgang dieselbe Umbenennung ausführt. Dieser Fall ist Gegenstand des folgenden Abschnitts.
|
|
||||||
|
|
||||||
\subsection{Stand bei Abgabe}
|
\subsection{Stand bei Abgabe}
|
||||||
|
|
||||||
Der zugehörige Pull Request war zum Zeitpunkt der Erstellung dieser Dokumentation noch offen. Die inhaltlichen Anmerkungen aus dem Review waren geschlossen, die abschließende Freigabe stand jedoch aus. Der Abschnitt beschreibt damit — anders als die vorangegangenen — Arbeit, die fachlich fertiggestellt, aber noch nicht in den Hauptbranch übernommen war.
|
Der zugehörige Pull Request war zum Zeitpunkt der Erstellung dieser Dokumentation noch offen. Die inhaltlichen Anmerkungen waren geschlossen, die abschließende Freigabe stand aus.
|
||||||
|
|||||||
@@ -3,30 +3,26 @@
|
|||||||
|
|
||||||
\subsection{PDF-Vorschau im Modal}
|
\subsection{PDF-Vorschau im Modal}
|
||||||
|
|
||||||
Die Vorschau öffnet PDF-Dokumente in einem überlagerten Fenster, ohne dass sie zuvor heruntergeladen werden müssen. Sie greift dabei auf dieselbe zeitlich begrenzte Zugriffs-URL zurück, die auch dem Download zugrunde liegt — der Unterschied liegt allein darin, dass der Browser das Dokument anzeigt, statt es zu speichern. Für Dateien, die keine PDF-Dokumente sind, wird keine Vorschau angeboten.
|
Die Vorschau öffnet PDF-Dokumente in einem überlagerten Fenster über dieselbe zeitlich begrenzte Zugriffs-URL, die auch dem Download zugrunde liegt. Für Nicht-PDF-Dateien wird keine Vorschau angeboten.
|
||||||
|
|
||||||
Bewusst nicht umgesetzt wurde eine Prüfung der Dateigröße. Diese Abgrenzung wurde im Approval-Termin ausdrücklich in die Anforderung aufgenommen: Das Dokument wird angezeigt, unabhängig davon, wie groß es ist. Die Begründung ist pragmatisch — jede Größengrenze wäre willkürlich, und die Betrachtung eines großen Dokuments ist kein Fehlerfall, sondern lediglich langsam. Der Browser stellt PDF-Dokumente ohnehin fortlaufend dar.
|
Eine Prüfung der Dateigröße wurde bewusst nicht umgesetzt. Diese Abgrenzung wurde im Approval-Termin festgelegt: Jede Grenze wäre willkürlich, und der Browser stellt PDF-Dokumente ohnehin fortlaufend dar.
|
||||||
|
|
||||||
\subsection{Aufbau der Freigabelinks}
|
\subsection{Aufbau der Freigabelinks}
|
||||||
|
|
||||||
Ein Freigabelink verweist auf eine eigene Seite, deren Adresse den relativen Pfad des Dokuments in kodierter Form enthält. Kodiert wird dabei nur der Anteil unterhalb des Kundenordners, nicht der Kundenordner selbst. Das ist keine Sicherheitsmaßnahme — die Kodierung ist trivial umkehrbar —, sondern dient dazu, Pfadtrennzeichen und Sonderzeichen unbeschadet durch die Adresse zu transportieren.
|
Ein Freigabelink verweist auf eine eigene Seite, deren Adresse den relativen Pfad des Dokuments kodiert enthält. Kodiert wird nur der Anteil unterhalb des Kundenordners, um Pfadtrennzeichen und Sonderzeichen unbeschadet durch die Adresse zu transportieren.
|
||||||
|
|
||||||
Dass der Kundenordner nicht Bestandteil des Links ist, hat dagegen sehr wohl eine Wirkung: Der Link enthält keinen Hinweis auf die Organisation und ist ohne den Kontext des angemeldeten Benutzers nicht auflösbar. Die Zuordnung geschieht ausschließlich über die Anmeldung des Aufrufers.
|
Dass der Kundenordner nicht Bestandteil des Links ist, hat eine Wirkung: Der Link enthält keinen Hinweis auf die Organisation und ist ohne Anmeldung nicht auflösbar.
|
||||||
|
|
||||||
Die Seite selbst zeigt keinen Dokumentinhalt an. Sie stellt Metaangaben für die Vorschau bereit und leitet anschließend auf die Dokumentenliste weiter, wobei die Sprungmarke auf das jeweilige Dokument gesetzt und — nach Umsetzung der Paginierung — die passende Seite angesteuert wird.
|
Die Seite zeigt keinen Dokumentinhalt an. Sie liefert Metaangaben für die Vorschau und leitet auf die Dokumentenliste weiter, wobei Sprungmarke und passende Seite angesteuert werden.
|
||||||
|
|
||||||
\subsection{Vorschau in Messengern}
|
\subsection{Vorschau in Messengern}
|
||||||
|
|
||||||
Der eigentliche Zweck der Freigabeseite ist, dass ein in einem Messenger geteilter Link dort mit Titel und Symbol dargestellt wird statt als nackte Adresse. Dazu werden entsprechende Metaangaben ausgeliefert: der Dokumentname als Titel, das Typsymbol als Bild und die Adresse der Dokumentenliste als Ziel.
|
Der Zweck der Freigabeseite ist, dass ein in einem Messenger geteilter Link mit Titel und Symbol dargestellt wird. Dazu werden Metaangaben ausgeliefert: Dokumentname als Titel, Typsymbol als Bild, Adresse der Dokumentenliste als Ziel.
|
||||||
|
|
||||||
Hier trat ein Problem auf, das sich nicht aus der Spezifikation ableiten ließ. Die Typsymbole liegen als Vektorgrafiken vor, und diese werden von den Vorschaudiensten gängiger Messenger nicht zuverlässig dargestellt. Die Symbole mussten daher zusätzlich als Rastergrafiken bereitgestellt werden, allein für diesen Zweck.
|
Die Typsymbole liegen als Vektorgrafiken vor, die von Vorschaudiensten gängiger Messenger nicht zuverlässig dargestellt werden. Sie mussten daher zusätzlich als Rastergrafiken bereitgestellt werden. Im Pull Request wurde vermerkt, dass die Rastergrafiken bei einer Gestaltungsänderung nachzuziehen sind.
|
||||||
|
|
||||||
Der Umstand hat eine unangenehme Nebenwirkung: Jedes Symbol existiert nun in zwei Formaten, die bei einer Gestaltungsänderung gemeinsam nachzuziehen sind. Im Pull Request wurde ausdrücklich vermerkt, dass die Rastergrafiken zu aktualisieren sind, sobald das Symboldesign endgültig feststeht.
|
|
||||||
|
|
||||||
\subsection{Sichtbarkeit der Vorschaubilder}
|
\subsection{Sichtbarkeit der Vorschaubilder}
|
||||||
|
|
||||||
Eine Entscheidung, die erst bei der Unterstützung von URL-Dateien vollständig zum Tragen kam, betrifft die Auslieferung der Vorschaubilder. Damit ein Messenger eine Vorschau erzeugen kann, muss er das Bild abrufen können — und zwar ohne Anmeldung, da der Vorschaudienst des Messengers keine Sitzung des Benutzers besitzt. Das Bild liegt somit auf einem öffentlich erreichbaren Pfad.
|
Damit ein Messenger eine Vorschau erzeugen kann, muss er das Bild ohne Anmeldung abrufen können. Bei den sieben festen Typsymbolen ist das unbedenklich, da sie keine kundenbezogene Information enthalten. Benutzerdefinierte Symbole aus URL-Dateien werden daher \emph{nicht} für die Vorschau ausgeliefert — andernfalls müsste Dateiinhalt über einen nicht authentifizierten Pfad zugänglich gemacht werden.
|
||||||
|
|
||||||
Solange es sich um die sieben festen Typsymbole handelt, ist das unbedenklich: Sie sind Bestandteil der Anwendung und enthalten keine kundenbezogene Information. Anders verhielte es sich bei Symbolen, die aus dem Inhalt einer Datei stammen. Aus diesem Grund wurde festgelegt, dass benutzerdefinierte Symbole aus URL-Dateien \emph{nicht} für die Vorschau ausgeliefert werden — andernfalls müsste Dateiinhalt über einen nicht authentifizierten Pfad zugänglich gemacht werden.
|
Die Entscheidung wurde im Pull Request festgehalten. Sie zeigt, dass eine harmlose Funktion in Verbindung mit einer anderen eine Sicherheitsfrage aufwirft, die keine der beiden für sich genommen aufgeworfen hätte.
|
||||||
|
|
||||||
Die Entscheidung wurde im Pull Request ausdrücklich festgehalten. Sie ist ein Beispiel dafür, dass eine an sich harmlose Funktion — ein Vorschaubild — in Verbindung mit einer anderen Funktion eine Sicherheitsfrage aufwirft, die keine der beiden für sich genommen aufgeworfen hätte.
|
|
||||||
|
|||||||
@@ -3,15 +3,13 @@
|
|||||||
|
|
||||||
\subsection{Ausgangslage}
|
\subsection{Ausgangslage}
|
||||||
|
|
||||||
Der Lesepfad des Kundenordner-Lookups ist unkritisch. Er fragt Metadaten ab und listet Objekte auf; gleichzeitige Aufrufe stören einander nicht. Der in Abschnitt~\ref{sec:folder-management} beschriebene dritte Schritt ist jedoch \emph{verändernd}: Er benennt Ordner um und legt sie an. Und er tut dies im Rahmen einer gewöhnlichen Seitenanfrage.
|
Der Lesepfad des Kundenordner-Lookups ist unkritisch. Der in Abschnitt~\ref{sec:folder-management} beschriebene dritte Schritt ist jedoch \emph{verändernd}: Er benennt Ordner um und legt sie an, im Rahmen einer gewöhnlichen Seitenanfrage. Da Houston in mehreren Instanzen betrieben wird, reicht eine Sperre innerhalb einer Instanz nicht aus.
|
||||||
|
|
||||||
Erschwerend kommt hinzu, dass Houston in mehr als einer Instanz betrieben wird. Eine Sperre innerhalb einer Instanz — der naheliegende erste Gedanke — reicht deshalb nicht aus, weil die zweite Instanz von ihr nichts weiß.
|
Bei der Durchsicht des Codes wurden zwei Fehlerszenarien gefunden, die ausdrücklich formulierte Akzeptanzkriterien verletzen.
|
||||||
|
|
||||||
Bei der Durchsicht des eigenen Codes im Anschluss an die Umsetzung wurden zwei konkrete Fehlerszenarien gefunden. Beide verletzen Akzeptanzkriterien, die im selben Backlog Item ausdrücklich formuliert worden waren.
|
|
||||||
|
|
||||||
\subsection{Szenario A: Datenverlust beim gleichzeitigen Umbenennen}
|
\subsection{Szenario A: Datenverlust beim gleichzeitigen Umbenennen}
|
||||||
|
|
||||||
Das erste Szenario betrifft die Fehlerbehandlung des Umbenennens. Sie entfernt die eigenen Teilkopien, damit das Ziel leer bleibt und der Vorgang wiederholbar ist. Diese Annahme trifft nicht mehr zu, sobald eine zweite Anfrage dieselbe Umbenennung ausführt. Abbildung~\ref{fig:race-condition-a} zeigt den Ablauf.
|
Das erste Szenario betrifft die Fehlerbehandlung des Umbenennens. Abbildung~\ref{fig:race-condition-a} zeigt den Ablauf.
|
||||||
|
|
||||||
\begin{figure}[H]
|
\begin{figure}[H]
|
||||||
\centering
|
\centering
|
||||||
@@ -20,43 +18,37 @@ Das erste Szenario betrifft die Fehlerbehandlung des Umbenennens. Sie entfernt d
|
|||||||
\label{fig:race-condition-a}
|
\label{fig:race-condition-a}
|
||||||
\end{figure}
|
\end{figure}
|
||||||
|
|
||||||
Beide Anfragen halten den Zielnamen für frei, weil beide prüfen, bevor eine von ihnen schreibt. Anfrage~A kopiert alle Objekte und löscht anschließend die Quelle. Anfrage~B, die langsamer ist, hat zu diesem Zeitpunkt erst einen Teil kopiert und findet beim nächsten Objekt die Quelle nicht mehr vor. Ihre Fehlerbehandlung greift und entfernt die von ihr erzeugten Kopien — das sind aber genau dieselben Objekte, die A soeben erfolgreich geschrieben hat. Da die Quelle bereits gelöscht ist, existiert von ihnen keine weitere Kopie.
|
Beide Anfragen halten den Zielnamen für frei, weil beide prüfen, bevor eine schreibt. Anfrage~A kopiert alle Objekte und löscht die Quelle. Anfrage~B findet beim nächsten Objekt die Quelle nicht mehr vor. Ihre Fehlerbehandlung entfernt die eigenen Teilkopien — dieselben Objekte, die A soeben geschrieben hat. Da die Quelle gelöscht ist, existiert keine Kopie mehr. Das Ergebnis ist Datenverlust.
|
||||||
|
|
||||||
Das Ergebnis ist endgültiger Datenverlust. Zusätzlich liefert B anschließend ein Präfix zurück, unter dem nichts mehr liegt. Die Fehlerbehandlung, die Datenverlust verhindern sollte, verursacht ihn.
|
|
||||||
|
|
||||||
\subsection{Szenario B: Vermischung zweier Mandanten}
|
\subsection{Szenario B: Vermischung zweier Mandanten}
|
||||||
|
|
||||||
Das zweite Szenario betrifft das Anlegen. Der Marker wird ohne Bedingung geschrieben. Haben zwei Organisationen denselben Firmennamen und greifen beide erstmals gleichzeitig zu, sehen beide den Zielnamen als frei an, und beide legen den Marker an. Der zuletzt geschriebene gewinnt.
|
Der Marker wird ohne Bedingung geschrieben. Haben zwei Organisationen denselben Firmennamen und greifen erstmals gleichzeitig zu, legen beide den Marker an; der zuletzt geschriebene gewinnt. Beide arbeiten anschließend im selben Ordner, obwohl das Metadatum nur einer gehört. Der Folgeaufruf korrigiert sich zwar selbst, doch zwischenzeitlich Abgelegtes verbleibt im fremden Ordner.
|
||||||
|
|
||||||
Beide Anfragen arbeiten anschließend in demselben Ordner weiter, obwohl das Metadatum nur einer von ihnen gehört. Bis zum nächsten Aufruf sieht die unterlegene Organisation Dokumente in einem Ordner, der laut Metadatum der anderen zugeordnet ist. Der Folgeaufruf korrigiert sich zwar selbst — die unterlegene Organisation erkennt dann den fremden Marker und erhält den um die Organisations-ID ergänzten Namen —, doch was zwischenzeitlich abgelegt wurde, verbleibt im fremden Ordner und ist dort für den falschen Kunden sichtbar.
|
Von den beiden Szenarien ist dieses das schwerwiegendere: Datenverlust ist ein Betriebsproblem, die Offenlegung von Kundendokumenten ein Vertraulichkeitsproblem.
|
||||||
|
|
||||||
Von den beiden Szenarien ist dieses das schwerwiegendere: Datenverlust ist ein Betriebsproblem, die Offenlegung von Kundendokumenten gegenüber einem anderen Kunden ein Vertraulichkeitsproblem.
|
|
||||||
|
|
||||||
\subsection{Abgegrenzte Fälle}
|
\subsection{Abgegrenzte Fälle}
|
||||||
|
|
||||||
Zur Eingrenzung wurde geprüft, welche nebenläufigen Abläufe \emph{nicht} betroffen sind. Zwei Anfragen derselben Organisation, die denselben Ordner anlegen, erzeugen identische Schlüssel mit identischen Metadaten und sind harmlos. Zwei Anfragen, die dieselbe Umbenennung vollständig ausführen, erzeugen inhaltsgleiche Kopien; ein Löschen bereits gelöschter Objekte ist unproblematisch. Auch der Fall, dass eine Anfrage liest, während eine andere verschiebt, ist abgedeckt: Da der Marker zuletzt kopiert wird und zusätzlich geprüft wird, ob am Ziel bereits Objekte liegen, wird ein halb gefülltes Ziel nicht als gültiger Kundenordner erkannt.
|
Zur Eingrenzung wurde geprüft, welche nebenläufigen Abläufe \emph{nicht} betroffen sind. Zwei Anfragen derselben Organisation erzeugen identische Schlüssel und sind harmlos. Lesen während einer Verschiebung ist abgedeckt: Da der Marker zuletzt kopiert wird, wird ein halb gefülltes Ziel nicht als Kundenordner erkannt. Das Problem beschränkt sich auf zwei \emph{unterschiedlich weit fortgeschrittene} verändernde Vorgänge.
|
||||||
|
|
||||||
Diese Abgrenzung ist für die Bewertung wesentlich. Sie zeigt, dass die getroffenen Vorkehrungen wirken und das Problem auf den Fall zweier \emph{unterschiedlich weit fortgeschrittener} verändernder Vorgänge beschränkt ist.
|
|
||||||
|
|
||||||
\subsection{Erwogene Gegenmaßnahmen}
|
\subsection{Erwogene Gegenmaßnahmen}
|
||||||
|
|
||||||
Vier Ansätze wurden formuliert:
|
Vier Ansätze wurden formuliert:
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
\item \textbf{Absicherung der Fehlerbehandlung.} Vor dem Entfernen der Teilkopien wird geprüft, ob der Marker der Quelle noch existiert. Fehlt er, hat ein anderer Vorgang die Umbenennung bereits abgeschlossen, und es darf nichts gelöscht werden. Das beseitigt Szenario~A weitgehend und verschiebt den verbleibenden Fehler in die ungefährliche Richtung: Es bleiben überzählige Objekte zurück statt Daten verloren zu gehen.
|
\item \textbf{Absicherung der Fehlerbehandlung.} Vor dem Entfernen der Teilkopien wird geprüft, ob der Marker der Quelle noch existiert. Fehlt er, hat ein anderer Vorgang die Umbenennung abgeschlossen, und es darf nichts gelöscht werden. Das beseitigt Szenario~A weitgehend: Es bleiben überzählige Objekte zurück statt Daten verloren zu gehen.
|
||||||
\item \textbf{Bedingtes Schreiben.} Wird der Marker nur unter der Bedingung geschrieben, dass er noch nicht existiert, ist das Beanspruchen eines Namens unteilbar und Szenario~B ausgeschlossen \autocite{aws-conditional-writes}. Voraussetzung ist, dass der eingesetzte Speicher diese vergleichsweise junge Erweiterung unterstützt — was angesichts der Erfahrungen mit anderen Funktionen ausdrücklich nachzuweisen wäre.
|
\item \textbf{Bedingtes Schreiben.} Wird der Marker nur unter der Bedingung geschrieben, dass er noch nicht existiert, ist das Beanspruchen eines Namens unteilbar und Szenario~B ausgeschlossen \autocite{aws-conditional-writes}. Voraussetzung ist, dass der Speicher diese vergleichsweise junge Erweiterung unterstützt — was ausdrücklich nachzuweisen wäre.
|
||||||
\item \textbf{Instanzübergreifende Sperre.} Eine über die Datenbank realisierte Sperre je Organisation würde den verändernden Teil sauber serialisieren, kostet aber einen zusätzlichen Zugriff je Anfrage und erfordert ein Konzept für den Fall, dass eine Sperre nicht freigegeben wird.
|
\item \textbf{Instanzübergreifende Sperre.} Eine über die Datenbank realisierte Sperre je Organisation würde den verändernden Teil sauber serialisieren, kostet aber einen zusätzlichen Zugriff je Anfrage und erfordert ein Konzept für den Fall, dass eine Sperre nicht freigegeben wird.
|
||||||
\item \textbf{Verlagerung aus dem Anfragepfad.} Der verändernde Teil wird gar nicht mehr im Rahmen einer Benutzeranfrage ausgeführt. Bestandsordner werden einmalig kontrolliert migriert, und die Anwendung protokolliert lediglich, wenn ein Ordner vom Sollzustand abweicht. Damit verschwindet die Ursache vollständig statt abgesichert zu werden.
|
\item \textbf{Verlagerung aus dem Anfragepfad.} Bestandsordner werden einmalig kontrolliert migriert, und die Anwendung protokolliert lediglich, wenn ein Ordner vom Sollzustand abweicht. Damit verschwindet die Ursache vollständig statt abgesichert zu werden.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
Der erste Ansatz sollte in jedem Fall umgesetzt werden, unabhängig davon, wie die eigentliche Serialisierung gelöst wird. Der vierte ist insofern bemerkenswert, als er keine technische, sondern eine organisatorische Lösung darstellt — er beseitigt das Problem, indem er den verändernden Vorgang aus dem nebenläufigen Kontext herausnimmt.
|
Der erste Ansatz sollte unabhängig von der Serialisierungslösung umgesetzt werden. Der vierte ist bemerkenswert, da er eine organisatorische statt technische Lösung darstellt.
|
||||||
|
|
||||||
\subsection{Umgang mit dem Befund}
|
\subsection{Umgang mit dem Befund}
|
||||||
|
|
||||||
Der Befund wurde als eigenes Backlog Item erfasst, mit erhöhter Priorität versehen und ausdrücklich vom laufenden Pull Request abgegrenzt. Diese Abgrenzung erfolgte nach Absprache im Team und wurde am Item dokumentiert. Das Team versah es mit einem Zeitrahmen von vier bis sechs Stunden.
|
Der Befund wurde als eigenes Backlog Item mit erhöhter Priorität erfasst und vom laufenden Pull Request abgegrenzt. Das Team versah es mit einem Zeitrahmen von vier bis sechs Stunden.
|
||||||
|
|
||||||
Die Alternative wäre gewesen, den Pull Request so lange offenzuhalten, bis auch die Nebenläufigkeit gelöst ist. Dagegen sprach, dass die Wahl der Gegenmaßnahme selbst eine offene Frage ist: Ob bedingtes Schreiben zur Verfügung steht, ließ sich angesichts der Erfahrungen mit anderen Speicherfunktionen nicht ohne Rückfrage beim Betreiber beantworten, und deren Bearbeitungsdauer war zuvor mit mehreren Wochen bemessen worden. Der Lookup selbst — die eigentliche Verbesserung — wäre dadurch blockiert worden, obwohl er den Zustand gegenüber vorher bereits deutlich verbessert.
|
Den Pull Request offenzuhalten, bis die Nebenläufigkeit gelöst ist, hätte den Lookup blockiert: Ob bedingtes Schreiben verfügbar ist, ließ sich ohne Rückfrage beim Betreiber nicht beantworten, und deren Bearbeitungsdauer war zuvor mit mehreren Wochen bemessen worden. Der Lookup verbessert den Zustand gegenüber vorher bereits deutlich.
|
||||||
|
|
||||||
Hinzu kommt, dass der verändernde Pfad ausschließlich beim ersten Zugriff einer Organisation durchlaufen wird. Nach der einmaligen Selbstheilung ist er für diesen Kunden dauerhaft irrelevant. Das Risikofenster ist damit eng begrenzt — es besteht pro Kunde genau einmal.
|
Hinzu kommt, dass der verändernde Pfad nur beim ersten Zugriff einer Organisation durchlaufen wird. Das Risikofenster besteht pro Kunde genau einmal.
|
||||||
|
|
||||||
Die Akzeptanzkriterien des Folgeitems halten fest, dass eine reine Sperre innerhalb einer Instanz ausdrücklich nicht als Lösung akzeptiert wird und dass beide Szenarien durch Unit-Tests nachzubilden sind, die ohne die Korrektur fehlschlagen.
|
Die Akzeptanzkriterien des Folgeitems halten fest, dass eine reine Sperre innerhalb einer Instanz nicht als Lösung akzeptiert wird und dass beide Szenarien durch Unit-Tests nachzubilden sind.
|
||||||
|
|||||||
@@ -3,30 +3,24 @@
|
|||||||
|
|
||||||
\subsection{Zugriff über das AWS SDK}
|
\subsection{Zugriff über das AWS SDK}
|
||||||
|
|
||||||
Der Zugriff auf den Speicher erfolgt über das AWS SDK für .NET. Obwohl der Speicher nicht bei Amazon betrieben wird, ist dies der naheliegende Weg: StorageGRID implementiert die S3-Schnittstelle, und das SDK lässt sich über die Angabe einer abweichenden Dienstadresse auf einen beliebigen kompatiblen Endpunkt richten. Eine eigene Implementierung der Protokolldetails — insbesondere der Signaturberechnung — wäre aufwendig und fehleranfällig gewesen.
|
Der Zugriff auf den Speicher erfolgt über das AWS SDK für .NET. Obwohl der Speicher nicht bei Amazon betrieben wird, ist dies der naheliegende Weg: StorageGRID implementiert die S3-Schnittstelle, und das SDK lässt sich über eine abweichende Dienstadresse auf einen beliebigen kompatiblen Endpunkt richten. Eine eigene Implementierung — insbesondere der Signaturberechnung — wäre aufwendig und fehleranfällig gewesen.
|
||||||
|
|
||||||
Der \texttt{S3DocumentsClient} kapselt die verwendeten Operationen. Er ist bewusst schmal gehalten und bietet nur die tatsächlich benötigten Zugriffe an: das Auflisten von Objekten unterhalb eines Präfixes, das Abrufen der Metadaten eines einzelnen Objekts, das Erzeugen zeitlich begrenzter Zugriffs-URLs, das Lesen eines Objektinhalts sowie — für die Ordnerverwaltung — das Anlegen, Kopieren und Löschen von Objekten.
|
Der \texttt{S3DocumentsClient} kapselt die verwendeten Operationen: Auflisten, Metadatenabruf, Erzeugen zeitlich begrenzter Zugriffs-URLs, Lesen von Objektinhalten sowie Anlegen, Kopieren und Löschen von Objekten. Diese Kapselung hält die SDK-spezifischen Typen aus der fachlichen Schicht und ermöglicht in Tests ein Mock (siehe Abschnitt~\ref{sec:unit-tests}).
|
||||||
|
|
||||||
Diese Kapselung erfüllt zwei Zwecke. Zum einen hält sie die SDK-spezifischen Anfrage- und Antworttypen aus der fachlichen Schicht heraus. Zum anderen ist sie die Voraussetzung dafür, den Speicherzugriff in Tests durch ein Mock zu ersetzen (siehe Abschnitt~\ref{sec:unit-tests}).
|
|
||||||
|
|
||||||
\subsection{Konfiguration mehrerer Speicher}
|
\subsection{Konfiguration mehrerer Speicher}
|
||||||
|
|
||||||
Eine Besonderheit ergab sich daraus, dass Houston bereits vor diesem Projekt einen S3-Speicher verwendete — für die Anbindung des Dokumentationssystems. Mit dem Dokumentenbereich kam ein zweiter, davon unabhängiger Speicher hinzu, mit eigenem Bucket und eigenen Zugangsdaten.
|
Houston verwendete bereits vor diesem Projekt einen S3-Speicher für das Dokumentationssystem. Mit dem Dokumentenbereich kam ein zweiter, davon unabhängiger Speicher hinzu.
|
||||||
|
|
||||||
In der ersten Fassung wurden die Einstellungen des neuen Speichers als eigenständiger Satz von Konfigurationswerten geführt. Im Review wurde angeregt, stattdessen eine gemeinsame Struktur für S3-Einstellungen zu verwenden und die beiden Verwendungen über benannte Registrierungen im Dienstcontainer auseinanderzuhalten \autocite{dotnet-keyed-di}.
|
In der ersten Fassung wurden die Einstellungen als eigenständiger Konfigurationssatz geführt. Im Review wurde angeregt, eine gemeinsame Struktur für S3-Einstellungen zu verwenden und die Verwendungen über benannte Registrierungen im Dienstcontainer auseinanderzuhalten \autocite{dotnet-keyed-di}. Ein dritter Speicher erfordert so lediglich einen weiteren Konfigurationsabschnitt und eine Registrierung, nicht aber eine Verdopplung der Einstellungsklassen.
|
||||||
|
|
||||||
Der Vorteil dieser Lösung liegt in der Erweiterbarkeit: Ein dritter Speicher erfordert lediglich einen weiteren Konfigurationsabschnitt und eine weitere Registrierung, nicht aber eine erneute Verdopplung der Einstellungsklassen. Zudem ist an der Registrierung unmittelbar ablesbar, welcher Programmteil auf welchen Speicher zugreift — bei zwei gleichartig benannten Konfigurationssätzen wäre diese Zuordnung nur aus dem Kontext erkennbar gewesen.
|
|
||||||
|
|
||||||
\subsection{Umgebungen und Zugangsdaten}
|
\subsection{Umgebungen und Zugangsdaten}
|
||||||
|
|
||||||
Für die drei Umgebungen existiert je ein eigener Bucket. Die Trennung erfolgt damit nicht über Präfixe innerhalb eines gemeinsamen Buckets, sondern über getrennte Buckets mit getrennten Zugangsdaten. Ein fehlerhaft konfigurierter Entwicklungsstand kann dadurch nicht auf Produktivdaten zugreifen.
|
Für die drei Umgebungen existiert je ein eigener Bucket mit getrennten Zugangsdaten, sodass ein fehlerhaft konfigurierter Entwicklungsstand nicht auf Produktivdaten zugreifen kann. Die Zugangsdaten liegen nicht im Quelltext, sondern werden über die Konfigurationsmechanismen der Anwendung bereitgestellt und im unternehmensweiten Passwortmanager hinterlegt.
|
||||||
|
|
||||||
Die Zugangsdaten selbst liegen nicht im Quelltext, sondern werden über die Konfigurationsmechanismen der Anwendung bereitgestellt; hinterlegt sind sie im unternehmensweiten Passwortmanager. Für die lokale Entwicklung wurden die Entwicklungseinstellungen um die entsprechenden Werte ergänzt.
|
|
||||||
|
|
||||||
\subsection{Auflisten von Objekten}
|
\subsection{Auflisten von Objekten}
|
||||||
|
|
||||||
Die zentrale Leseoperation ist das Auflisten von Objekten unterhalb eines Präfixes. Sie wird mit dem Kundenpräfix aufgerufen und liefert sämtliche Objekte des jeweiligen Kunden — über alle Typordner hinweg, da die Anzeige eine flache Liste ist.
|
Die zentrale Leseoperation listet Objekte unterhalb eines Präfixes auf. Sie wird mit dem Kundenpräfix aufgerufen und liefert sämtliche Objekte über alle Typordner hinweg.
|
||||||
|
|
||||||
Zwei Eigenschaften dieser Operation prägten die Umsetzung. Erstens liefert sie Ergebnisse blockweise: Überschreitet die Trefferzahl eine bestimmte Größe, wird ein Fortsetzungsmerkmal zurückgegeben, mit dem der nächste Block abgerufen werden kann \autocite{aws-listobjectsv2}. Diese Eigenschaft wurde für die Paginierung genutzt (siehe Abschnitt~\ref{sec:filter-pagination}). Zweitens liefert sie zwar Schlüssel, Größe und Änderungszeitpunkt, aber keine benutzerdefinierten Metadaten. Genau diese Einschränkung ist die Ursache des in Abschnitt~\ref{sec:lookup-research} beschriebenen Lookup-Problems.
|
Zwei Eigenschaften prägten die Umsetzung. Erstens liefert die Operation Ergebnisse blockweise: Überschreitet die Trefferzahl eine Grenze, wird ein Fortsetzungsmerkmal zurückgegeben \autocite{aws-listobjectsv2}. Diese Eigenschaft wurde für die Paginierung genutzt (siehe Abschnitt~\ref{sec:filter-pagination}). Zweitens liefert sie keine benutzerdefinierten Metadaten — die Ursache des in Abschnitt~\ref{sec:lookup-research} beschriebenen Lookup-Problems.
|
||||||
|
|
||||||
Beim Auflisten werden zwei Arten von Einträgen herausgefiltert. Zum einen die Platzhalterobjekte, die die Typordner repräsentieren — sie sind technisch Objekte, fachlich aber keine Dokumente. Zum anderen der Marker des Kundenordners selbst. Beide erkennt der Dienst daran, dass ihr Schlüssel auf das Trennzeichen endet.
|
Beim Auflisten werden Platzhalterobjekte der Typordner und der Marker des Kundenordners herausgefiltert. Beide erkennt der Dienst daran, dass ihr Schlüssel auf das Trennzeichen endet.
|
||||||
|
|||||||
@@ -3,26 +3,20 @@
|
|||||||
|
|
||||||
\subsection{Umsetzung}
|
\subsection{Umsetzung}
|
||||||
|
|
||||||
Die Suche filtert die Dokumentenliste anhand des Dokumentnamens und berücksichtigt dabei Teiltreffer. Ein geleertes Suchfeld stellt die vollständige Liste wieder her. Fachlich beschränkt sie sich bewusst auf den Namen: Im Approval-Termin wurde gefragt, ob auch eine Beschreibung durchsucht werden solle; da Dokumente im Speicher keine Beschreibung tragen, wurde die Suche auf den Namen begrenzt.
|
Die Suche filtert die Dokumentenliste anhand des Namens und berücksichtigt Teiltreffer. Ein geleertes Suchfeld stellt die vollständige Liste wieder her. Im Approval-Termin wurde gefragt, ob auch eine Beschreibung durchsucht werden solle; da Dokumente keine tragen, wurde die Suche auf den Namen begrenzt.
|
||||||
|
|
||||||
\subsection{Der Begriff „serverseitig"}
|
\subsection{Der Begriff „serverseitig"}
|
||||||
|
|
||||||
Die Anforderung verlangt eine serverseitige Suche. Dieser Begriff bedarf in diesem Zusammenhang einer Präzisierung, weil er auf zwei verschiedene Ebenen zutreffen kann.
|
Die Anforderung verlangt eine serverseitige Suche. Gemeint ist, dass die Filterung in der Anwendung stattfindet, nicht im Browser — der Browser erhält die bereits gefilterte Menge. Eine Filterung im Browser würde voraussetzen, dass sämtliche Dokumente zuvor übertragen wurden, was mit der Paginierung unvereinbar wäre.
|
||||||
|
|
||||||
Gemeint und umgesetzt ist, dass die Filterung in der Anwendung stattfindet und nicht im Browser des Benutzers. Der Browser erhält also bereits die gefilterte Menge. Das ist die für die Anforderung entscheidende Eigenschaft, denn eine Filterung im Browser würde voraussetzen, dass sämtliche Dokumente zuvor übertragen wurden — was mit der Paginierung unvereinbar wäre und bei großen Beständen unnötig Daten überträgt.
|
Nicht umgesetzt ist eine Filterung durch den \emph{Speicher}. Houston listet die Objekte auf und filtert die Namen anschließend selbst. Im Review stellte \emph{Hanna Ebner} die Frage, ob sich das nicht direkt über die Schnittstelle lösen lasse, und musste verneint werden.
|
||||||
|
|
||||||
Nicht umgesetzt — und mit den verfügbaren Mitteln nicht umsetzbar — ist eine Filterung durch den \emph{Speicher}. Houston listet die Objekte des Kundenordners auf und filtert die zurückgelieferten Namen anschließend selbst. Die Frage, ob sich das nicht direkt über die Schnittstelle lösen lasse, wurde im Review von \emph{Hanna Ebner} gestellt und musste verneint werden.
|
|
||||||
|
|
||||||
\subsection{Warum keine Filterung im Speicher möglich war}
|
\subsection{Warum keine Filterung im Speicher möglich war}
|
||||||
|
|
||||||
Im weiteren Verlauf des Reviews brachte \emph{Timo Walter} die zuvor gemeinsam betrachtete Abfragefunktion des Speichers ins Spiel. Die Prüfung ergab, dass diese Funktion Objekt\emph{inhalte} filtert und nicht Objektnamen — sie ist dafür gedacht, aus einer strukturierten Datei einzelne Datensätze auszuwählen \autocite{aws-s3-select}. Für eine Namenssuche über mehrere Objekte hinweg ist sie nicht geeignet.
|
Im weiteren Verlauf brachte \emph{Timo Walter} die Abfragefunktion des Speichers ins Spiel. Die Prüfung ergab, dass diese Objektinhalte filtert, nicht Objektnamen — sie wählt aus strukturierten Dateien einzelne Datensätze aus \autocite{aws-s3-select}.
|
||||||
|
|
||||||
Als zweite Möglichkeit wurde der Suchdienst des Speicherherstellers erwogen, der Objektmetadaten in einen durchsuchbaren Index spiegelt \autocite{storagegrid-search-integration}. Zum Zeitpunkt des Reviews stand dessen Verfügbarkeit noch nicht fest; die Anfrage über den Betreiber lief bereits (siehe Abschnitt~\ref{sec:infrastructure}). Die spätere Antwort fiel negativ aus.
|
Als zweite Möglichkeit wurde der Suchdienst des Speicherherstellers erwogen \autocite{storagegrid-search-integration}. Dessen Verfügbarkeit stand noch nicht fest; die Anfrage lief bereits (siehe Abschnitt~\ref{sec:infrastructure}). Die spätere Antwort fiel negativ aus. Auf Anregung des Reviewers wurde dieser Umstand in das Recherche-Item aufgenommen.
|
||||||
|
|
||||||
Auf Anregung des Reviewers wurde dieser Umstand nicht nur im Gesprächsverlauf festgehalten, sondern ausdrücklich in das Recherche-Item aufgenommen. Damit war die Suche nicht länger eine Funktion mit einer unausgesprochenen Schwäche, sondern eine Funktion mit einer dokumentierten und einem Folgeitem zugeordneten Einschränkung.
|
|
||||||
|
|
||||||
\subsection{Bewertung}
|
\subsection{Bewertung}
|
||||||
|
|
||||||
Die praktische Auswirkung ist derzeit gering. Die Zahl der Dokumente je Kunde bewegt sich in einer Größenordnung, in der das Auflisten und anschließende Filtern nicht spürbar ins Gewicht fällt — anders als beim Kundenordner-Lookup, der über \emph{alle} Kunden iterierte und daher mit der Kundenzahl skalierte. Die Suche skaliert dagegen nur mit der Dokumentenzahl eines einzelnen Kunden.
|
Die praktische Auswirkung ist derzeit gering. Die Dokumentenzahl je Kunde ist überschaubar; die Suche skaliert nur mit der Dokumentenzahl eines einzelnen Kunden, nicht mit der Kundenzahl. Der Lookup wurde daher im Projektzeitraum gelöst, die Suchoptimierung blieb für den Ausblick.
|
||||||
|
|
||||||
Damit war die Priorisierung klar: Der Lookup wurde noch im Projektzeitraum gelöst, die Suchoptimierung blieb ein Thema für den Ausblick.
|
|
||||||
|
|||||||
@@ -3,16 +3,14 @@
|
|||||||
|
|
||||||
\subsection{Eigene Symbole statt Symbolschrift}
|
\subsection{Eigene Symbole statt Symbolschrift}
|
||||||
|
|
||||||
Houston verwendet für Symbole eine gängige Symbolbibliothek. Für die sieben Dokumententypen enthielt diese jedoch keine passenden Motive — Begriffe wie „SLA-Report Ticketbearbeitung" oder „Security Assessment" lassen sich mit allgemeinen Symbolen nicht sinnvoll unterscheiden. Es wurden daher eigene Vektorgrafiken erstellt.
|
Houston verwendet für Symbole eine gängige Symbolbibliothek. Für die sieben Dokumententypen enthielt diese keine passenden Motive — Begriffe wie „SLA-Report Ticketbearbeitung" lassen sich mit allgemeinen Symbolen nicht unterscheiden. Es wurden daher eigene Vektorgrafiken erstellt.
|
||||||
|
|
||||||
Damit stellte sich die Frage, wie diese in die bestehende Oberfläche eingebunden werden. Eine Einbindung als gewöhnliche Grafik hätte bedeutet, dass die Symbole eine feste Farbe tragen. Houston unterstützt jedoch ein helles und ein dunkles Erscheinungsbild, und die umgebenden Symbole der Bibliothek passen sich der Textfarbe an. Fest eingefärbte Symbole wären im dunklen Erscheinungsbild kaum sichtbar gewesen.
|
Eine Einbindung als gewöhnliche Grafik hätte eine feste Farbe bedeutet. Houston unterstützt jedoch ein helles und ein dunkles Erscheinungsbild, und fest eingefärbte Symbole wären im dunklen kaum sichtbar gewesen.
|
||||||
|
|
||||||
\subsection{Einfärbung über Masken}
|
\subsection{Einfärbung über Masken}
|
||||||
|
|
||||||
Gelöst wurde dies, indem die Vektorgrafiken nicht als Bild eingebunden, sondern als Maske über einer Hintergrundfläche verwendet werden. Die Fläche übernimmt dabei die aktuelle Textfarbe, sodass sich das Symbol automatisch an das Erscheinungsbild anpasst — genau wie die Symbole der Bibliothek.
|
Gelöst wurde dies, indem die Vektorgrafiken als Maske über einer Hintergrundfläche dienen. Die Fläche übernimmt die aktuelle Textfarbe, sodass sich das Symbol automatisch anpasst. Die eigenen Symbole lassen sich über dieselben Gestaltungsklassen ansprechen wie die vorhandenen. Alle Grafiken müssen dieselben Abmessungen haben; im Review fiel auf, dass eine abwich, und die Maße wurden angeglichen.
|
||||||
|
|
||||||
Der Ansatz hat den zusätzlichen Vorteil, dass sich die eigenen Symbole über dieselben Gestaltungsklassen ansprechen lassen wie die vorhandenen. Für die Verwendung in der Seite ist damit nicht erkennbar, ob ein Symbol aus der Bibliothek stammt oder eigens erstellt wurde. Als Nebenbedingung ergab sich, dass alle Grafiken dieselben Abmessungen haben müssen, damit sie im Textfluss gleich ausgerichtet erscheinen. Im Review fiel auf, dass eine der Grafiken von den übrigen abwich; die Maße wurden angeglichen.
|
|
||||||
|
|
||||||
\subsection{Standardsymbol}
|
\subsection{Standardsymbol}
|
||||||
|
|
||||||
Dokumente ohne erkennbaren Typ — solche, die unmittelbar im Kundenordner liegen oder in einem unbekannten Unterordner — erhalten ein neutrales Standardsymbol. Damit ist sichergestellt, dass jede Zeile ein Symbol trägt und die Liste optisch gleichmäßig bleibt. Zugleich ist an der Darstellung erkennbar, dass für dieses Dokument keine Typzuordnung vorliegt, ohne dass dies wie ein Fehler wirkt.
|
Dokumente ohne erkennbaren Typ erhalten ein neutrales Standardsymbol. Damit trägt jede Zeile ein Symbol, und an der Darstellung ist erkennbar, dass keine Typzuordnung vorliegt, ohne dass dies wie ein Fehler wirkt.
|
||||||
|
|||||||
@@ -3,36 +3,26 @@
|
|||||||
|
|
||||||
\subsection{Anwendungsfall}
|
\subsection{Anwendungsfall}
|
||||||
|
|
||||||
Nicht alle Unterlagen, die für einen Kunden relevant sind, lassen sich sinnvoll in den Speicher kopieren. Ein fortlaufend gepflegtes Dokument in einem Dokumentenverwaltungssystem oder ein Kanal in einem Kollaborationswerkzeug soll verlinkt und nicht dupliziert werden — eine Kopie wäre sofort veraltet.
|
Nicht alle Unterlagen lassen sich sinnvoll in den Speicher kopieren. Ein fortlaufend gepflegtes Dokument oder ein Kanal in einem Kollaborationswerkzeug soll verlinkt werden — eine Kopie wäre sofort veraltet. Dafür werden \texttt{.url}-Dateien unterstützt: kleine Textdateien im INI-Format mit Zieladresse und optionalem Symbol. In der Liste erscheinen sie wie gewöhnliche Dokumente, führen beim Anklicken jedoch auf die hinterlegte Adresse.
|
||||||
|
|
||||||
Für diesen Zweck werden \texttt{.url}-Dateien unterstützt. Dabei handelt es sich um ein etabliertes Format für Verknüpfungen: eine kleine Textdatei, die im INI-Format die Zieladresse und optional ein Symbol enthält. Legt ein Kundenbetreuer eine solche Datei im Speicher ab, erscheint sie in der Dokumentenliste wie ein gewöhnliches Dokument, führt beim Anklicken jedoch auf die hinterlegte Adresse.
|
Der Anwendungsfall wurde in der Feature-Analyse geklärt: Gemeint ist das Einbetten \emph{externer} Ressourcen, nicht das Teilen von Houston-Dokumenten nach außen (siehe Abschnitt~\ref{sec:feature-analysis}).
|
||||||
|
|
||||||
Der Anwendungsfall war zunächst missverständlich formuliert und wurde in der Feature-Analyse geklärt: Gemeint ist das Einbetten \emph{externer} Ressourcen in den Dokumentenbereich, nicht das Teilen von Houston-Dokumenten nach außen (siehe Abschnitt~\ref{sec:feature-analysis}).
|
|
||||||
|
|
||||||
\subsection{Auswertung des Dateiformats}
|
\subsection{Auswertung des Dateiformats}
|
||||||
|
|
||||||
Für das Auswerten des INI-Formats wurde bewusst keine externe Bibliothek eingebunden, sondern eine kleine eigene Auswertung geschrieben. Der Grund ist das Verhältnis von Aufwand zu Nutzen: Benötigt wird ein einziger Wert aus einem einzigen Abschnitt. Eine allgemeine Bibliothek für ein Format, von dem ein sehr kleiner Ausschnitt gebraucht wird, brächte eine dauerhaft zu pflegende Abhängigkeit mit sich — samt Aktualisierungen, Lizenzprüfung und einer weiteren Position in der Abhängigkeitsliste — für Funktionalität, die in wenigen Zeilen selbst geschrieben ist.
|
Für das INI-Format wurde bewusst keine externe Bibliothek eingebunden. Benötigt wird ein einziger Wert aus einem Abschnitt. Eine Bibliothek brächte eine dauerhaft zu pflegende Abhängigkeit für Funktionalität, die in wenigen Zeilen selbst geschrieben ist. Zudem würde sie bei fehlerhaften Dateien vermutlich einen Fehler melden, während hier ein stiller Rückfall erforderlich ist.
|
||||||
|
|
||||||
Hinzu kommt, dass eine allgemeine Bibliothek in diesem Fall gar nicht das gewünschte Verhalten böte. Sie würde bei fehlerhaften Dateien vermutlich einen Fehler melden, während hier ein stiller Rückfall erforderlich ist (siehe unten). Die eigene Auswertung ist auf genau dieses Verhalten hin geschrieben.
|
|
||||||
|
|
||||||
\subsection{Rückfall auf normales Verhalten}
|
\subsection{Rückfall auf normales Verhalten}
|
||||||
|
|
||||||
Kann in einer \texttt{.url}-Datei keine gültige Adresse erkannt werden, wird sie wie eine gewöhnliche Datei behandelt und zum Herunterladen angeboten. Dieses Verhalten war ausdrücklich gefordert.
|
Kann keine gültige Adresse erkannt werden, wird die Datei wie eine gewöhnliche Datei zum Herunterladen angeboten. Eine fehlerhafte Verknüpfungsdatei darf das Dokument nicht unzugänglich machen.
|
||||||
|
|
||||||
Die Überlegung dahinter ist, dass eine fehlerhafte Verknüpfungsdatei kein Grund sein darf, das Dokument unzugänglich zu machen. Der Kunde erhält im schlechtesten Fall eine kleine Textdatei statt einer Weiterleitung — ein nachvollziehbares Ergebnis, aus dem sich zudem ablesen lässt, was schiefgegangen ist. Die Alternative, einen Fehler anzuzeigen oder den Eintrag auszublenden, wäre für den Kunden weniger hilfreich.
|
|
||||||
|
|
||||||
\subsection{Anzeigename}
|
\subsection{Anzeigename}
|
||||||
|
|
||||||
Der Dateiname einer Verknüpfung endet auf \texttt{.url}. Diese Endung ist für den Kunden bedeutungslos und würde in der Liste lediglich stören. Sie wird deshalb für die Anzeige entfernt.
|
Die Endung \texttt{.url} wird für die Anzeige entfernt. Im Review wies \emph{Robin Noack} darauf hin, dass die dafür verwendete Zeichenzahl als unmittelbare Zahl im Code stand; stattdessen sollte die Länge der Endung selbst verwendet werden, da Zahl und Endung an verschiedenen Stellen stehen und bei einer Änderung auseinanderlaufen können.
|
||||||
|
|
||||||
Im Review wies \emph{Robin Noack} darauf hin, dass die dafür verwendete Zeichenzahl als unmittelbare Zahl im Code stand, und schlug vor, stattdessen die Länge der Endung selbst zu verwenden. Der Hinweis ist auf den ersten Blick eine Kleinigkeit, trifft aber einen realen Fehlerfall: Zahl und Endung stehen an verschiedenen Stellen, und wird die eine geändert, bleibt die andere unbemerkt zurück.
|
|
||||||
|
|
||||||
\subsection{Symbole für Verknüpfungen}
|
\subsection{Symbole für Verknüpfungen}
|
||||||
|
|
||||||
Das INI-Format sieht die Angabe eines Symbols vor. Da benutzerdefinierte Symbole aus den in Abschnitt~\ref{sec:pdf-preview} genannten Gründen nicht ausgeliefert werden, wurde ein anderer Weg gewählt: Es stehen benannte Voreinstellungen zur Verfügung, die auf Symbole der in Houston vorhandenen Bibliothek verweisen. Für die häufigsten Ziele — die Dateiablage, das Dokumentenverwaltungssystem und das Kollaborationswerkzeug des Unternehmens — wurden eigene Symbole ergänzt.
|
Da benutzerdefinierte Symbole aus den in Abschnitt~\ref{sec:pdf-preview} genannten Gründen nicht ausgeliefert werden, stehen stattdessen benannte Voreinstellungen zur Verfügung, die auf Symbole der in Houston vorhandenen Bibliothek verweisen. Für die häufigsten Ziele — Dateiablage, Dokumentenverwaltungssystem und Kollaborationswerkzeug — wurden eigene Symbole ergänzt. Die verfügbaren Namen wurden im internen Wiki dokumentiert und im Pull Request darauf verwiesen.
|
||||||
|
|
||||||
Damit erhält ein Kundenbetreuer die gewünschte visuelle Unterscheidung, ohne dass Dateiinhalt an einer nicht authentifizierten Stelle verarbeitet werden muss. Da diese Voreinstellungen nur nützen, wenn sie bekannt sind, wurden die verfügbaren Namen im internen Wiki dokumentiert und im Pull Request darauf verwiesen.
|
|
||||||
|
|
||||||
\subsection{Ausschluss vom ZIP-Download}
|
\subsection{Ausschluss vom ZIP-Download}
|
||||||
|
|
||||||
Verknüpfungsdateien erhalten keine Auswahlbox und können damit nicht Teil eines Archivs werden. Der Grund ist, dass ein Archiv sonst eine Datei enthielte, die beim Öffnen nicht das erwartete Dokument liefert, sondern eine Verknüpfung, die möglicherweise nur innerhalb des Unternehmensnetzes auflösbar ist. Da eine Verknüpfung fachlich kein Dokument ist, sondern ein Verweis, wäre ihre Aufnahme in ein Dokumentenarchiv irreführend.
|
Verknüpfungsdateien erhalten keine Auswahlbox und können nicht Teil eines Archivs werden. Ein Archiv enthielte sonst eine Datei, die beim Öffnen eine möglicherweise nur intern auflösbare Verknüpfung liefert. Da eine Verknüpfung fachlich kein Dokument ist, wäre ihre Aufnahme irreführend.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
\section{Ausgangssituation}
|
\section{Ausgangssituation}
|
||||||
\label{sec:initial-situation}
|
\label{sec:initial-situation}
|
||||||
|
|
||||||
Die Idee, Kunden über Houston Zugang zu ihren Dokumenten zu ermöglichen, besteht seit November 2023: \emph{Nicole Kimmel} legte damals das Feature~484 „Dokumente" mit zwei Stichpunkten an — „Vertrag, Betriebshandbuch, Feinkonzepte an zentraler Stelle abgelegt" und „Rechnungen einsehbar". Diese Notiz blieb über zweieinhalb Jahre nahezu unverändert im Backlog und spiegelt die damaligen Bedürfnisse wider, ohne einen konkreten Lösungsansatz zu beschreiben.
|
Die Idee besteht seit November 2023: \emph{Nicole Kimmel} legte damals das Feature~484 „Dokumente" mit zwei Stichpunkten an — „Vertrag, Betriebshandbuch, Feinkonzepte an zentraler Stelle abgelegt" und „Rechnungen einsehbar". Diese Notiz blieb über zweieinhalb Jahre unverändert im Backlog, ohne einen konkreten Lösungsansatz zu beschreiben.
|
||||||
|
|
||||||
Zum Zeitpunkt des Projektbeginns im Sommer 2026 gab es keinen strukturierten Prozess, über den Kunden selbstständig auf ihre Dokumente zugreifen konnten. Verträge, Berichte und ähnliche Unterlagen wurden punktuell per E-Mail oder über Dateiablagen bereitgestellt. Daraus ergaben sich mehrere Probleme:
|
Zum Projektbeginn im Sommer 2026 wurden Verträge, Berichte und ähnliche Unterlagen punktuell per E-Mail oder Dateiablage bereitgestellt. Daraus ergaben sich mehrere Probleme:
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \textbf{Fehlende Zentralisierung:} Dokumente lagen verteilt in E-Mails, Dateiablagen und lokalen Verzeichnissen ohne einheitlichen Zugangspunkt.
|
\item \textbf{Fehlende Zentralisierung:} Dokumente lagen verteilt in E-Mails, Dateiablagen und lokalen Verzeichnissen ohne einheitlichen Zugangspunkt.
|
||||||
@@ -12,4 +12,4 @@ Zum Zeitpunkt des Projektbeginns im Sommer 2026 gab es keinen strukturierten Pro
|
|||||||
\item \textbf{Kein sicherer, mandantengetrennter Zugriff:} Es existierte kein technischer Mechanismus, der sicherstellte, dass ein Kunde ausschließlich seine eigenen Dokumente einsehen konnte.
|
\item \textbf{Kein sicherer, mandantengetrennter Zugriff:} Es existierte kein technischer Mechanismus, der sicherstellte, dass ein Kunde ausschließlich seine eigenen Dokumente einsehen konnte.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
Auf technischer Seite war kein geeigneter S3-Speicher provisioniert und die Houston-Anwendung kannte keine Verbindung zu einem externen Objektspeicher. Die Bereitstellung der notwendigen Infrastruktur — drei S3-Buckets (DEV, TEST, PROD) bei Advanced Unibyte auf Basis von NetApp StorageGRID — musste erst im Laufe des Projekts beantragt und eingerichtet werden.
|
Auf technischer Seite war kein S3-Speicher provisioniert. Die Bereitstellung der notwendigen Infrastruktur — drei S3-Buckets (DEV, TEST, PROD) bei Advanced Unibyte auf Basis von NetApp StorageGRID — musste erst im Projektverlauf beantragt werden.
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
\section{Projektbeteiligte}
|
\section{Projektbeteiligte}
|
||||||
\label{sec:participants}
|
\label{sec:participants}
|
||||||
|
|
||||||
Das Projekt wurde durch eine strukturierte Zusammenarbeit verschiedener Beteiligter mit klar definierten Rollen umgesetzt.
|
Als \textbf{Praktikant und Entwickler} (\emph{Linus Nagel}) war ich für Anforderungsklärung, Konzeption, Implementierung aller Product Backlog Items, Akzeptanzkriterien und Dokumentation verantwortlich.
|
||||||
|
|
||||||
In meiner Rolle als \textbf{Praktikant und Entwickler} (\emph{Linus Nagel}) war ich für die gesamte technische Umsetzung verantwortlich: Anforderungsklärung, Konzeption, Implementierung aller Product Backlog Items, das Verfassen der Akzeptanzkriterien und die Erstellung dieser Dokumentation.
|
\emph{Sarah Hinzmann} übernahm die \textbf{betriebliche Betreuung}, koordinierte Abstimmungen und war an Code-Reviews der Abschlussphase beteiligt. \emph{Thomas Drewermann} fungierte als \textbf{Product Owner}: Er arbeitete das Feature~484 im Juni 2026 aus, definierte den Umfang und beantwortete Rückfragen.
|
||||||
|
|
||||||
Die \textbf{betriebliche Betreuung} übernahm \emph{Sarah Hinzmann}. Sie koordinierte die Abstimmungen, begleitete das Projekt von der Themenvergabe bis zur Abgabe und war an Code-Reviews der Abschlussphase beteiligt.
|
Die \textbf{technische Qualitätssicherung} lag beim Team der Unicorn Development: \emph{Timo Walter} (Code-Reviews der frühen PRs, Architektur-Rückfragen), \emph{Robin Noack} (Reviews der Schlussphase), \emph{Hanna Ebner} (UI-Konzept und Clickdummy). \emph{Maria-Lena Andersz} führte die \textbf{Abnahmetests} durch.
|
||||||
|
|
||||||
Als \textbf{Product Owner und fachlicher Ansprechpartner} fungierte \emph{Thomas Drewermann}. Er arbeitete das Feature~484 im Juni 2026 vollständig aus, definierte den Umfang, beantwortete Rückfragen während der Feature-Analyse und begleitete den Projektverlauf fachlich.
|
Weitere Beteiligte: \emph{Stephan Janßen} (Schätzung, Backlog-Pflege), \emph{Christiana Sobik} (Backlog-Pflege), \emph{Bianco Veigel} (Sprint-Planung), \emph{Nicole Kimmel} (ursprüngliche Anforderung, 2023).
|
||||||
|
|
||||||
Die \textbf{technische Qualitätssicherung} übernahm das Entwicklerteam der Unicorn Development: \emph{Timo Walter} führte die Code-Reviews der frühen Pull Requests durch und stellte Rückfragen zu Architekturentscheidungen; \emph{Robin Noack} übernahm die Reviews in der Schlussphase. \emph{Hanna Ebner} erarbeitete das UI-Konzept und den Clickdummy. Das gesamte Team war an der Aufwandsschätzung der Product Backlog Items beteiligt.
|
|
||||||
|
|
||||||
Die \textbf{Abnahmetests} wurden durch \emph{Maria-Lena Andersz} durchgeführt.
|
|
||||||
|
|
||||||
Weitere Beteiligte in unterstützenden Rollen: \emph{Stephan Janßen} (Schätzung und Backlog-Pflege), \emph{Christiana Sobik} (Backlog-Pflege), \emph{Bianco Veigel} (Sprint-Planung), \emph{Nicole Kimmel} (ursprüngliche Anforderung, 2023).
|
|
||||||
|
|
||||||
\begin{table}[H]
|
\begin{table}[H]
|
||||||
\centering
|
\centering
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
\section{Projektbeschreibung}
|
\section{Projektbeschreibung}
|
||||||
\label{sec:project-description}
|
\label{sec:project-description}
|
||||||
|
|
||||||
Das Projekt \emph{Houston Dokumente} hat zum Ziel, Kunden im Kundenportal Houston einen zentralen Bereich bereitzustellen, in dem sie ihre Dokumente einsehen und herunterladen können. Die Dokumente werden in einem S3-Speichersystem abgelegt und gepflegt; den Kunden werden sie über eine neue Houston-Seite zugänglich gemacht.
|
Das Projekt \emph{Houston Dokumente} stellt Kunden im Kundenportal Houston einen zentralen Bereich bereit, in dem sie ihre Dokumente einsehen und herunterladen können. Die Dokumente werden in einem S3-Speicher abgelegt; Mitarbeiter pflegen sie über das interne Dateiverwaltungswerkzeug Filestash, Kunden rufen sie über eine neue Houston-Seite strukturiert ab.
|
||||||
|
|
||||||
Bisher existierte kein einheitlicher, zentraler Zugangspunkt für kundenbezogene Dokumente wie Verträge, Berichte oder Protokolle. Diese wurden punktuell per E-Mail oder Dateiablage bereitgestellt und waren für Kunden nicht selbstständig abrufbar. Die neue Dokumentenseite in Houston löst diese Situation ab: Mitarbeiter pflegen die Dokumente über ein internes Dateiverwaltungswerkzeug direkt im S3-Speicher, Kunden können sie anschließend strukturiert abrufen.
|
Der Dokumentenbereich unterscheidet sieben fachlich definierte Dokumententypen — darunter Service-Protokolle, SLA-Reports, Monitoring Reports und Vertragsunterlagen. Weitere Funktionen sind Suche, Typfilter, Paginierung, PDF-Viewer, Einzel- und ZIP-Download sowie URL-Dateien, mit denen beliebige Webadressen in der Dokumentenliste verknüpft werden können.
|
||||||
|
|
||||||
Der Dokumentenbereich unterscheidet sieben fachlich definierte Dokumententypen — darunter Service-Protokolle, SLA-Reports, Monitoring Reports und Vertragsunterlagen — und gliedert die Anzeige anhand dieser Typen. Darüber hinaus umfasst das Projekt eine Suchfunktion, Filter nach Dokumententyp, Paginierung, einen PDF-Viewer, den Download einzelner Dateien sowie das Herunterladen mehrerer Dokumente als ZIP-Archiv. Zusätzlich werden sogenannte URL-Dateien unterstützt, mit denen beliebige Webadressen — etwa Links zu Teams-Kanälen oder SharePoint-Seiten — in der Dokumentenliste verknüpft werden können.
|
Technisch umfasst das Projekt die Anbindung an den S3-Speicher über das AWS SDK für .NET, ein rollenbasiertes Berechtigungskonzept über Microsoft Entra~ID sowie die automatische Anlage der Ordnerstruktur beim ersten Seitenaufruf. Schreibzugriffe durch Kunden sind nicht vorgesehen; dies bleibt Aufgabe der internen Mitarbeiter.
|
||||||
|
|
||||||
Das Projekt umfasst die vollständige Integration des Dokumentenbereichs in die bestehende Houston-Webanwendung. Dazu zählen die Anbindung an den S3-Speicher über das AWS SDK für .NET, ein rollenbasiertes Berechtigungskonzept über Microsoft Entra~ID sowie die automatische Anlage der Ordnerstruktur für neue Kunden beim ersten Seitenaufruf. Nicht Bestandteil des Projekts sind Schreibzugriffe durch Kunden sowie die Anlage oder Bearbeitung von Dokumenten durch den Kunden selbst; dies bleibt Aufgabe der internen Mitarbeiter über das Dateiverwaltungswerkzeug.
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
Folgende Punkte sind explizit \emph{nicht} Bestandteil des Projekts:
|
Folgende Punkte sind explizit \emph{nicht} Bestandteil des Projekts:
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \textbf{Kein Schreibzugriff für Kunden:} Kunden können Dokumente ausschließlich einsehen und herunterladen. Das Hochladen, Bearbeiten oder Löschen von Dokumenten durch den Kunden ist nicht vorgesehen.
|
\item \textbf{Kein Schreibzugriff für Kunden:} Kunden können Dokumente ausschließlich einsehen und herunterladen.
|
||||||
\item \textbf{Kein internes Upload-UI in Houston:} Das Hochladen und Verwalten von Dokumenten durch WorkSimple-Mitarbeiter erfolgt ausschließlich über Filestash, einen externen S3-Browser. Eine eigene Upload-Oberfläche in Houston wurde nicht entwickelt.
|
\item \textbf{Kein internes Upload-UI in Houston:} Die Dokumentenverwaltung durch Mitarbeiter erfolgt ausschließlich über Filestash.
|
||||||
\item \textbf{Keine freie Ordnerstruktur intern:} Interne Mitarbeiter können über Filestash keine beliebigen Ordner anlegen, die dem Kunden angezeigt werden. Nur die sieben definierten Typordner sind für Kunden sichtbar; weitere Ordner werden ignoriert.
|
\item \textbf{Keine freie Ordnerstruktur intern:} Nur die sieben definierten Typordner sind für Kunden sichtbar; weitere Ordner werden ignoriert.
|
||||||
\item \textbf{Keine Dashboard-Kacheln im PidI-Umfang:} Geplante Erweiterungen wie Dashboard-Kacheln für Tenant-Härtung aus Security-Assessment-Metadaten oder eine Anzeige zuletzt hinzugefügter Dokumente sind als Feature-Creep im Backlog erfasst, aber nicht Teil des PidI-Projekts.
|
\item \textbf{Keine Dashboard-Kacheln im PidI-Umfang:} Geplante Erweiterungen wie Dashboard-Kacheln für Tenant-Härtung aus Security-Assessment-Metadaten oder eine Anzeige zuletzt hinzugefügter Dokumente sind als Feature-Creep im Backlog erfasst, aber nicht Teil des PidI-Projekts.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
\section{Systemlandschaft}
|
\section{Systemlandschaft}
|
||||||
\label{sec:system-landscape}
|
\label{sec:system-landscape}
|
||||||
|
|
||||||
Der Dokumentenbereich ist in die bestehende Systemlandschaft von WorkSimple eingebettet. Abbildung~\ref{fig:system-context} zeigt den Systemkontext und das Zusammenspiel der beteiligten Systeme.
|
Abbildung~\ref{fig:system-context} zeigt den Systemkontext des Dokumentenbereichs.
|
||||||
|
|
||||||
\begin{figure}[H]
|
\begin{figure}[H]
|
||||||
\centering
|
\centering
|
||||||
@@ -10,12 +10,12 @@ Der Dokumentenbereich ist in die bestehende Systemlandschaft von WorkSimple eing
|
|||||||
\label{fig:system-context}
|
\label{fig:system-context}
|
||||||
\end{figure}
|
\end{figure}
|
||||||
|
|
||||||
\textbf{Houston} ist das Kundenportal von WorkSimple. Es ist als ASP.NET-Core-Webanwendung mit Razor Pages implementiert und aggregiert Informationen aus mehreren internen Systemen zu einer einheitlichen Oberfläche für Kunden. Im Rahmen dieses Projekts wurde Houston um den Dokumentenbereich erweitert.
|
\textbf{Houston} ist das Kundenportal von WorkSimple, implementiert als ASP.NET-Core-Webanwendung mit Razor Pages. Es wurde im Rahmen dieses Projekts um den Dokumentenbereich erweitert.
|
||||||
|
|
||||||
\textbf{Efecte} ist das unternehmenseigene ITSM-Tool (IT Service Management). Es dient als zentrale Datenbasis für Kundeninformationen, darunter die eindeutige Organisations-ID jedes Kunden (\texttt{efecte-org-id}), die im Rahmen des Projekts als Autorisierungsmerkmal für den Zugriff auf den richtigen S3-Kundenordner verwendet wird.
|
\textbf{Efecte} ist das ITSM-Tool des Unternehmens und liefert die eindeutige Organisations-ID jedes Kunden (\texttt{efecte-org-id}), die als Autorisierungsmerkmal für den S3-Kundenordner dient.
|
||||||
|
|
||||||
\textbf{Microsoft Entra~ID} (ehemals Azure~AD) übernimmt die Authentifizierung und Autorisierung der Benutzer. Nach erfolgreicher Anmeldung erhält Houston ein Token, das unter anderem die Efecte-Organisations-ID und die zugewiesenen Anwendungsrollen des Benutzers enthält. Die neu eingeführte Rolle \texttt{Documents.Read} steuert den Zugriff auf den Dokumentenbereich.
|
\textbf{Microsoft Entra~ID} (ehemals Azure~AD) übernimmt Authentifizierung und Autorisierung. Das Token enthält die Efecte-Organisations-ID und die Anwendungsrollen; die Rolle \texttt{Documents.Read} steuert den Zugriff auf den Dokumentenbereich.
|
||||||
|
|
||||||
\textbf{S3-Speicher (NetApp StorageGRID)} ist die Ablage für alle Kundendokumente. Er wird von Advanced Unibyte betrieben und ist S3-kompatibel. Houston greift über das AWS SDK für .NET auf den Speicher zu. Jeder Kunde erhält einen eigenen Ordner im Bucket, der über ein S3-Objekt-Metadatum (\texttt{efecte-org-id}) identifiziert wird.
|
\textbf{S3-Speicher (NetApp StorageGRID)} ist die von Advanced Unibyte betriebene, S3-kompatible Ablage für Kundendokumente. Houston greift über das AWS SDK für .NET darauf zu. Jeder Kunde erhält einen eigenen Ordner, identifiziert über ein S3-Metadatum (\texttt{efecte-org-id}).
|
||||||
|
|
||||||
\textbf{Filestash} ist ein webbasierter S3-Browser, der intern von WorkSimple-Mitarbeitern genutzt wird, um Dokumente in den S3-Speicher hochzuladen und zu verwalten. Filestash ist eine externe Anwendung und kein Bestandteil der Houston-Entwicklung.
|
\textbf{Filestash} ist ein webbasierter S3-Browser, den WorkSimple-Mitarbeiter intern zum Hochladen und Verwalten von Dokumenten nutzen.
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
\section{Zielsituation}
|
\section{Zielsituation}
|
||||||
\label{sec:target-situation}
|
\label{sec:target-situation}
|
||||||
|
|
||||||
Mit der Fertigstellung des Dokumentenbereichs erhalten Kunden im Kundenportal Houston erstmals einen strukturierten, selbstständig nutzbaren Zugang zu ihren Dokumenten. Die Zielsituation zeichnet sich durch eine zentrale Ablage im S3-Speicher und eine mandantengetrennte Darstellung in Houston aus.
|
Der Dokumentenbereich bietet folgende Funktionen:
|
||||||
|
|
||||||
Der Dokumentenbereich bietet folgende zentrale Funktionen:
|
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \textbf{Document Explorer:} Auf der Seite \texttt{/documents} werden alle Dokumente des Kunden als Liste dargestellt, gegliedert nach sieben fachlich definierten Dokumententypen (Service-Protokoll, Abnahme-Dokumente, SLA-Reports Ticketbearbeitung, Monitoring Reports, Security Assessments, Abrechnungsdaten, Vertragsunterlagen). Leere Ordner werden nicht angezeigt.
|
\item \textbf{Document Explorer:} Auf \texttt{/documents} werden alle Dokumente des Kunden als Liste dargestellt, gegliedert nach sieben Dokumententypen. Leere Ordner werden nicht angezeigt.
|
||||||
\item \textbf{Typfilter und Suche:} Dokumente können nach Typ gefiltert und titelbasiert serverseitig durchsucht werden.
|
\item \textbf{Typfilter und Suche:} Filterung nach Typ und titelbasierte serverseitige Suche.
|
||||||
\item \textbf{Paginierung:} Große Dokumentenmengen werden seitenweise dargestellt.
|
\item \textbf{Paginierung:} Seitenweise Darstellung großer Dokumentenmengen.
|
||||||
\item \textbf{Downloads:} Einzelne Dokumente lassen sich direkt herunterladen; mehrere Dokumente können als ZIP-Archiv gebündelt heruntergeladen werden.
|
\item \textbf{Downloads:} Einzeldownload sowie ZIP-Download mehrerer ausgewählter Dokumente.
|
||||||
\item \textbf{PDF-Vorschau:} PDF-Dokumente können in einem modalen Viewer direkt im Browser angezeigt werden.
|
\item \textbf{PDF-Vorschau:} Anzeige von PDF-Dokumenten in einem modalen Viewer.
|
||||||
\item \textbf{Share-Link-Previews:} Für einzelne Dokumente können zeitlich begrenzte Freigabelinks erzeugt werden.
|
\item \textbf{Share-Link-Previews:} Erzeugung zeitlich begrenzter Freigabelinks.
|
||||||
\item \textbf{URL-Dateien:} Spezielle \texttt{.url}-Dateien ermöglichen die Verlinkung beliebiger Webadressen — z.\,B. Teams-Kanäle oder SharePoint-Seiten — in der Dokumentenliste.
|
\item \textbf{URL-Dateien:} Spezielle \texttt{.url}-Dateien ermöglichen die Verlinkung beliebiger Webadressen — z.\,B. Teams-Kanäle oder SharePoint-Seiten — in der Dokumentenliste.
|
||||||
\item \textbf{Mandantentrennung:} Jeder Kunde sieht ausschließlich die Dokumente in seinem eigenen S3-Ordner, der über die \texttt{efecte-org-id} aus dem Authentifizierungstoken identifiziert wird.
|
\item \textbf{Mandantentrennung:} Jeder Kunde sieht ausschließlich die Dokumente in seinem eigenen S3-Ordner, der über die \texttt{efecte-org-id} aus dem Authentifizierungstoken identifiziert wird.
|
||||||
\item \textbf{Automatische Ordneranlage:} Beim ersten Aufruf der Dokumentenseite wird die vollständige Ordnerstruktur für den Kunden im S3 angelegt, sofern sie noch nicht existiert.
|
\item \textbf{Automatische Ordneranlage:} Beim ersten Aufruf der Dokumentenseite wird die vollständige Ordnerstruktur für den Kunden im S3 angelegt, sofern sie noch nicht existiert.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
Die Pflege der Dokumente erfolgt weiterhin durch WorkSimple-Mitarbeiter über Filestash, den internen S3-Browser. Kunden haben ausschließlich lesenden Zugriff.
|
Kunden haben ausschließlich lesenden Zugriff; die Pflege erfolgt durch Mitarbeiter über Filestash.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
\subsection{Entwicklungsprozess}
|
\subsection{Entwicklungsprozess}
|
||||||
|
|
||||||
Die Softwareentwicklung bei der Unicorn Development erfolgt nach einem agilen Prozess mit zweiwöchigen Sprints, verwaltet in Azure DevOps. Jedes Work Item durchläuft dabei einen definierten Zustandsautomaten, der in Abbildung~\ref{fig:ado-workflow} dargestellt ist.
|
Die Unicorn Development arbeitet agil mit zweiwöchigen Sprints in Azure DevOps. Jedes Work Item durchläuft den in Abbildung~\ref{fig:ado-workflow} dargestellten Zustandsautomaten.
|
||||||
|
|
||||||
\begin{figure}[H]
|
\begin{figure}[H]
|
||||||
\centering
|
\centering
|
||||||
@@ -12,16 +12,16 @@ Die Softwareentwicklung bei der Unicorn Development erfolgt nach einem agilen Pr
|
|||||||
\label{fig:ado-workflow}
|
\label{fig:ado-workflow}
|
||||||
\end{figure}
|
\end{figure}
|
||||||
|
|
||||||
Ein neu angelegtes Item befindet sich zunächst im Zustand \texttt{New}. Sobald Beschreibung und Akzeptanzkriterien vollständig sind, wird es zur Freigabe eingereicht (\texttt{To Approve}). Im Approval-Termin prüft das Team, ob das Item der \emph{Definition of Ready} genügt, stellt Rückfragen und schätzt den Aufwand in Story Points. Erst danach gilt es als \texttt{Approved} und kann in einen Sprint gezogen werden (\texttt{Committed}). Nach erfolgreichem Merge des zugehörigen Pull Requests wechselt das Item nach \texttt{Dev Completed}; die fachliche Abnahme überführt es schließlich nach \texttt{Test Completed}.
|
Ein neues Item steht auf \texttt{New} und wird nach vollständiger Beschreibung zur Freigabe eingereicht (\texttt{To Approve}). Im Approval-Termin prüft das Team die \emph{Definition of Ready}, stellt Rückfragen und schätzt Story Points; danach gilt es als \texttt{Approved} und kann in einen Sprint gezogen werden (\texttt{Committed}). Nach dem Merge wechselt es zu \texttt{Dev Completed}; die Abnahme überführt es nach \texttt{Test Completed}.
|
||||||
|
|
||||||
Dieser Prozess erwies sich im Projektverlauf als wirksam: Die Rückfragen im Approval-Termin — insbesondere durch \emph{Timo Walter} — deckten mehrfach Lücken in den Akzeptanzkriterien auf, etwa zur Frage, wann und wie oft die Ordnerstruktur geprüft wird, oder ob Namensbeschränkungen für Kundenordner zu berücksichtigen sind.
|
Rückfragen im Approval-Termin — insbesondere durch \emph{Timo Walter} — deckten mehrfach Lücken in den Akzeptanzkriterien auf, etwa zur Häufigkeit der Ordnerstrukturprüfung oder zu Namensbeschränkungen.
|
||||||
|
|
||||||
\subsection{Schnitt der Product Backlog Items}
|
\subsection{Schnitt der Product Backlog Items}
|
||||||
|
|
||||||
Der Backlog-Schnitt erfolgte in zwei Phasen. Am 18.~Juni 2026 — unmittelbar nach der Feature-Analyse — wurden die acht Items der Kernfunktionalität angelegt. Sie orientieren sich direkt an der Gliederung der Feature-Beschreibung: Der Document Explorer bildet die Basis, die im Abschnitt \emph{Feature-Creep} genannten Punkte (Suche, Einzeldownload, ZIP-Download, PDF-Modal, URL-Dateien) wurden jeweils zu eigenen Items. Am 7.~Juli 2026 kam das Item zur Typisierung über Icons hinzu, am 8.~Juli das Item zur automatischen Ordneranlage.
|
Der Backlog-Schnitt erfolgte in zwei Phasen. Am 18.~Juni 2026 wurden acht Kern-Items angelegt, orientiert an der Feature-Beschreibung: Document Explorer als Basis, die \emph{Feature-Creep}-Punkte (Suche, Einzeldownload, ZIP-Download, PDF-Modal, URL-Dateien) als eigene Items. Am 7.~Juli kam die Typisierung über Icons hinzu, am 8.~Juli die automatische Ordneranlage.
|
||||||
|
|
||||||
Eine zweite Gruppe von Items entstand erst während der Umsetzung. Am 22.~Juli ergänzte die UI-Zuarbeit die Anforderungen um Paginierung und Typfilter. Am 28.~Juli wurde die Recherche zur Optimierung der S3-Abfrage angelegt, deren Ergebnis am 12.~August in das Item zum Kundenordner-Lookup mündete. Am 17.~August kam schließlich das Item zu den Race Conditions hinzu (siehe Abschnitt~\ref{sec:race-conditions}).
|
Eine zweite Gruppe entstand während der Umsetzung: Am 22.~Juli ergänzte die UI-Zuarbeit Paginierung und Typfilter. Am 28.~Juli wurde die Recherche zur Optimierung der S3-Abfrage angelegt, deren Ergebnis am 12.~August in das Kundenordner-Lookup-Item mündete. Am 17.~August kam das Item zu Race Conditions hinzu (siehe Abschnitt~\ref{sec:race-conditions}).
|
||||||
|
|
||||||
Insgesamt hängen 14 Product Backlog Items und ein Bug am Feature. Die vollständige Übersicht mit Aufwandsschätzung, Sprint und Status findet sich in Tabelle~\ref{tab:backlog} im Anhang. Die geschätzten Aufwände summieren sich auf 46 Story Points, verteilt auf 35 Punkte in Sprint~15.2026 und 11 Punkte in Sprint~16.2026.
|
Insgesamt hängen 14 Product Backlog Items und ein Bug am Feature (Tabelle~\ref{tab:backlog} im Anhang). Die geschätzten Aufwände summieren sich auf 46 Story Points: 35 in Sprint~15.2026, 11 in Sprint~16.2026.
|
||||||
|
|
||||||
Bemerkenswert ist die Aufteilung: Die im Voraus geschnittenen Items betreffen ausschließlich fachliche Funktionen. Sämtliche nachgeschobenen Items der zweiten Gruppe entstanden aus technischen Problemen, die erst bei der Implementierung sichtbar wurden — ein Muster, das in Abschnitt~\ref{sec:reflection} aufgegriffen wird.
|
Die vorgeschnittenen Items betreffen ausschließlich fachliche Funktionen; alle nachgeschobenen entstanden aus technischen Problemen bei der Implementierung — ein Muster, das in Abschnitt~\ref{sec:reflection} aufgegriffen wird.
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
\section{Feature-Analyse}
|
\section{Feature-Analyse}
|
||||||
\label{sec:feature-analysis}
|
\label{sec:feature-analysis}
|
||||||
|
|
||||||
Das Feature~484 „Dokumente" existierte seit November 2023 als zweizeilige Bedarfsnotiz im Backlog. Am 10.~Juni 2026 arbeitete \emph{Thomas Drewermann} es in mehreren aufeinanderfolgenden Bearbeitungen zu einer vollständigen Feature-Beschreibung aus. Dabei entstanden die Entscheidung für einen S3-Speicher als Ablage, die Festlegung auf Filestash als internes Verwaltungswerkzeug, der Katalog der sieben Dokumententypen sowie die Abschnitte \emph{Feature-Creep} und \emph{Out of Scope}.
|
Das Feature~484 „Dokumente" existierte seit November 2023 als zweizeilige Bedarfsnotiz. Am 10.~Juni 2026 arbeitete \emph{Thomas Drewermann} es zu einer vollständigen Feature-Beschreibung aus — einschließlich S3, Filestash als Verwaltungswerkzeug, den sieben Dokumententypen sowie den Abschnitten \emph{Feature-Creep} und \emph{Out of Scope}.
|
||||||
|
|
||||||
Am 18.~Juni 2026 führte ich eine Feature-Analyse durch, um die verbliebenen Unklarheiten vor dem Backlog-Schnitt zu beseitigen. Vier Rückfragen wurden als Kommentare am Work Item gestellt und noch am selben Tag beantwortet. Drei davon entschieden Architekturfragen:
|
Am 18.~Juni 2026 führte ich die Feature-Analyse durch. Vier Rückfragen wurden als Kommentare gestellt und am selben Tag beantwortet; drei entschieden Architekturfragen:
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
\item \textbf{Autorisierung über Metadaten:} Auf die Frage, ob die Efecte-Organisations-ID als Metadatum am Kundenordner gespeichert und die Berechtigung darüber aufgelöst werden könne, lautete die Antwort \emph{ja}. Damit war das Autorisierungskonzept festgelegt (siehe Abschnitt~\ref{sec:authorization}).
|
\item \textbf{Autorisierung über Metadaten:} Die Efecte-Organisations-ID wird als Metadatum am Kundenordner gespeichert; die Berechtigung wird darüber aufgelöst (siehe Abschnitt~\ref{sec:authorization}).
|
||||||
\item \textbf{Filestash als externes Werkzeug:} Es wurde bestätigt, dass Filestash unverändert als Verwaltungsoberfläche genutzt wird und \emph{nicht} als Referenz für einen Nachbau in Houston dient. Damit beschränkte sich der Implementierungsaufwand auf die Kundensicht.
|
\item \textbf{Filestash als externes Werkzeug:} Filestash dient unverändert als Verwaltungsoberfläche, \emph{nicht} als Referenz für einen Nachbau in Houston.
|
||||||
\item \textbf{Typisierung über Ordner statt Metadaten:} Auf die Frage, ob der Dokumententyp als Metafeld an der Datei vermerkt werden solle, lautete die Antwort \emph{nein, Ordner}. Der Typ wird also über den Unterordner bestimmt, in dem das Dokument liegt.
|
\item \textbf{Typisierung über Ordner statt Metadaten:} Der Dokumententyp wird über den Unterordner bestimmt, nicht als Metafeld an der Datei.
|
||||||
\item \textbf{Bedeutung der URL-Dateien:} Die vierte Frage klärte, dass mit der Verlinkung zu Teams oder SharePoint gemeint ist, \emph{externe} Ressourcen in den Dokumentenbereich einzubetten — und nicht umgekehrt Houston-Dokumente nach außen zu teilen. Daraus entstand das Product Backlog Item zur Unterstützung von \texttt{.url}-Dateien.
|
\item \textbf{Bedeutung der URL-Dateien:} Gemeint ist das Einbetten \emph{externer} Ressourcen in den Dokumentenbereich, nicht das Teilen von Houston-Dokumenten nach außen.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
Die dritte Entscheidung erwies sich im weiteren Verlauf als die folgenreichste. Die Abbildung des Typs über die Ordnerstruktur macht die Filterung nach Typ günstig, da sie sich auf ein Präfix-Listing reduziert. Sie erzwingt jedoch, dass die Ordnerstruktur für jeden Kunden vorhanden ist, und macht damit die automatische Anlage der Ordner zu einer eigenen Anforderung. Zugleich erschwert sie die typübergreifende Suche, da hierfür mehrere Präfixe durchlaufen werden müssen.
|
Die dritte Entscheidung erwies sich als die folgenreichste: Die Typabbildung über Ordner macht die Filterung günstig (Präfix-Listing), erzwingt aber die automatische Anlage der Ordnerstruktur und erschwert die typübergreifende Suche.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
\section{Beschaffung der S3-Infrastruktur}
|
\section{Beschaffung der S3-Infrastruktur}
|
||||||
\label{sec:infrastructure}
|
\label{sec:infrastructure}
|
||||||
|
|
||||||
Der für das Projekt benötigte S3-Speicher stand zu Projektbeginn nicht zur Verfügung und musste über den internen Service-Desk-Prozess beantragt werden. Da der Speicher nicht von WorkSimple selbst, sondern von Advanced Unibyte betrieben wird, war die Beschaffung mit einem mehrstufigen Abstimmungsweg verbunden. Abbildung~\ref{fig:timeline-infra} zeigt den zeitlichen Verlauf.
|
Der S3-Speicher stand zu Projektbeginn nicht bereit und musste über den Service-Desk beantragt werden. Da Advanced Unibyte ihn betreibt, war ein mehrstufiger Abstimmungsweg nötig. Abbildung~\ref{fig:timeline-infra} zeigt den Verlauf.
|
||||||
|
|
||||||
\begin{figure}[H]
|
\begin{figure}[H]
|
||||||
\centering
|
\centering
|
||||||
@@ -12,23 +12,23 @@ Der für das Projekt benötigte S3-Speicher stand zu Projektbeginn nicht zur Ver
|
|||||||
|
|
||||||
\subsection{Bereitstellung der Buckets}
|
\subsection{Bereitstellung der Buckets}
|
||||||
|
|
||||||
Am 22.~Juli 2026 stellte ich den Service Request „Houston DEV S3 Documents Speicher" an das interne Infrastruktur-Team. Der Request wurde am 23.~Juli \emph{Alexander Wagner} zugewiesen und am 27.~Juli abgeschlossen: Advanced Unibyte hatte die Buckets angelegt, die Zugangsdaten wurden im Passwortmanager Passbolt hinterlegt. Insgesamt wurden drei Buckets bereitgestellt — je einer für die Entwicklungs-, Test- und Produktivumgebung.
|
Am 22.~Juli 2026 stellte ich den Service Request. Er wurde am 23.~Juli \emph{Alexander Wagner} zugewiesen und am 27.~Juli abgeschlossen: drei Buckets (DEV, TEST, PROD) waren angelegt, die Zugangsdaten in Passbolt hinterlegt.
|
||||||
|
|
||||||
Vom Antrag bis zur Verfügbarkeit vergingen fünf Arbeitstage. Da der Document Explorer als erstes Item ohnehin erst am 27.~Juli in die Umsetzung ging, entstand hieraus keine Verzögerung.
|
Da der Document Explorer ohnehin erst am 27.~Juli in die Umsetzung ging, entstand keine Verzögerung.
|
||||||
|
|
||||||
\subsection{Freischaltung zusätzlicher Funktionen}
|
\subsection{Freischaltung zusätzlicher Funktionen}
|
||||||
|
|
||||||
Deutlich aufwendiger gestaltete sich die Klärung, welche S3-Funktionen die StorageGRID-Installation tatsächlich unterstützt. Im Rahmen der Recherche zur Optimierung des Kundenordner-Lookups (siehe Abschnitt~\ref{sec:lookup-research}) kamen zwei Funktionen als mögliche Lösungen in Betracht:
|
Aufwendiger war die Klärung der verfügbaren S3-Funktionen. Im Rahmen der Lookup-Recherche (siehe Abschnitt~\ref{sec:lookup-research}) kamen zwei in Betracht:
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \textbf{S3 Select} (\texttt{SelectObjectContent}) erlaubt es, Inhalte einzelner Objekte serverseitig per SQL-ähnlicher Abfrage zu filtern \autocite{aws-s3-select, storagegrid-s3-select}.
|
\item \textbf{S3 Select} (\texttt{SelectObjectContent}) erlaubt es, Inhalte einzelner Objekte serverseitig per SQL-ähnlicher Abfrage zu filtern \autocite{aws-s3-select, storagegrid-s3-select}.
|
||||||
\item Der \textbf{Search Integration Service} von StorageGRID spiegelt Objektmetadaten in einen Elasticsearch-Index und ermöglicht dadurch eine echte Suche über Metadaten \autocite{storagegrid-search-integration}.
|
\item Der \textbf{Search Integration Service} von StorageGRID spiegelt Objektmetadaten in einen Elasticsearch-Index und ermöglicht dadurch eine echte Suche über Metadaten \autocite{storagegrid-search-integration}.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
Am 30.~Juli beantragte ich die Freischaltung beider Funktionen. Da hierfür der Betreiber einbezogen werden musste, kontaktierte \emph{Lennart Meinert} am 4.~August Advanced Unibyte. Am 6.~August benannte er die drei betroffenen Buckets, am 7.~August bestätigte Advanced Unibyte die Aktivierung von S3 Select für alle drei Umgebungen.
|
Am 30.~Juli beantragte ich die Freischaltung beider Funktionen. \emph{Lennart Meinert} kontaktierte am 4.~August Advanced Unibyte; am 7.~August wurde S3 Select für alle drei Umgebungen aktiviert.
|
||||||
|
|
||||||
Für den Search Integration Service fiel die Antwort anders aus: Am 17.~August teilte Advanced Unibyte mit, dass diese Funktion derzeit nicht angeboten werde; das Thema wurde intern an den dortigen Product Owner eskaliert. Am 21.~August schlug Advanced Unibyte ein Folgegespräch vor. Da zu diesem Zeitpunkt bereits eine Lösung ohne serverseitige Suche gefunden und umgesetzt war (siehe Abschnitt~\ref{sec:lookup-decision}), wurde der Service Request geschlossen und das Thema in den Ausblick verschoben.
|
Für den Search Integration Service teilte Advanced Unibyte am 17.~August mit, dass die Funktion derzeit nicht angeboten werde, und schlug am 21.~August ein Folgegespräch vor. Da bereits eine Lösung ohne serverseitige Suche umgesetzt war (siehe Abschnitt~\ref{sec:lookup-decision}), wurde der Request geschlossen.
|
||||||
|
|
||||||
\subsection{Bewertung}
|
\subsection{Bewertung}
|
||||||
|
|
||||||
Zwischen dem ersten Antrag und der abschließenden Klärung lagen vier Wochen. Diese Vorlaufzeit war zu Projektbeginn nicht eingeplant und beeinflusste die Architekturentscheidung unmittelbar: Ein Lösungsansatz, der auf einer erst noch zu beschaffenden Fremdleistung beruht, ist innerhalb eines Projektzeitraums von wenigen Wochen nicht belastbar. Die schließlich gewählte Lösung kommt daher ohne Erweiterung der Speicherfunktionen aus.
|
Zwischen erstem Antrag und abschließender Klärung lagen vier Wochen — zu Projektbeginn nicht eingeplant. Ein Lösungsansatz, der auf einer noch zu beschaffenden Fremdleistung beruht, ist innerhalb eines Projektzeitraums von wenigen Wochen nicht belastbar. Die gewählte Lösung kommt daher ohne Erweiterung der Speicherfunktionen aus.
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
\section{Einarbeitung}
|
\section{Einarbeitung}
|
||||||
\label{sec:onboarding}
|
\label{sec:onboarding}
|
||||||
|
|
||||||
Vor Beginn der Implementierung war eine Einarbeitung in die für das Projekt relevanten Technologien erforderlich. Im Zentrum stand dabei der S3-Objektspeicher, mit dem im bisherigen Verlauf des Praktikums noch nicht gearbeitet worden war.
|
Vor der Implementierung war eine Einarbeitung in die projektrelevanten Technologien erforderlich, insbesondere S3.
|
||||||
|
|
||||||
\subsection{S3 als Objektspeicher}
|
\subsection{S3 als Objektspeicher}
|
||||||
|
|
||||||
S3 (\emph{Simple Storage Service}) ist kein klassisches Dateisystem, sondern ein Objektspeicher. Objekte werden über einen flachen Schlüsselraum adressiert; eine Ordnerhierarchie existiert technisch nicht. Was in Werkzeugen wie Filestash als Ordner dargestellt wird, ist lediglich ein Präfix im Objektschlüssel, das in Verbindung mit einem Trennzeichen (\texttt{Delimiter}) beim Auflisten hierarchisch interpretiert wird \autocite{aws-listobjectsv2}. Diese Eigenschaft prägt das gesamte Ablagekonzept (siehe Abschnitt~\ref{sec:s3-layout}) und war für mehrere spätere Architekturentscheidungen ausschlaggebend.
|
S3 (\emph{Simple Storage Service}) ist ein Objektspeicher, kein Dateisystem. Objekte werden über einen flachen Schlüsselraum adressiert; was als Ordner erscheint, ist ein Präfix im Objektschlüssel, das per \texttt{Delimiter} hierarchisch interpretiert wird \autocite{aws-listobjectsv2}. Diese Eigenschaft prägt das Ablagekonzept (siehe Abschnitt~\ref{sec:s3-layout}) und war für mehrere Architekturentscheidungen ausschlaggebend.
|
||||||
|
|
||||||
Eine zweite wesentliche Erkenntnis betrifft die Abfragemöglichkeiten: An Objekten können zwar benutzerdefinierte Metadaten hinterlegt werden, S3 bietet jedoch \emph{keine} Möglichkeit, Objekte anhand dieser Metadaten zu suchen. Ein Lookup nach einem Metadatenwert erfordert daher das Auflisten aller in Frage kommenden Objekte und eine anschließende clientseitige Filterung. Dieses Detail wurde erst im Verlauf der Implementierung zum zentralen technischen Problem des Projekts (siehe Abschnitt~\ref{sec:lookup-research}).
|
S3 bietet zudem \emph{keine} Suche über benutzerdefinierte Metadaten. Ein Lookup erfordert das Auflisten aller Objekte mit clientseitiger Filterung — das zentrale technische Problem des Projekts (siehe Abschnitt~\ref{sec:lookup-research}).
|
||||||
|
|
||||||
\subsection{AWS SDK für .NET}
|
\subsection{AWS SDK für .NET}
|
||||||
|
|
||||||
Der Zugriff auf den S3-Speicher erfolgt aus Houston heraus über das AWS SDK für .NET. Zentrale Schnittstelle ist \texttt{IAmazonS3}, über die sämtliche Operationen (\texttt{ListObjectsV2}, \texttt{GetObject}, \texttt{PutObject}, \texttt{CopyObject}, \texttt{DeleteObjects}) ausgeführt werden. Da \texttt{IAmazonS3} eine Schnittstelle ist, lässt sie sich in Unit-Tests durch ein Mock ersetzen, was für die spätere Testabdeckung des Kundenordner-Lookups entscheidend war (siehe Abschnitt~\ref{sec:unit-tests}).
|
Der Zugriff erfolgt über \texttt{IAmazonS3} aus dem AWS SDK für .NET (\texttt{ListObjectsV2}, \texttt{GetObject}, \texttt{PutObject}, \texttt{CopyObject}, \texttt{DeleteObjects}). Da \texttt{IAmazonS3} eine Schnittstelle ist, lässt sie sich in Unit-Tests durch ein Mock ersetzen (siehe Abschnitt~\ref{sec:unit-tests}).
|
||||||
|
|
||||||
\subsection{NetApp StorageGRID}
|
\subsection{NetApp StorageGRID}
|
||||||
|
|
||||||
Der eingesetzte Speicher wird nicht bei Amazon betrieben, sondern von Advanced Unibyte auf Basis von NetApp StorageGRID bereitgestellt. StorageGRID ist S3-kompatibel, implementiert die S3-API jedoch nicht vollständig. Welche Funktionen tatsächlich verfügbar sind, hängt von der Konfiguration der Installation ab und musste im Einzelfall geklärt werden. Diese Einschränkung betraf im Projektverlauf sowohl \texttt{SelectObjectContent} \autocite{storagegrid-s3-select} als auch den \emph{Search Integration Service} \autocite{storagegrid-search-integration} und führte zu den in Abschnitt~\ref{sec:infrastructure} beschriebenen Abstimmungen.
|
Der Speicher wird von Advanced Unibyte auf Basis von NetApp StorageGRID betrieben. StorageGRID ist S3-kompatibel, implementiert die API jedoch nicht vollständig. Dies betraf im Projektverlauf \texttt{SelectObjectContent} \autocite{storagegrid-s3-select} und den \emph{Search Integration Service} \autocite{storagegrid-search-integration} und führte zu den in Abschnitt~\ref{sec:infrastructure} beschriebenen Abstimmungen.
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
\section{Anforderungen}
|
\section{Anforderungen}
|
||||||
\label{sec:requirements}
|
\label{sec:requirements}
|
||||||
|
|
||||||
Aus der Feature-Beschreibung und der Analyse ergaben sich die folgenden Anforderungen. Die funktionalen Anforderungen wurden anschließend in Product Backlog Items überführt; die vollständige Zuordnung findet sich in Tabelle~\ref{tab:requirements} im Anhang.
|
Aus Feature-Beschreibung und Analyse ergaben sich die folgenden Anforderungen (Zuordnung zu Backlog Items in Tabelle~\ref{tab:requirements} im Anhang).
|
||||||
|
|
||||||
\subsection{Funktionale Anforderungen}
|
\subsection{Funktionale Anforderungen}
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \textbf{FA-1 — Dokumentenliste:} Auf der Seite \texttt{/documents} werden alle Dokumente der Organisation des angemeldeten Benutzers als Liste dargestellt. Ordner selbst werden nicht als Einträge angezeigt; leere Typordner erscheinen nicht.
|
\item \textbf{FA-1 — Dokumentenliste:} Auf \texttt{/documents} werden alle Dokumente der Organisation aufgelistet; leere Typordner erscheinen nicht.
|
||||||
\item \textbf{FA-2 — Mandantentrennung:} Ein Benutzer sieht ausschließlich Dokumente, die im Ordner seiner Organisation liegen. Die Zuordnung erfolgt über die \texttt{efecte-org-id} am Kundenordner.
|
\item \textbf{FA-2 — Mandantentrennung:} Ein Benutzer sieht ausschließlich Dokumente seiner Organisation, zugeordnet über die \texttt{efecte-org-id}.
|
||||||
\item \textbf{FA-3 — Rollenbasierter Zugriff:} Der Navigationspunkt ist nur bei vorhandener Rollenberechtigung sichtbar. Ein direkter Aufruf ohne Berechtigung führt zu einer 403-Antwort.
|
\item \textbf{FA-3 — Rollenbasierter Zugriff:} Der Navigationspunkt ist nur bei vorhandener Berechtigung sichtbar; Aufruf ohne Berechtigung ergibt 403.
|
||||||
\item \textbf{FA-4 — Typisierung und Icons:} Jedes Dokument wird mit einem Icon dargestellt, das aus dem Unterordner abgeleitet wird. Unbekannte Typen erhalten ein Standard-Icon.
|
\item \textbf{FA-4 — Typisierung und Icons:} Jedes Dokument wird mit einem aus dem Unterordner abgeleiteten Icon dargestellt.
|
||||||
\item \textbf{FA-5 — Suche:} Dokumente können serverseitig nach ihrem Titel durchsucht werden, inklusive Teiltreffern. Ein leeres Suchfeld zeigt wieder die vollständige Liste.
|
\item \textbf{FA-5 — Suche:} Serverseitige Titelsuche mit Teiltreffern.
|
||||||
\item \textbf{FA-6 — Typfilter:} Unterhalb der Suchleiste kann je Dokumententyp ein Element zum Ein- und Ausblenden ausgewählt werden.
|
\item \textbf{FA-6 — Typfilter:} Filterelemente je Dokumententyp zum Ein- und Ausblenden.
|
||||||
\item \textbf{FA-7 — Paginierung:} Die Liste wird seitenweise dargestellt; die Seitengröße ist durch den Benutzer festlegbar, analog zu den übrigen Houston-Seiten.
|
\item \textbf{FA-7 — Paginierung:} Seitenweise Darstellung mit benutzerdefinierter Seitengröße, analog zu den übrigen Houston-Seiten.
|
||||||
\item \textbf{FA-8 — Einzeldownload:} Jeder Eintrag besitzt einen Download-Button. Der Download erfolgt über eine zeitlich begrenzte Pre-Signed URL \autocite{aws-presigned-urls} unter Beibehaltung des ursprünglichen Dateinamens.
|
\item \textbf{FA-8 — Einzeldownload:} Download über eine zeitlich begrenzte Pre-Signed URL \autocite{aws-presigned-urls} unter Beibehaltung des Dateinamens.
|
||||||
\item \textbf{FA-9 — ZIP-Download:} Mehrere Dokumente können über Auswahlboxen markiert und gemeinsam als ZIP-Archiv heruntergeladen werden. Die Ordnerstruktur des Archivs entspricht der S3-Struktur; das Archiv wird erst beim Klick erzeugt.
|
\item \textbf{FA-9 — ZIP-Download:} Ausgewählte Dokumente werden als ZIP-Archiv heruntergeladen; die Ordnerstruktur entspricht der S3-Struktur.
|
||||||
\item \textbf{FA-10 — PDF-Vorschau:} PDF-Dokumente können in einem Modal angezeigt werden, ohne zuvor heruntergeladen zu werden. Für Nicht-PDF-Dateien wird keine Vorschau geöffnet.
|
\item \textbf{FA-10 — PDF-Vorschau:} PDF-Anzeige im Modal ohne vorherigen Download.
|
||||||
\item \textbf{FA-11 — Share-Links:} Für jedes Dokument kann ein Freigabelink erzeugt werden, dessen Zielseite OpenGraph-Meta-Tags für eine Linkvorschau bereitstellt und anschließend auf den Document Explorer weiterleitet.
|
\item \textbf{FA-11 — Share-Links:} Freigabelink mit OpenGraph-Meta-Tags für Linkvorschau und Weiterleitung auf den Document Explorer.
|
||||||
\item \textbf{FA-12 — URL-Dateien:} Im Speicher abgelegte \texttt{.url}-Dateien werden nach dem INI-Format ausgewertet und leiten beim Anklicken auf die hinterlegte Adresse weiter. Ist keine gültige URL erkennbar, wird die Datei wie eine normale Datei behandelt.
|
\item \textbf{FA-12 — URL-Dateien:} \texttt{.url}-Dateien werden nach INI-Format ausgewertet und leiten auf die hinterlegte Adresse weiter; fehlt eine gültige URL, wird die Datei normal behandelt.
|
||||||
\item \textbf{FA-13 — Automatische Ordneranlage:} Beim Aufruf der Dokumentenseite wird die vollständige Typordnerstruktur für die Organisation angelegt, sofern sie noch nicht existiert.
|
\item \textbf{FA-13 — Automatische Ordneranlage:} Beim Aufruf wird die Typordnerstruktur für die Organisation angelegt, sofern sie fehlt.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
\subsection{Nichtfunktionale Anforderungen}
|
\subsection{Nichtfunktionale Anforderungen}
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \textbf{NFA-1 — Skalierbarkeit des Lookups:} Die Auflösung des Kundenordners darf nicht linear mit der Anzahl der Kunden wachsen. Im Normalfall soll ein einzelner Aufruf genügen.
|
\item \textbf{NFA-1 — Skalierbarkeit des Lookups:} Die Auflösung des Kundenordners darf nicht linear mit der Kundenanzahl wachsen.
|
||||||
\item \textbf{NFA-2 — Fehlerbehandlung:} Können Dokumente nicht geladen werden, wird eine verständliche Fehlermeldung angezeigt. Eine stillschweigend leere Seite ist nicht zulässig.
|
\item \textbf{NFA-2 — Fehlerbehandlung:} Bei Ladefehlern wird eine verständliche Meldung angezeigt; eine stillschweigend leere Seite ist unzulässig.
|
||||||
\item \textbf{NFA-3 — Leerer Zustand:} Existieren für einen berechtigten Benutzer keine Dokumente, wird ein entsprechender Hinweis angezeigt.
|
\item \textbf{NFA-3 — Leerer Zustand:} Existieren keine Dokumente, wird ein Hinweis angezeigt.
|
||||||
\item \textbf{NFA-4 — Konsistenz zur bestehenden Oberfläche:} Suche, Paginierung und Bedienelemente orientieren sich an den übrigen Houston-Seiten.
|
\item \textbf{NFA-4 — Konsistenz:} Bedienelemente orientieren sich an den übrigen Houston-Seiten.
|
||||||
\item \textbf{NFA-5 — Testbarkeit:} Die Logik zur Auflösung des Kundenordners ist durch Unit-Tests mit einem gemockten \texttt{IAmazonS3} abgedeckt.
|
\item \textbf{NFA-5 — Testbarkeit:} Der Kundenordner-Lookup ist durch Unit-Tests mit gemocktem \texttt{IAmazonS3} abgedeckt.
|
||||||
\item \textbf{NFA-6 — Pfadsicherheit:} Beim Download wird geprüft, dass der angeforderte Schlüssel innerhalb des Kundenordners liegt; Pfadanteile zum Verlassen des Ordners werden abgewiesen.
|
\item \textbf{NFA-6 — Pfadsicherheit:} Beim Download wird geprüft, dass der Schlüssel innerhalb des Kundenordners liegt.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
\section{Zeit- und Aufwandsplanung}
|
\section{Zeit- und Aufwandsplanung}
|
||||||
\label{sec:schedule}
|
\label{sec:schedule}
|
||||||
|
|
||||||
Für das Praktikum sind 24 Manntage beziehungsweise 192 Arbeitsstunden vorgesehen. Der Projektzeitraum erstreckt sich von der Themenfindung Ende Mai 2026 bis zur Abgabe der Dokumentation. Die Umsetzung verteilt sich auf die Sprints 15.2026 bis 17.2026. Abbildung~\ref{fig:gantt} zeigt die geplante zeitliche Verteilung.
|
Für das Praktikum sind 24 Manntage (192 Stunden) vorgesehen. Der Projektzeitraum reicht von Ende Mai 2026 bis zur Abgabe; die Umsetzung verteilt sich auf die Sprints 15–17.2026. Abbildung~\ref{fig:gantt} zeigt die Zeitplanung.
|
||||||
|
|
||||||
\begin{figure}[H]
|
\begin{figure}[H]
|
||||||
\centering
|
\centering
|
||||||
@@ -10,8 +10,8 @@ Für das Praktikum sind 24 Manntage beziehungsweise 192 Arbeitsstunden vorgesehe
|
|||||||
\label{fig:gantt}
|
\label{fig:gantt}
|
||||||
\end{figure}
|
\end{figure}
|
||||||
|
|
||||||
Die Planung gliedert sich in vier Phasen. Die \textbf{Analyse- und Konzeptionsphase} (Ende Mai bis Anfang Juli) umfasste die Themenfindung mit \emph{Sarah Hinzmann} und \emph{Thomas Drewermann}, die Feature-Analyse sowie den Schnitt und die Freigabe der Product Backlog Items. Die \textbf{Implementierungsphase} begann am 27.~Juli mit dem ersten Pull Request und erstreckte sich über die Sprints 15 und 16. Parallel dazu lief die \textbf{Qualitätssicherung} in Form fortlaufender Code-Reviews; der Abnahmetest begann am 13.~August. Die \textbf{Abschlussphase} umfasst Release, Dokumentation und Präsentation.
|
Vier Phasen gliedern die Planung: Die \textbf{Analyse- und Konzeptionsphase} (Ende Mai bis Anfang Juli) umfasste Themenfindung, Feature-Analyse und Backlog-Schnitt. Die \textbf{Implementierungsphase} begann am 27.~Juli über die Sprints 15 und 16. Parallel lief die \textbf{Qualitätssicherung} (Code-Reviews, Abnahmetest ab 13.~August). Die \textbf{Abschlussphase} umfasst Release, Dokumentation und Präsentation.
|
||||||
|
|
||||||
Eine Besonderheit der Planung ist die Abhängigkeit von der Infrastrukturbereitstellung: Der Document Explorer konnte erst umgesetzt werden, nachdem der S3-Speicher zur Verfügung stand. Diese Abhängigkeit wurde bereits im Approval-Termin am 8.~Juli durch \emph{Stephan Janßen} als Voraussetzung am Item vermerkt. Der tatsächliche Vorlauf für die Beschaffung wird im folgenden Abschnitt beschrieben.
|
Die Planung hing von der Infrastrukturbereitstellung ab: Der Document Explorer konnte erst nach Verfügbarkeit des S3-Speichers umgesetzt werden. \emph{Stephan Janßen} vermerkte diese Abhängigkeit am 8.~Juli als Voraussetzung am Item.
|
||||||
|
|
||||||
Der Soll-Ist-Vergleich der Zeitplanung findet sich in Abschnitt~\ref{sec:target-comparison}.
|
Der Soll-Ist-Vergleich der Zeitplanung findet sich in Abschnitt~\ref{sec:target-comparison}.
|
||||||
|
|||||||
@@ -3,38 +3,36 @@
|
|||||||
|
|
||||||
\subsection{Ablauf}
|
\subsection{Ablauf}
|
||||||
|
|
||||||
Die fachliche Abnahme übernahm \emph{Maria-Lena Andersz}. Sie begann am 13.~August 2026, also zu einem Zeitpunkt, an dem erst ein Teil der Pull Requests zusammengeführt war, und zog sich mit Unterbrechungen bis zum 25.~August. Geprüft wurde gegen die Akzeptanzkriterien der einzelnen Backlog Items auf der Testumgebung.
|
Die fachliche Abnahme übernahm \emph{Maria-Lena Andersz}. Sie begann am 13.~August 2026 und zog sich mit Unterbrechungen bis zum 25.~August; geprüft wurde gegen die Akzeptanzkriterien der Backlog Items auf der Testumgebung.
|
||||||
|
|
||||||
Die Abnahme zerfiel faktisch in zwei Abschnitte. Der erste war von Zugriffs- und Konfigurationsproblemen geprägt und förderte kaum fachliche Erkenntnisse zutage. Erst nachdem diese behoben waren, konnte die eigentliche funktionale Prüfung stattfinden.
|
Die Abnahme zerfiel in zwei Abschnitte: zunächst Zugriffs- und Konfigurationsprobleme, erst danach die funktionale Prüfung.
|
||||||
|
|
||||||
\subsection{Der Menüpunkt war nicht sichtbar}
|
\subsection{Der Menüpunkt war nicht sichtbar}
|
||||||
|
|
||||||
Der erste Befund lautete, dass der neue Menüpunkt „Dokumente" nicht erscheine. Für eine Funktion, die zu diesem Zeitpunkt bereits mehrere zusammengeführte Pull Requests umfasste, ist das ein ernüchternder Einstieg.
|
Der Menüpunkt „Dokumente" erschien nicht. Die Ursache lag nicht im Code, sondern in der Berechtigungsvergabe — der Testerin war die Anwendungsrolle nicht zugewiesen. Das Verhalten entsprach der Spezifikation: Ohne Rolle ist der Menüpunkt unsichtbar (siehe Abschnitt~\ref{sec:authorization}).
|
||||||
|
|
||||||
Die Ursache lag nicht im Code, sondern in der Berechtigungsvergabe: Der Testerin war die Anwendungsrolle nicht zugewiesen. Das Verhalten war damit exakt das spezifizierte — ohne die Rolle ist der Menüpunkt unsichtbar (siehe Abschnitt~\ref{sec:authorization}). Die Funktion verhielt sich also korrekt und war dennoch unbenutzbar.
|
Der Vorgang legt eine Lücke offen: Die Rolle war Ende Juli für die Entwicklungsumgebung beantragt worden; dass ihre Vergabe an Testende ein eigener Schritt ist, war weder in Akzeptanzkriterien noch Übergabe festgehalten. Eine rollenbasierte Funktion bringt Anforderungen mit, die über den Code hinausgehen.
|
||||||
|
|
||||||
Der Vorgang legt eine Lücke im Vorgehen offen. Die Rolle war Ende Juli für die Entwicklungsumgebung beantragt worden; dass ihre Vergabe an die Testenden ein eigener, ausdrücklich zu veranlassender Schritt ist, war weder in den Akzeptanzkriterien noch in einer Übergabe festgehalten. Eine rollenbasierte Funktion bringt damit eine Anforderung mit sich, die über den Code hinausgeht: Wer testen soll, braucht die Rolle, und wer die Funktion ausrollt, muss dies veranlassen.
|
Hier bewährte sich die 403-Antwort (Abschnitt~\ref{sec:authorization}): Bei 404 wäre für die Testerin nicht unterscheidbar gewesen, ob die Seite fehlt oder die Berechtigung.
|
||||||
|
|
||||||
An dieser Stelle bewährte sich die in Abschnitt~\ref{sec:authorization} beschriebene Entscheidung für eine 403-Antwort. Hätte die Anwendung stattdessen mit 404 geantwortet, wäre für die Testerin nicht unterscheidbar gewesen, ob die Seite nicht existiert, noch nicht ausgerollt ist oder ihr lediglich die Berechtigung fehlt. Die Fehlersuche wäre entsprechend länger gelaufen.
|
|
||||||
|
|
||||||
\subsection{Konfiguration und Testdaten}
|
\subsection{Konfiguration und Testdaten}
|
||||||
|
|
||||||
Nach der Klärung der Berechtigung folgte ein zweiter Befund: Es wurden keine Dokumente angezeigt. Auch hier lag die Ursache in der Umgebung. Zum einen mussten überhaupt erst Testdateien in den für die Testumgebung vorgesehenen Speicher geladen werden — ein leerer Speicher führt zum leeren Zustand, der wie beabsichtigt keinen Fehler darstellt. Zum anderen bestanden Abweichungen in der Konfiguration, insbesondere bei der Zuordnung zwischen dem Testkonto und einer Organisation.
|
Nach Klärung der Berechtigung wurden keine Dokumente angezeigt. Auch hier lag die Ursache in der Umgebung: Es fehlten Testdateien im Speicher, und die Zuordnung zwischen Testkonto und Organisation war nicht korrekt konfiguriert.
|
||||||
|
|
||||||
Dieser zweite Punkt ist charakteristisch für das gewählte Ablagekonzept: Ob ein Benutzer Dokumente sieht, hängt von einer Kette ab, die über das Anmeldetoken, die Organisationszuordnung und das Metadatum am Kundenordner läuft. Ist ein Glied dieser Kette in einer Umgebung nicht korrekt eingerichtet, ist das Ergebnis eine leere, aber fehlerfreie Seite. Nach Bereinigung der Konfiguration funktionierte die Anzeige wie vorgesehen.
|
Das ist charakteristisch für das Ablagekonzept: Ob ein Benutzer Dokumente sieht, hängt von einer Kette ab — Anmeldetoken, Organisationszuordnung, Metadatum am Kundenordner. Ist ein Glied falsch, zeigt die Seite keine Dokumente, aber auch keinen Fehler. Nach Bereinigung funktionierte die Anzeige.
|
||||||
|
|
||||||
Rückblickend hätte eine kurze Prüfliste für die Inbetriebnahme in einer neuen Umgebung — Speicher erreichbar, Testdaten vorhanden, Rolle vergeben, Organisationszuordnung gesetzt — den ersten Abschnitt der Abnahme deutlich verkürzt.
|
Eine Prüfliste für die Inbetriebnahme — Speicher erreichbar, Testdaten vorhanden, Rolle vergeben, Organisationszuordnung gesetzt — hätte diesen Abschnitt verkürzt.
|
||||||
|
|
||||||
\subsection{Funktionale Prüfung}
|
\subsection{Funktionale Prüfung}
|
||||||
|
|
||||||
Nach Behebung der Umgebungsprobleme fand am 24.~August die eigentliche funktionale Abnahme statt. Geprüft wurden die Paginierung, das Verhalten der Suche, die Sprungmarken der Freigabelinks, das Fehlerverhalten, die Behandlung von Verknüpfungsdateien und der Download.
|
Am 24.~August fand die funktionale Abnahme statt: Paginierung, Suchverhalten, Sprungmarken der Freigabelinks, Fehlerverhalten, Verknüpfungsdateien und Download.
|
||||||
|
|
||||||
Diese Prüfung verlief im Wesentlichen erfolgreich. Festgehalten wurde, dass einzelne Bestandteile zu diesem Zeitpunkt auf der Testumgebung noch nicht verfügbar waren — was daran lag, dass der letzte Pull Request noch offen war (siehe Abschnitt~\ref{sec:folder-management}). Die gefundenen Abweichungen wurden als eigene Fehlerberichte erfasst statt in der Abnahmediskussion abgehandelt zu werden.
|
Die Prüfung verlief überwiegend erfolgreich; einzelne Bestandteile waren nicht verfügbar, da der letzte Pull Request offen war (siehe Abschnitt~\ref{sec:folder-management}). Abweichungen wurden als Fehlerberichte erfasst.
|
||||||
|
|
||||||
\subsection{Der gefundene Fehler}
|
\subsection{Der gefundene Fehler}
|
||||||
|
|
||||||
Aus der funktionalen Prüfung ging ein Fehlerbericht hervor. Er betrifft nicht die Funktion, sondern die Konsistenz: Das Suchfeld des Dokumentenbereichs blendet nach einer Eingabe eine kleine Schaltfläche zum Leeren des Feldes ein. Diese Schaltfläche existiert auf den übrigen Houston-Seiten nicht, weshalb die Suche sich abweichend darstellt und verhält.
|
Ein Fehlerbericht: Das Suchfeld blendet nach Eingabe eine Schaltfläche zum Leeren ein, die auf den übrigen Houston-Seiten nicht existiert — eine Abweichung in Darstellung und Verhalten.
|
||||||
|
|
||||||
Für sich betrachtet ist die Schaltfläche eine sinnvolle Erleichterung. Der Fehler liegt nicht in ihrer Funktion, sondern darin, dass sie an dieser einen Stelle existiert und sonst nirgends. Genau das verletzt die Konsistenzanforderung (NFA-4).
|
Die Schaltfläche ist eine sinnvolle Erleichterung; der Fehler liegt darin, dass sie nur an dieser Stelle existiert, was NFA-4 (Konsistenz) verletzt.
|
||||||
|
|
||||||
Der Befund ist ein gutes Beispiel für die Grenzen der vorgelagerten Prüfstufen. Weder ein Unit-Test noch ein Code-Review hätte ihn finden können: Der erste prüft das Modul gegen sich selbst, der zweite den Quelltext einer Änderung. Sichtbar wird die Abweichung erst, wenn jemand die neue Seite neben den bestehenden Seiten betrachtet — und das leistet nur ein Abnahmetest durch eine Person, die die übrige Anwendung kennt. Der Fehler wurde für den folgenden Sprint eingeplant.
|
Weder Unit-Test noch Code-Review hätten ihn finden können; sichtbar wird die Abweichung erst beim Vergleich mit den bestehenden Seiten — das leistet nur ein Abnahmetest durch eine Person, die die Anwendung kennt. Der Fehler wurde für den folgenden Sprint eingeplant.
|
||||||
|
|||||||
@@ -3,42 +3,40 @@
|
|||||||
|
|
||||||
\subsection{Umfang}
|
\subsection{Umfang}
|
||||||
|
|
||||||
Die Umsetzung verteilte sich auf elf Pull Requests mit insgesamt 113 Diskussionssträngen. Tabelle~\ref{tab:pull-requests} im Anhang enthält die vollständige Übersicht mit Laufzeiten, Reviewern und Strangzahl. Kein Pull Request wurde abgelehnt oder verworfen; sämtliche abgeschlossenen erhielten eine Freigabe. Die inhaltliche Auseinandersetzung fand also durchgängig in den Diskussionssträngen statt und nicht über Ablehnungen.
|
Die Umsetzung verteilte sich auf elf Pull Requests mit 113 Diskussionssträngen (Tabelle~\ref{tab:pull-requests} im Anhang). Kein Pull Request wurde abgelehnt; sämtliche erhielten eine Freigabe. Die inhaltliche Auseinandersetzung fand durchgängig in den Diskussionssträngen statt.
|
||||||
|
|
||||||
Dass kein einziger Pull Request verworfen werden musste, ist kein Zufall. Die vorgelagerte Klärung — die Feature-Analyse im Juni und die Backlog-Durchsicht im Juli — hatte die fachlichen Fragen so weit beantwortet, dass keine Implementierung auf einer falschen Annahme beruhte. Der Aufwand der Vorklärung zahlte sich damit unmittelbar aus.
|
Dass kein Pull Request verworfen wurde, ist kein Zufall: Die vorgelagerte Klärung — Feature-Analyse im Juni, Backlog-Durchsicht im Juli — hatte die fachlichen Fragen so weit beantwortet, dass keine Implementierung auf einer falschen Annahme beruhte.
|
||||||
|
|
||||||
\subsection{Prüftiefe und Risiko}
|
\subsection{Prüftiefe und Risiko}
|
||||||
|
|
||||||
Bemerkenswert ist die Verteilung der Diskussionsstränge über die Pull Requests. Sie ist stark ungleich, folgt aber erkennbar dem Risiko der jeweiligen Änderung.
|
Die Verteilung der Diskussionsstränge folgt dem Risiko: Die intensivste Prüfung erfuhren Einzeldownload (30 Stränge) und Freigabelinks (23) — die Arbeiten, bei denen ein Fehler Kundendokumente offengelegt hätte. Der Document Explorer folgt mit 21 Strängen, die PDF-Vorschau mit zwei: eine Darstellungsfunktion ohne eigene Sicherheitsentscheidung.
|
||||||
|
|
||||||
Die intensivste Prüfung erfuhren der Einzeldownload mit 30 und die Freigabelinks mit 23 Diskussionssträngen — also genau die beiden Arbeiten, bei denen ein Fehler zur Offenlegung von Kundendokumenten geführt hätte. Der Document Explorer als Grundlage des Moduls folgt mit 21 Strängen. Am unteren Ende steht die PDF-Vorschau mit zwei Strängen: eine reine Darstellungsfunktion, die auf bereits geprüften Bausteinen aufsetzt und keine eigene Sicherheitsentscheidung trifft.
|
Die Verteilung entstand ohne Vorgabe — die Reviewer lenkten ihre Aufmerksamkeit intuitiv dorthin, wo ein Fehler teuer gewesen wäre.
|
||||||
|
|
||||||
Diese Verteilung entstand ohne ausdrückliche Vorgabe. Sie deutet darauf hin, dass die Reviewer ihre Aufmerksamkeit intuitiv dorthin lenkten, wo ein Fehler teuer gewesen wäre — ein Verhalten, das sich mit einer formalen Vorgabe kaum hätte erzwingen lassen.
|
|
||||||
|
|
||||||
\subsection{Wiederkehrende Themen}
|
\subsection{Wiederkehrende Themen}
|
||||||
|
|
||||||
Über alle Diskussionsstränge hinweg lassen sich fünf Muster erkennen.
|
Über alle Diskussionsstränge hinweg lassen sich fünf Muster erkennen.
|
||||||
|
|
||||||
\textbf{Sicherheit und Eingabeprüfung.} Der größte Anteil entfiel auf die Frage, ob eine von außen kommende Angabe ausreichend geprüft wird. Konkret betraf das die Pfadprüfung beim Download, den Verzicht auf das Durchreichen von Dateiströmen zugunsten zeitlich begrenzter Zugriffs-URLs, den bewussten Ausschluss benutzerdefinierter Symbole aus der Vorschau und den Grundsatz, keine Dateiinhalte über nicht authentifizierte Pfade auszuliefern.
|
\textbf{Sicherheit und Eingabeprüfung.} Der größte Anteil entfiel auf die Prüfung externer Angaben: Pfadprüfung beim Download, zeitlich begrenzte Zugriffs-URLs statt Dateiströme, Ausschluss benutzerdefinierter Symbole aus der Vorschau und der Grundsatz, keine Inhalte über nicht authentifizierte Pfade auszuliefern.
|
||||||
|
|
||||||
\textbf{Kompatibilität vor Ideallösung.} Mehrfach wurde eine technisch sauberere Lösung zugunsten einer verlässlich funktionierenden verworfen. Das prägnanteste Beispiel ist die Bereitstellung der Vorschaubilder als Rastergrafik, weil die Vektorvariante von verbreiteten Messengern nicht zuverlässig dargestellt wird (siehe Abschnitt~\ref{sec:pdf-preview}).
|
\textbf{Kompatibilität vor Ideallösung.} Mehrfach wurde eine technisch sauberere Lösung zugunsten einer verlässlicheren verworfen — etwa die Bereitstellung der Vorschaubilder als Rastergrafik, weil die Vektorvariante von verbreiteten Messengern nicht zuverlässig dargestellt wird (siehe Abschnitt~\ref{sec:pdf-preview}).
|
||||||
|
|
||||||
\textbf{Abgrenzung statt Ausweitung.} Im Review erkannte Probleme wurden konsequent als neue Backlog Items erfasst, statt sie im laufenden Pull Request mitzuerledigen. Das betrifft die Speicherabfrage aus dem Explorer und der Suche, die Typsuche und die Nebenläufigkeit im Lookup. Dieses Vorgehen hielt die Pull Requests auf ihren jeweiligen Gegenstand begrenzt und machte zugleich sichtbar, dass die Probleme erkannt und nicht übergangen wurden.
|
\textbf{Abgrenzung statt Ausweitung.} Erkannte Probleme wurden als neue Backlog Items erfasst statt im laufenden Pull Request miterledigt — etwa Speicherabfrage, Typsuche und Nebenläufigkeit. Das hielt die Pull Requests begrenzt und machte die Probleme sichtbar.
|
||||||
|
|
||||||
\textbf{Struktur und Wartbarkeit.} Wiederkehrend waren Hinweise auf auszulagernde Skripte in Seitenvorlagen, auf fehlertolerantes statt manuelles Auswerten von Aufzählungswerten, auf überflüssige Kommentare und auf uneinheitliche Benennungen.
|
\textbf{Struktur und Wartbarkeit.} Wiederkehrend: auszulagernde Skripte, fehlertolerantes Auswerten von Aufzählungswerten, überflüssige Kommentare, uneinheitliche Benennungen.
|
||||||
|
|
||||||
\textbf{Fachliche Klärung im Review.} In mehreren Fällen führte die Diskussion nicht zu einer Änderung am Code, sondern am Backlog Item. So wurde die Antwort bei fehlender Berechtigung von 404 auf 403 geändert und festgehalten, dass auch ein einzeln ausgewähltes Dokument als Archiv ausgeliefert wird. Das Review leistete damit auch Anforderungsarbeit — ein Hinweis darauf, dass sich Akzeptanzkriterien im Vorfeld nie vollständig formulieren lassen.
|
\textbf{Fachliche Klärung im Review.} In mehreren Fällen änderte die Diskussion nicht den Code, sondern das Backlog Item — etwa den Statuscode bei fehlender Berechtigung (403 statt 404) und die Festlegung, dass auch einzelne Dokumente als Archiv ausgeliefert werden. Das Review leistete damit Anforderungsarbeit.
|
||||||
|
|
||||||
\subsection{Wechsel der Reviewer}
|
\subsection{Wechsel der Reviewer}
|
||||||
|
|
||||||
Über die Projektlaufzeit wechselte der Hauptreviewer zweimal: \emph{Timo Walter} prüfte die fünf Pull Requests der Anfangsphase, \emph{Sarah Hinzmann} übernahm Anfang August, \emph{Robin Noack} die Schlussphase. Zusätzlich beteiligte sich \emph{Hanna Ebner} an den frühen Diskussionen.
|
Über die Projektlaufzeit wechselte der Hauptreviewer zweimal: \emph{Timo Walter} prüfte die fünf Pull Requests der Anfangsphase, \emph{Sarah Hinzmann} übernahm Anfang August, \emph{Robin Noack} die Schlussphase. Zusätzlich beteiligte sich \emph{Hanna Ebner} an den frühen Diskussionen.
|
||||||
|
|
||||||
Der Wechsel war organisatorisch bedingt, hatte aber einen erkennbaren fachlichen Effekt. Die Schwerpunkte verschoben sich mit den Personen: Die frühen Reviews befassten sich stark mit Architektur, Sicherheit und der Grundsatzfrage der Speicherabfrage; die mittleren mit Bedienung und Wartbarkeit; die späten mit Benennung, Fehlertoleranz und Codestil. Ein durchgehend gleicher Reviewer hätte diese Bandbreite vermutlich nicht abgedeckt, weil sich Aufmerksamkeitsmuster mit der Zeit verfestigen. Zugleich verteilte der Wechsel das Wissen über das neue Modul im Team.
|
Der Wechsel war organisatorisch bedingt, hatte aber einen fachlichen Effekt: Frühe Reviews befassten sich mit Architektur, Sicherheit und Speicherabfrage; mittlere mit Bedienung und Wartbarkeit; späte mit Benennung, Fehlertoleranz und Codestil. Ein gleicher Reviewer hätte diese Bandbreite vermutlich nicht abgedeckt; zugleich verteilte der Wechsel das Wissen über das Modul im Team.
|
||||||
|
|
||||||
\subsection{Kritische Betrachtung des Vorgehens}
|
\subsection{Kritische Betrachtung des Vorgehens}
|
||||||
|
|
||||||
Ein Punkt verdient eine selbstkritische Bewertung. Am 27.~Juli wurden vier Pull Requests am selben Tag eröffnet, ein fünfter folgte am Tag darauf. Da sie inhaltlich aufeinander aufbauten, ließ sich nur der erste zeitnah abschließen; die übrigen blieben zwischen zwei und drei Wochen offen und wurden erst nach dem Zusammenführen ihrer jeweiligen Vorgänger fertiggestellt.
|
Am 27.~Juli wurden vier Pull Requests am selben Tag eröffnet, ein fünfter folgte am Tag darauf. Da sie inhaltlich aufeinander aufbauten, ließ sich nur der erste zeitnah abschließen; die übrigen blieben zwei bis drei Wochen offen.
|
||||||
|
|
||||||
Die in Abschnitt~\ref{sec:architecture} beschriebene Verkettung machte die einzelnen Änderungen zwar gut prüfbar, verlagerte den Aufwand aber an das Ende: Der überwiegende Teil der Zusammenführungen fällt in die Woche vom 18. bis 21.~August — unmittelbar vor Abnahme und Produktivsetzung. Diese Ballung erhöhte den Druck in einer ohnehin kritischen Phase.
|
Die in Abschnitt~\ref{sec:architecture} beschriebene Verkettung machte die Änderungen gut prüfbar, verlagerte den Aufwand aber ans Ende: Der überwiegende Teil der Zusammenführungen fällt in die Woche vom 18. bis 21.~August — unmittelbar vor Abnahme und Produktivsetzung.
|
||||||
|
|
||||||
Rückblickend wäre ein stärker sequenzielles Vorgehen vorzuziehen gewesen: jeweils einen Pull Request abschließen, bevor der nächste eröffnet wird. Der Vorteil paralleler Bearbeitung — nie auf ein Review warten zu müssen — erwies sich als geringer als erwartet, weil die inhaltliche Abhängigkeit ein echtes paralleles Vorankommen ohnehin verhinderte.
|
Rückblickend wäre sequenzielles Vorgehen vorzuziehen gewesen, da die inhaltliche Abhängigkeit echtes paralleles Vorankommen ohnehin verhinderte.
|
||||||
|
|||||||
@@ -3,24 +3,24 @@
|
|||||||
|
|
||||||
\subsection{Umgebungen}
|
\subsection{Umgebungen}
|
||||||
|
|
||||||
Die Auslieferung folgt dem in Houston etablierten dreistufigen Weg über Entwicklungs-, Test- und Produktivumgebung. Für den Dokumentenbereich kommt hinzu, dass jede Stufe einen eigenen Speicher besitzt (siehe Abschnitt~\ref{sec:s3-client}). Eine Auslieferung umfasst damit nicht nur den Anwendungsstand, sondern setzt voraus, dass der zugehörige Speicher eingerichtet, erreichbar und in der jeweiligen Umgebung korrekt hinterlegt ist.
|
Die Auslieferung folgt dem in Houston etablierten dreistufigen Weg über Entwicklungs-, Test- und Produktivumgebung. Jede Stufe besitzt einen eigenen Speicher (siehe Abschnitt~\ref{sec:s3-client}); eine Auslieferung setzt voraus, dass dieser eingerichtet, erreichbar und korrekt hinterlegt ist.
|
||||||
|
|
||||||
Diese zusätzliche Abhängigkeit war der Grund dafür, dass die Bereitstellung der Infrastruktur (Abschnitt~\ref{sec:infrastructure}) bereits im Approval-Termin als Voraussetzung am ersten Backlog Item vermerkt worden war.
|
Deshalb wurde die Infrastrukturbereitstellung (Abschnitt~\ref{sec:infrastructure}) bereits im Approval-Termin als Voraussetzung vermerkt.
|
||||||
|
|
||||||
\subsection{Rollen als Teil der Auslieferung}
|
\subsection{Rollen als Teil der Auslieferung}
|
||||||
|
|
||||||
Der zweite umgebungsabhängige Bestandteil ist die Anwendungsrolle. Sie muss je Umgebung vorhanden sein und den betreffenden Personen zugewiesen werden. Beides ist kein Bestandteil des ausgelieferten Anwendungsstands, sondern eine begleitende Maßnahme.
|
Die Anwendungsrolle muss je Umgebung vorhanden sein und den betreffenden Personen zugewiesen werden — kein Bestandteil des Anwendungsstands, sondern eine begleitende Maßnahme.
|
||||||
|
|
||||||
Wie in Abschnitt~\ref{sec:acceptance-testing} beschrieben, führte genau dieser Punkt zu Verzögerungen im Abnahmetest. Vor der Produktivsetzung wurde er entsprechend ausdrücklich behandelt: Gegenstand der Abstimmung am 20.~August war die Frage, ob sämtliche Funktionen des Moduls tatsächlich hinter der Rolle liegen, bevor der Stand produktiv geht.
|
Dieser Punkt führte im Abnahmetest zu Verzögerungen (Abschnitt~\ref{sec:acceptance-testing}). Vor der Produktivsetzung wurde daher am 20.~August geprüft, ob sämtliche Funktionen des Moduls hinter der Rolle liegen.
|
||||||
|
|
||||||
Diese Prüfung ist nicht überflüssig, weil das Modul mehrere Einstiegspunkte besitzt. Neben der Dokumentenliste existieren eigene Routen für Download und Freigabe. Wäre eine davon versehentlich nicht von der zentralen Richtlinie erfasst, bliebe sie ohne Anmeldung erreichbar, ohne dass dies in der Bedienoberfläche sichtbar wäre — der Menüpunkt wäre weiterhin ausgeblendet. Die in Abschnitt~\ref{sec:architecture} beschriebene zentrale Registrierung der Autorisierung ist genau die Maßnahme, die diesen Fehler unwahrscheinlich macht; die Prüfung vor der Auslieferung bestätigt ihn zusätzlich.
|
Die Prüfung ist nötig, weil das Modul mehrere Einstiegspunkte besitzt: Wäre eine Route nicht von der zentralen Richtlinie erfasst, bliebe sie ohne Anmeldung erreichbar, ohne dass dies sichtbar wäre. Die zentrale Registrierung (Abschnitt~\ref{sec:architecture}) macht diesen Fehler unwahrscheinlich; die Prüfung bestätigt dies.
|
||||||
|
|
||||||
\subsection{Stand bei Abgabe}
|
\subsection{Stand bei Abgabe}
|
||||||
|
|
||||||
Zum Zeitpunkt der Erstellung dieser Dokumentation stellt sich der Auslieferungsstand wie folgt dar. Zehn der elf Pull Requests waren zusammengeführt; der überwiegende Teil davon in der Woche vom 18.~bis 21.~August. Der Dokumentenbereich war damit in seinen Kernfunktionen — Liste, Typisierung, Suche, Filter, Paginierung, Downloads, Vorschau, Freigabelinks und Verknüpfungsdateien — ausgeliefert.
|
Bei Abgabe waren zehn der elf Pull Requests zusammengeführt, überwiegend in der Woche vom 18.~bis 21.~August. Der Dokumentenbereich war damit in seinen Kernfunktionen — Liste, Typisierung, Suche, Filter, Paginierung, Downloads, Vorschau, Freigabelinks und Verknüpfungsdateien — ausgeliefert.
|
||||||
|
|
||||||
Nicht abgeschlossen war die Ordnerverwaltung mit dem neuen Kundenordner-Lookup. Der zugehörige Pull Request war fachlich fertiggestellt und ohne offene inhaltliche Anmerkungen, aber noch nicht freigegeben und nicht zusammengeführt. Bis dahin bleibt der Lookup bei dem in Abschnitt~\ref{sec:document-explorer} beschriebenen linearen Verfahren — funktional korrekt, aber mit der bekannten Skalierungsschwäche.
|
Nicht abgeschlossen war die Ordnerverwaltung mit dem neuen Kundenordner-Lookup. Der Pull Request war fachlich fertig, aber nicht freigegeben; bis dahin bleibt das in Abschnitt~\ref{sec:document-explorer} beschriebene lineare Verfahren — funktional korrekt, aber mit bekannter Skalierungsschwäche.
|
||||||
|
|
||||||
Ebenfalls offen sind das Backlog Item zur Nebenläufigkeit, das bewusst abgegrenzt und für die Umsetzung freigegeben ist, sowie der aus der Abnahme hervorgegangene Fehlerbericht, der für den folgenden Sprint eingeplant ist.
|
Ebenfalls offen sind das Backlog Item zur Nebenläufigkeit sowie der Fehlerbericht aus der Abnahme, der für den folgenden Sprint eingeplant ist.
|
||||||
|
|
||||||
Der Dokumentenbereich ist damit in Betrieb, aber nicht in allen Teilen abgeschlossen. Diese Unterscheidung ist für die Bewertung des Projekts wesentlich und wird in Abschnitt~\ref{sec:target-comparison} aufgegriffen.
|
Der Dokumentenbereich ist in Betrieb, aber nicht vollständig abgeschlossen (Abschnitt~\ref{sec:target-comparison}).
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
\section{Teststrategie}
|
\section{Teststrategie}
|
||||||
\label{sec:test-strategy}
|
\label{sec:test-strategy}
|
||||||
|
|
||||||
Die Qualitätssicherung stützte sich auf drei Stufen, die unterschiedliche Fehlerarten adressieren und zu unterschiedlichen Zeitpunkten wirken.
|
Die Qualitätssicherung stützte sich auf drei Stufen.
|
||||||
|
|
||||||
\textbf{Unit-Tests} sichern die Logik ab, die sich isoliert prüfen lässt und deren Verhalten von außen schwer zu beobachten ist. Das betrifft im vorliegenden Modul vor allem die Auflösung des Kundenordners und die Behandlung von Pfaden. Diese Tests laufen bei jedem Übersetzungsvorgang und melden Abweichungen unmittelbar.
|
\textbf{Unit-Tests} sichern isoliert prüfbare Logik ab — hier vor allem Kundenordner-Auflösung und Pfadbehandlung. Sie laufen bei jedem Übersetzungsvorgang.
|
||||||
|
|
||||||
\textbf{Code-Reviews} prüfen Entwurf, Lesbarkeit und Sicherheitseigenschaften. Sie erfassen Fehlerarten, für die ein automatisierter Test nicht formuliert werden kann, weil das erwartete Verhalten erst im Gespräch entsteht — etwa die Frage, ob eine bestimmte Information auf einem nicht authentifizierten Pfad ausgeliefert werden darf.
|
\textbf{Code-Reviews} prüfen Entwurf, Lesbarkeit und Sicherheitseigenschaften — Fehlerarten, für die kein automatisierter Test formulierbar ist, etwa ob eine Information auf einem nicht authentifizierten Pfad ausgeliefert werden darf.
|
||||||
|
|
||||||
\textbf{Abnahmetests} prüfen gegen die Akzeptanzkriterien aus fachlicher Sicht und in der tatsächlichen Umgebung. Sie erfassen Fehler, die aus dem Zusammenspiel mit der Konfiguration, den Berechtigungen und den realen Daten entstehen — also genau jene Klasse von Problemen, die in Unit-Tests und Reviews systematisch unsichtbar bleibt.
|
\textbf{Abnahmetests} prüfen gegen die Akzeptanzkriterien in der tatsächlichen Umgebung. Sie erfassen Fehler aus dem Zusammenspiel von Konfiguration, Berechtigungen und realen Daten — eine Klasse, die in Unit-Tests und Reviews unsichtbar bleibt.
|
||||||
|
|
||||||
Der Zuschnitt der automatisierten Tests folgte dabei bewusst dem Risiko und nicht einer Abdeckungsvorgabe. Für den Kundenordner-Lookup wurde die Testabdeckung ausdrücklich als Akzeptanzkriterium in das Backlog Item aufgenommen; für Darstellungsfunktionen wie die PDF-Vorschau geschah dies nicht. Die Begründung liegt im Verhältnis von Fehlerwahrscheinlichkeit zu Fehlerwirkung: Ein Fehler im Lookup führt zur Anzeige fremder Dokumente oder zu Datenverlust und ist im laufenden Betrieb schwer zu bemerken. Ein Fehler in der PDF-Vorschau ist beim ersten Aufruf offensichtlich.
|
Die automatisierten Tests folgten dem Risiko, nicht einer Abdeckungsvorgabe. Für den Kundenordner-Lookup war Testabdeckung ausdrücklich als Akzeptanzkriterium aufgenommen; für die PDF-Vorschau nicht. Ein Fehler im Lookup führt zur Anzeige fremder Dokumente oder Datenverlust und ist im Betrieb schwer zu bemerken; ein Fehler in der Vorschau ist beim ersten Aufruf offensichtlich.
|
||||||
|
|
||||||
Diese Priorisierung hat allerdings eine Kehrseite, die sich im Abnahmetest zeigte: Fehler in den Bereichen ohne automatisierte Abdeckung wurden erst dort gefunden. Abschnitt~\ref{sec:acceptance-testing} greift dies auf.
|
Diese Priorisierung hat eine Kehrseite: Fehler in Bereichen ohne automatisierte Abdeckung wurden erst im Abnahmetest gefunden (Abschnitt~\ref{sec:acceptance-testing}).
|
||||||
|
|||||||
@@ -3,30 +3,26 @@
|
|||||||
|
|
||||||
\subsection{Testbarkeit durch Kapselung}
|
\subsection{Testbarkeit durch Kapselung}
|
||||||
|
|
||||||
Die Voraussetzung für die Testbarkeit des Moduls wurde bereits mit dem Entwurf geschaffen. Da der Speicherzugriff ausschließlich über die Schnittstelle des SDK erfolgt, lässt sich diese in Tests durch eine Attrappe ersetzen. Die Tests laufen damit ohne Netzwerkverbindung, ohne Zugangsdaten und ohne einen realen Speicher.
|
Da der Speicherzugriff über die SDK-Schnittstelle erfolgt, lässt sich diese in Tests durch eine Attrappe ersetzen. Die Tests laufen damit ohne Netzwerkverbindung, ohne Zugangsdaten und ohne einen realen Speicher.
|
||||||
|
|
||||||
Dies ist mehr als eine Bequemlichkeit. Erst die Attrappe erlaubt es, Zustände herzustellen, die sich real kaum oder nur mit erheblichem Aufwand erzeugen ließen — etwa einen Kundenordner mit falschem Namen, aber korrektem Metadatum, oder einen Ordner ohne jedes Metadatum. Genau diese Randfälle sind es, die in der Praxis selten auftreten und deren Behandlung deshalb ohne Test unbemerkt fehlerhaft bleiben könnte.
|
Die Attrappe erlaubt Zustände, die sich real kaum erzeugen ließen — etwa einen Kundenordner mit falschem Namen, aber korrektem Metadatum, oder einen Ordner ohne Metadatum. Gerade diese Randfälle könnten ohne Test unbemerkt fehlerhaft bleiben.
|
||||||
|
|
||||||
\subsection{Abgedeckte Pfade}
|
\subsection{Abgedeckte Pfade}
|
||||||
|
|
||||||
Die Tests des Kundenordner-Lookups bilden die in Abschnitt~\ref{sec:lookup-decision} beschriebenen Wege einzeln ab: den Direktzugriff bei korrektem Ordnernamen, den Kollisionsfall, die Rückfallebene mit anschließender Umbenennung, das Anlegen eines fehlenden Kundenordners sowie das Ignorieren eines Ordners ohne gültiges Metadatum. Für jeden Weg wird nicht nur das Ergebnis geprüft, sondern auch, welche Zugriffe auf den Speicher tatsächlich erfolgt sind — beim Direktzugriff etwa, dass genau eine Metadatenabfrage und keine Auflistung stattgefunden hat.
|
Die Tests bilden die in Abschnitt~\ref{sec:lookup-decision} beschriebenen Wege einzeln ab: Direktzugriff, Kollisionsfall, Rückfallebene mit Umbenennung, Anlegen eines fehlenden Kundenordners und Ignorieren eines Ordners ohne gültiges Metadatum. Geprüft wird nicht nur das Ergebnis, sondern auch die Speicherzugriffe — beim Direktzugriff etwa, dass genau eine Metadatenabfrage und keine Auflistung stattfand.
|
||||||
|
|
||||||
Dieser Punkt verdient Beachtung: Die Anforderung an den Lookup war keine funktionale, sondern eine über den Aufwand. Ein Test, der lediglich das richtige Präfix prüft, würde auch dann bestehen, wenn die Implementierung weiterhin alle Ordner durchliefe. Die eigentliche Eigenschaft — dass der Regelfall mit einem einzigen Zugriff auskommt — lässt sich nur über die beobachteten Aufrufe der Attrappe prüfen.
|
Die Anforderung an den Lookup war keine funktionale, sondern eine über den Aufwand. Ein Test, der nur das richtige Präfix prüft, bestünde auch, wenn die Implementierung weiterhin alle Ordner durchliefe. Die eigentliche Eigenschaft — dass der Regelfall mit einem einzigen Zugriff auskommt — lässt sich nur über die beobachteten Aufrufe prüfen.
|
||||||
|
|
||||||
\subsection{Testdaten}
|
\subsection{Testdaten}
|
||||||
|
|
||||||
Ein Reviewfund betraf die verwendeten Testdaten. \emph{Timo Walter} merkte an, dass in den Tests reale Kundennamen verwendet wurden, und schlug Platzhalter vor.
|
\emph{Timo Walter} merkte an, dass reale Kundennamen in den Tests verwendet wurden, und schlug Platzhalter vor. Der Hinweis ist klein, in der Sache aber richtig: Testdaten liegen in der Versionsverwaltung und bleiben dauerhaft lesbar; ein realer Kundenname ist eine unnötige Offenlegung, da der Test mit Platzhaltern denselben Zweck erfüllt. Die Daten wurden ersetzt.
|
||||||
|
|
||||||
Der Hinweis ist inhaltlich klein, in der Sache aber richtig. Testdaten sind Quelltext: Sie liegen in der Versionsverwaltung, sind für jeden mit Zugriff auf das Repository lesbar und bleiben dort dauerhaft erhalten, auch wenn sie später geändert werden. Ein realer Kundenname in einem Testfall stellt damit eine unnötige Offenlegung dar — unnötig deshalb, weil der Test mit einem Platzhalternamen exakt denselben Zweck erfüllt. Die Testdaten wurden entsprechend ersetzt.
|
|
||||||
|
|
||||||
\subsection{Ein Test, der das Falsche prüfte}
|
\subsection{Ein Test, der das Falsche prüfte}
|
||||||
|
|
||||||
Der aufschlussreichste Fund der gesamten Qualitätssicherung betraf einen Test, der bestand — aber aus dem falschen Grund. \emph{Timo Walter} bemerkte beim Lesen, dass der Prüfling bereits in der ersten Zeile abbrach, weil die Auswertung des Pfades einen unbekannten Typbezeichner vorfand und daraufhin kein Ergebnis lieferte. Der Test endete damit, bevor die eigentlich zu prüfende Logik überhaupt erreicht war.
|
\emph{Timo Walter} bemerkte beim Lesen, dass ein Test bestand, aber aus dem falschen Grund: Der Prüfling brach in der ersten Zeile ab, weil die Pfadauswertung einen unbekannten Typbezeichner vorfand — die eigentlich zu prüfende Logik wurde nie erreicht.
|
||||||
|
|
||||||
Aus dieser Beobachtung leitete er zusätzlich einen vermuteten Fehler im Code ab: Ein Dokument, das unmittelbar im Kundenordner liegt und damit keinen Typ trägt, dürfe nicht dazu führen, dass die Auswertung scheitert.
|
Er leitete einen vermuteten Fehler ab: Ein Dokument ohne Typ dürfe die Auswertung nicht scheitern lassen. Die Klärung ergab, dass der Code korrekt, der Test aber missverständlich benannt war. Geprüft wurde eine Sicherheitseigenschaft: dass ein Benutzer, der den Kundennamen als Pfadbestandteil in die Adresse schreibt, kein Ergebnis erhält und keine Anfrage an den Speicher ausgelöst wird.
|
||||||
|
|
||||||
Die anschließende Klärung ergab, dass der Code korrekt war, der Test jedoch missverständlich benannt und aufgebaut. Geprüft wurde nämlich nicht der untypisierte Fall, sondern eine Sicherheitseigenschaft: dass ein Benutzer, der den Kundennamen selbst als Pfadbestandteil in die Adresse schreibt, kein Ergebnis erhält und dass daraufhin keine Anfrage an den Speicher abgesetzt wird. Der Kundenname darf im relativen Pfad niemals vorkommen — genau das war der Gegenstand des Tests.
|
Der Vorgang zeigt zweierlei: Ein Test, der aus dem falschen Grund grün ist, ist wertlos, weil er Sicherheit suggeriert. Der Fund war nicht durch Ausführen zu erzielen, sondern nur durch Lesen — er belegt den Wert des Reviews für Testcode.
|
||||||
|
|
||||||
Der Vorgang ist in zweifacher Hinsicht lehrreich. Zum einen zeigt er, dass ein bestandener Test keine Aussage über die geprüfte Eigenschaft trifft, solange nicht sichergestellt ist, dass er den relevanten Codepfad überhaupt erreicht — ein Test, der aus dem falschen Grund grün ist, ist wertlos und zugleich gefährlich, weil er Sicherheit suggeriert. Zum anderen zeigt er den Wert des Reviews für Testcode: Der Fund war nicht durch Ausführen zu erzielen, sondern nur durch Lesen.
|
Als Konsequenz wurde die Absicht des Tests explizit gemacht. Diese Anforderung — dass ein Test ohne den Fix fehlschlagen muss — wurde in die Akzeptanzkriterien des Folgeitems zur Nebenläufigkeit aufgenommen (siehe Abschnitt~\ref{sec:race-conditions}).
|
||||||
|
|
||||||
Als Konsequenz wurde die Absicht des Tests explizit gemacht. Genau diese Anforderung — dass ein Test ohne den zugehörigen Fix fehlschlagen muss — wurde später auch in die Akzeptanzkriterien des Folgeitems zur Nebenläufigkeit aufgenommen (siehe Abschnitt~\ref{sec:race-conditions}).
|
|
||||||
|
|||||||
Reference in New Issue
Block a user