Fix: Typos

This commit is contained in:
2026-03-23 07:21:13 +01:00
parent 6482c6230c
commit 3460dd34b6

View File

@@ -36,10 +36,10 @@ Ein wesentlicher Teil meiner Arbeit lag in der Bereitstellung der technischen Ba
\ \
Zusätzlich zur Infrastruktur habe ich drei zentrale Module für den Alumnihub entworfen und implementiert: Zusätzlich zur Infrastruktur habe ich drei zentrale Module für den AlumniHub entworfen und implementiert:
1. **Anmeldetool (EventRegistration)**: Ein Modul zum verwalten von Anmeldungen für Veranstaltungen. Es ermöglicht die einfache Erstellung von Einladungen zu Veranstaltungen und die Verwaltung von Anmeldungen. 1. **Anmeldetool (EventRegistration)**: Ein Modul zum Verwalten von Anmeldungen für Veranstaltungen. Es ermöglicht die einfache Erstellung von Einladungen sowie die Verwaltung von Anmeldungen.
2. **Reporting System**: Ein generisches System zur Meldung von Inhalten, das nach dem *Open-Closed-Prinzip* entworfen wurde, um eine einfache Erweiterbarkeit für alle anderen Module zu bieten. 2. **Reporting-System**: Ein generisches System zur Meldung von Inhalten, das nach dem *Open-Closed-Prinzip* entworfen wurde, um eine einfache Erweiterbarkeit für alle anderen Module zu bieten.
3. **Schwarzes Brett (BlackBoard)**: Ein Community-Modul für den Austausch von Informationen, inklusive eines automatisierten E-Mail-Digest-Systems zur Nutzerbenachrichtigung. 3. **Schwarzes Brett (BlackBoard)**: Ein Community-Modul für den Austausch von Informationen, inklusive eines automatisierten E-Mail-Digest-Systems zur Nutzerbenachrichtigung.
@@ -60,11 +60,11 @@ Das Ziel des Anmeldetools ist die effiziente Organisation von Vereinsveranstaltu
- **Teilnehmerliste**: Für jede Veranstaltung muss eine Übersicht der Rückmeldungen (Anmeldungen/Absagen) für Administratoren einsehbar sein. - **Teilnehmerliste**: Für jede Veranstaltung muss eine Übersicht der Rückmeldungen (Anmeldungen/Absagen) für Administratoren einsehbar sein.
- **Statistische Auswertung**: Die Rückmeldungen sollen grafisch (z. B. als Tortendiagramm) aufbereitet werden, um die Planung zu erleichtern. - **Statistische Auswertung**: Die Rückmeldungen sollen grafisch (z. B. als Tortendiagramm) aufbereitet werden, um die Planung zu erleichtern.
#### Globales Reporting System #### Reporting-System
\ \
Das Reporting System dient der Qualitätssicherung von Benutzerinhalten über alle Module hinweg. Das Reporting-System dient der Qualitätssicherung von Benutzerinhalten über alle Module hinweg.
- **Inhalte melden**: Benutzer müssen die Möglichkeit haben, unangemessene Einträge (z. B. am Schwarzen Brett) zu melden. Dabei muss ein Grund für die Meldung angegeben werden können. - **Inhalte melden**: Benutzer müssen die Möglichkeit haben, unangemessene Einträge (z. B. am Schwarzen Brett) zu melden. Dabei muss ein Grund für die Meldung angegeben werden können.
- **Generische Schnittstelle**: Das System muss so entworfen sein, dass beliebige andere Module (z. B. Hall of Fame) die Meldefunktion ohne Änderungen am Kern des Reporting-Systems einbinden können. - **Generische Schnittstelle**: Das System muss so entworfen sein, dass beliebige andere Module (z. B. Hall of Fame) die Meldefunktion ohne Änderungen am Kern des Reporting-Systems einbinden können.
@@ -117,7 +117,7 @@ Table: Wesentliche Use Cases der entwickelten Module
Es ist zu beachten, dass es sich hierbei um eine Auswahl handelt und nicht alle Use Cases der Module abgebildet werden. Es ist zu beachten, dass es sich hierbei um eine Auswahl handelt und nicht alle Use Cases der Module abgebildet werden.
## Technisches Umfeld ## Technologien
Mein Aufgabenbereich umfasst einerseits die Entwicklung eigener Module, sowie das Bereitstellen des Services. Mein Aufgabenbereich umfasst einerseits die Entwicklung eigener Module, sowie das Bereitstellen des Services.
@@ -127,18 +127,18 @@ Mein Aufgabenbereich umfasst einerseits die Entwicklung eigener Module, sowie da
\ \
Auch steht die Wahl der Programmiersprache und des CMS an. Nachdem wir im Unterricht fast ausschließlich mit C# entwickelt haben und nicht in eine komplett unbekannte Entwicklungsumgebung abdriften wollten, haben wir uns für die Webentwicklung mit ASP.NET Core 9 (Upgrade im Lauf der Diplomarbeit auf .NET Core 10) und dem CMS Oqtane entschieden. Auch hier gab es einige Kandidaten: Auch steht die Wahl der Programmiersprache und des CMS an. Nachdem wir im Unterricht fast ausschließlich mit C# entwickelt haben und nicht in eine komplett unbekannte Entwicklungsumgebung abdriften wollten, haben wir uns für die Webentwicklung mit ASP.NET Core 9 (Upgrade im Laufe der Diplomarbeit auf .NET Core 10) und dem CMS Oqtane entschieden. Auch hier gab es einige Kandidaten:
- Piranha CMS - Piranha CMS
> Piranha erscheint auf den ersten Blick nicht so flexibel wie Oqtane, es basiert auf .NET 8.0 und wird nicht so aktiv gewartet. > Piranha erscheint auf den ersten Blick nicht so flexibel wie Oqtane, es basiert auf .NET 8.0 und wird nicht so aktiv gewartet.
- Umbraco - Umbraco
> Bei Umbraco muss viel in der Admin Oberfläche von dem CMS gearbeitet werden, im Großen und Ganzen wirkt dieses CMS nicht so flexibel. Die Dokumentation wirkt auf den ersten Blick sehr gut! > Bei Umbraco muss viel in der Admin-Oberfläche vom CMS gearbeitet werden, im Großen und Ganzen wirkt dieses CMS nicht so flexibel. Die Dokumentation wirkt auf den ersten Blick sehr gut!
- DNN / Dot Net Nuke - DNN / Dot Net Nuke
> Dieses CMS ist der Platzhirsch. Es wird von der DNN Foundation gewartet, arbeitet mit dem Dotnet Framework, welches nicht unter Linux läuft. Und ein Windows Server ist im Betrieb teurer und in der Absicherung aufwändiger. > Dieses CMS ist der Platzhirsch. Es wird von der DNN Foundation gewartet und arbeitet mit dem Dotnet Framework, welches nicht unter Linux läuft. Ein Windows Server ist im Betrieb teurer und in der Absicherung aufwändiger.
- Oqtane - Oqtane
> Oqtane wirkt auf sehr modular und flexibel, auch innerhalb von Modulen kann man auf alle Funktionen des ASP.NET Core Frameworks verwenden. Die Dokumentation wirkt nicht besonders gut, aber ausreichend. Die enthaltenen Fehlinformationen und mangelnden Anleitungen für den Betrieb mit Linux sind erst im Nachhinein aufgefallen. > Oqtane wirkt sehr modular und flexibel, auch innerhalb von Modulen kann man alle Funktionen des ASP.NET Core Frameworks verwenden. Die Dokumentation wirkt nicht besonders gut, aber ausreichend. Die enthaltenen Fehlinformationen und mangelnden Anleitungen für den Betrieb mit Linux sind erst im Nachhinein aufgefallen.
Insbesondere aufgrund seiner sehr hohen Flexibilität, haben wir uns am Ende für Oqtane entschieden. Insbesondere aufgrund seiner sehr hohen Flexibilität haben wir uns am Ende für Oqtane entschieden.
#### Entscheidungsfindung restliche Infrastruktur #### Entscheidungsfindung restliche Infrastruktur
@@ -152,7 +152,7 @@ Im Bereich der Datenbanken musste ich mir ein paar Fragen stellen:
2. Mit welcher speziellen Implementierung bekommen wir Support und bei welcher haben wir Vorwissen im Team? 2. Mit welcher speziellen Implementierung bekommen wir Support und bei welcher haben wir Vorwissen im Team?
3. Ist das auserkorene System kompatibel mit dem CMS, auf dem wir aufbauen? 3. Ist das auserkorene System kompatibel mit dem CMS, auf dem wir aufbauen?
Es war von Anfang an klar, dass es ein SQL-basiertes System wird, da wir im Team nur mit SQL-basierten Systemen Erfahrungen haben. Außerdem unterstützt unser CMS (Oqtane) nur SQL-basierte Systeme. In der Linuxwelt kommen jetzt nur noch ein paar Datenbankmanagementsysteme in die Auswahl: PostgreSQL, MySQL / MariaDB, SQLite. Da ist die Wahl auf PostgreSQL gefallen. Grund dafür war meine Vorerfahrung mit diesem Datenbankmanagementsystem, welche ich in meinem Nebenjob erlangt habe. Es war von Anfang an klar, dass es ein SQL-basiertes System wird, da wir im Team nur mit SQL-basierten Systemen Erfahrungen haben. Außerdem unterstützt unser CMS (Oqtane) nur SQL-basiierte Systeme. In der Linux-Welt kommen jetzt nur noch ein paar Datenbankmanagementsysteme in die Auswahl: PostgreSQL, MySQL / MariaDB, SQLite. Da ist die Wahl auf PostgreSQL gefallen. Grund dafür war meine Vorerfahrung mit diesem Datenbankmanagementsystem, welche ich in meinem Nebenjob erlangt habe.
### Beschreibung und Architektur von Oqtane ### Beschreibung und Architektur von Oqtane
@@ -170,17 +170,17 @@ Ein Modul in Oqtane besteht aus vier Projekten:
- Im Server-Projekt liegt Sourcecode, welcher serverseitig ausgeführt werden soll. Dazu gehören unter anderem alle Repositories, Controller, Manager, Migrationen und Server-Services und Server-Startuplogik. - Im Server-Projekt liegt Sourcecode, welcher serverseitig ausgeführt werden soll. Dazu gehören unter anderem alle Repositories, Controller, Manager, Migrationen und Server-Services und Server-Startuplogik.
- Im Client-Projekt liegen Code und Razor-Komponenten für den Client. Also Client-Startuplogik, Client-Services, Ressourcendateien (.resx), die Komponenten / das User Interface und die Moduldefinitionen für jedes Modul. - Im Client-Projekt liegen Code und Razor-Komponenten für den Client. Also Client-Startuplogik, Client-Services, Ressourcendateien (.resx), die Komponenten / das User-Interface und die Moduldefinitionen für jedes Modul.
- Im Shared-Projekt wird geteilter Sourcecode abgelegt, der server- und clientseitig verwendet wird. In der Praxis bleibt es hierbei bei den EntityFramework-Modellen zum Speichern der Daten im Arbeitsspeicher. - Im Shared-Projekt wird geteilter Sourcecode abgelegt, der server- und clientseitig verwendet wird. In der Praxis bleibt es hierbei bei den EntityFramework-Modellen zum Speichern der Daten im Arbeitsspeicher.
- Im Package Projekt findet man Skripte zum Debuggen und Releasen eines Moduls und die NuGet-Spezifikation. - Im Package-Projekt findet man Skripte zum Debuggen und Releasen eines Moduls und die NuGet-Spezifikation.
- Beim Debug werden die DLLs, PDBs und statischen Assets wie Skripte und Stylesheets der drei anderen Projekte in den bereits gebauten Oqtane.Server `oqtane.framework/oqtane.server/bin/debug/net10.0/...` kopiert. - Beim Debug werden die DLLs, PDBs und statischen Assets wie Skripte und Stylesheets der drei anderen Projekte in den bereits gebauten Oqtane.Server `oqtane.framework/oqtane.server/bin/debug/net10.0/...` kopiert.
- Beim Release wird ein NuGet-Paket erstellt und unter oqtane.framework/oqtane.server/Packages abgelegt. Dort abgelegte NuGet-Pakete werden beim nächsten Start des Oqtane Servers installiert (Datenbank Migrationen werden gemacht und die Pakete entpackt). - Beim Release wird ein NuGet-Paket erstellt und unter oqtane.framework/oqtane.server/Packages abgelegt. Dort abgelegte NuGet-Pakete werden beim nächsten Start des Oqtane-Servers installiert (Datenbank-Migrationen werden gemacht und die Pakete entpackt).
### Zusammenspiel der Infrastruktur ### Zusammenspiel der Infrastruktur
In diesem Kapitel erkläre ich wie die ausgewählten Komponenten zusammenspielen. In diesem Kapitel erkläre ich, wie die ausgewählten Komponenten zusammenspielen.
#### NginX as Reverse Proxy #### NginX as Reverse Proxy
@@ -189,12 +189,12 @@ In diesem Kapitel erkläre ich wie die ausgewählten Komponenten zusammenspielen
NginX fungiert in unserer Infrastruktur als `Reverse Proxy`. Ein Reverse Proxy nimmt Anfragen aus dem Internet entgegen und leitet sie an interne Server (wie Kestrel) weiter. Dies bietet mehrere Vorteile: NginX fungiert in unserer Infrastruktur als `Reverse Proxy`. Ein Reverse Proxy nimmt Anfragen aus dem Internet entgegen und leitet sie an interne Server (wie Kestrel) weiter. Dies bietet mehrere Vorteile:
- **Sicherheit**: Die interne Applikation ist nicht direkt dem Internet ausgesetzt. - **Sicherheit**: Die interne Applikation ist nicht direkt dem Internet ausgesetzt.
- **SSL-Terminierung**: NginX übernimmt die rechenintensive Verschlüsselung (HTTPS), während die Applikation dahinter über einfaches HTTP kommuniziert. - **SSL-Terminierung**: Nginx übernimmt die rechenintensive Verschlüsselung (HTTPS), während die Applikation dahinter über einfaches HTTP kommuniziert.
- **Statische Inhalte**: NginX kann statische Dateien (Bilder, CSS) effizienter ausliefern als ein Applikationsserver. - **Statische Inhalte**: Nginx kann statische Dateien (Bilder, CSS) effizienter ausliefern als ein Applikationsserver.
Wir verwenden NginX für die SSL Terminierung. Das Zertifikat wird von Let's Encrypt bereit gestellt und mittels HTTP-Challenges und dem Certbot auf dem Server aktualisiert. Wir verwenden Nginx für die SSL-Terminierung. Das Zertifikat wird von Lets Encrypt bereitgestellt und mittels HTTP-Challenges und dem Certbot auf dem Server aktualisiert.
Hier ist ein Auszug der NginX-Konfiguration (`nginx.conf`) für den Alumnihub: Hier ist ein Auszug der NginX-Konfiguration (`nginx.conf`) für den AlumniHub:
```nginx ```nginx
server { server {
@@ -215,7 +215,7 @@ server {
} }
``` ```
Oqtane selber läuft als Systemd-Service im Kestrel Backend. Kestrel ist ein kleiner Webserver, welcher in das ASP.NET Core Framework eingebaut worden ist. Oqtane (bzw. der ASP.NET Core Server "Kestrel") hört auf der Loopback IP und Port 5000. Damit ist Oqtane nur durch NginX erreichbar. PostgreSQL ist die Datenbank in dem System: Sie hört wieder auf der Loopback IP und Port 5432. In der folgenden Grafik ist das System Schematisch dargestellt. Oqtane selbst läuft als Systemd-Service im Kestrel-Backend. Kestrel ist ein kleiner Webserver, welcher in das ASP.NET Core Framework eingebaut worden ist. Oqtane (bzw. der ASP.NET Core Server "Kestrel") hört auf der Loopback-IP und Port 5000. Damit ist Oqtane nur durch Nginx erreichbar. PostgreSQL ist die Datenbank in dem System: Sie hört wieder auf der Loopback-IP und Port 5432. In der folgenden Grafik ist das System schematisch dargestellt.
```mermaid ```mermaid
sequenceDiagram sequenceDiagram
@@ -243,7 +243,7 @@ Zusätzlich gab es einen Administrationszugang zu den Servern, welcher über SSH
Table: SSH Zugänge in den unterschiedlichen Umgebungen Table: SSH Zugänge in den unterschiedlichen Umgebungen
Die VPN basierten Zugänge sind tendenziell schwieriger zu finden und auszunutzen, während die Lösung in der Schule mittels Highport den SSH Service öffentlich erreichbar macht. Durch den `Highport` ist der SSH Service schwieriger zu finden. Zur Authentifizierung mit SSH verwenden wir SSH-Keys, da diese durch ihre komplexität sicherer sind, als Passwörter. Die VPN-basierten Zugänge sind tendenziell schwieriger zu finden und auszunutzen, während die Lösung in der Schule mittels Highport den SSH-Service öffentlich erreichbar macht. Durch den `Highport` ist der SSH-Service schwieriger zu finden. Zur Authentifizierung mit SSH verwenden wir SSH-Keys, da diese durch ihre Komplexität sicherer sind als Passwörter.
### Entwicklung mit ASP.NET ### Entwicklung mit ASP.NET
@@ -251,7 +251,7 @@ Die VPN basierten Zugänge sind tendenziell schwieriger zu finden und auszunutze
\ \
Blazor ist ein kostenloses und quelloffenes Web-Framework, welches es möglich macht Benutzeroberflächen für Web-Browser, basierend auf C# und HTML, zu erstellen. Es wird von Microsoft als teil des ASP.NET Core Frameworks entwickelt. Blazor ist ein kostenloses und quelloffenes Web-Framework, welches es ermöglicht, Benutzeroberflächen für Web-Browser basierend auf C# und HTML zu erstellen. Es wird von Microsoft als Teil des ASP.NET Core Frameworks entwickelt.
Blazor hat mehrere Hosting-Modelle: Blazor hat mehrere Hosting-Modelle:
@@ -265,9 +265,9 @@ Blazor hat mehrere Hosting-Modelle:
Table: Vergleich der Blazor Hosting-Modelle Table: Vergleich der Blazor Hosting-Modelle
Im Rahmen dieser Diplomarbeit haben wir und für den RenderModus Interactive Server entschieden. Dieser Modus ist für Oqtane die Standardeinstellung und bietet eine gute Balance zwischen Leistung und Benutzerfreundlichkeit. Das ist für unseren Anwendungsfall auch die Empfehlung von Oqtane selbst. [@oqtane_docs_rendermodes] Im Rahmen dieser Diplomarbeit haben wir uns für den Rendermodus Interactive Server entschieden. Dieser Modus ist für Oqtane die Standardeinstellung und bietet eine gute Balance zwischen Leistung und Benutzerfreundlichkeit. Das ist für unseren Anwendungsfall auch die Empfehlung von Oqtane selbst. [@oqtane_docs_rendermodes]
Razor-Komponenten (in dieser Arbeit, sowie umgangssprachlich, auch oft nur Komponenten genannt) sind der Grundbaustein für Blazor Web Apps. Sie bestehen aus HTML, welches mit der Verwendung von inline C# beeinflusst werden kann. Blazor stellt sicher, dass das gerenderte Markup aktualisiert wird, wenn sich der Status der Komponente ändert. Dieser Code kann entweder vollständig in einer .razor Datei liegen, oder in einer seperaten Code-Behind-Datei. Inline C# Code wird mithilfe von `@`- Zeichen markiert. Hier ist ein Beispiel für einen einfachen Counter: Razor-Komponenten (in dieser Arbeit sowie umgangssprachlich auch oft nur Komponenten genannt) sind der Grundbaustein für Blazor Web Apps. Sie bestehen aus HTML, welches mit der Verwendung von Inline-C# beeinflusst werden kann. Blazor stellt sicher, dass das gerenderte Markup aktualisiert wird, wenn sich der Status der Komponente ändert. Dieser Code kann entweder vollständig in einer .razor-Datei liegen oder in einer separaten Code-Behind-Datei. Inline-C#-Code wird mithilfe von `@`-Zeichen markiert. Hier ist ein Beispiel für einen einfachen Counter:
```razor ```razor
<h1>Counter</h1> <h1>Counter</h1>
@@ -289,7 +289,7 @@ Razor-Komponenten (in dieser Arbeit, sowie umgangssprachlich, auch oft nur Kompo
Mit`@count` in Zeile 3 wird der Wert der Variablen count in den `<p>` Tag mit eingebaut. Mit `@onclick="Increment"` in Zeile 5 wird die onclick Property vom `<button>`Tag auf die Increment Methode im C# Code gesetzt. Der `@code` Block in Zeile 7 ist der C# Code, welcher diese Komponente dynamisch macht. Hier ist die Variable count und die Methode Increment definiert, welche dieser Komponente interaktiv macht. Mit`@count` in Zeile 3 wird der Wert der Variablen count in den `<p>` Tag mit eingebaut. Mit `@onclick="Increment"` in Zeile 5 wird die onclick Property vom `<button>`Tag auf die Increment Methode im C# Code gesetzt. Der `@code` Block in Zeile 7 ist der C# Code, welcher diese Komponente dynamisch macht. Hier ist die Variable count und die Methode Increment definiert, welche dieser Komponente interaktiv macht.
Razor hat auch eine Reihe an Keywords, wie zum Beispiel (nur Auszugsweise, bzw. die die wir verwendet haben): Razor hat auch eine Reihe an Keywords, wie zum Beispiel (nur auszugsweise bzw. die, die wir verwendet haben):
- namespace: Gibt den aktuellen Namespace in der Razor Datei an. - namespace: Gibt den aktuellen Namespace in der Razor Datei an.
- inherits: Gibt die Superklasse der generierten C# Klasse an. - inherits: Gibt die Superklasse der generierten C# Klasse an.
@@ -338,7 +338,7 @@ In den folgenden beiden Kapiteln wird das Dependency Inversion Principle und das
\ \
Das Dependency-Inversion-Principle (DIP / auf Deutsch: Abhängigkeits-Umkehr-Prinzip) ist eines von den fünf `SOLID` Prinzipien in der Softwareentwicklung. Das Dependency-Inversion-Principle (DIP / auf Deutsch: Abhängigkeits-Umkehr-Prinzip) ist eines der fünf SOLID-Prinzipien in der Softwareentwicklung.
Das DIP unterscheidet zwischen high-level und low-level Modulen. Das DIP unterscheidet zwischen high-level und low-level Modulen.
@@ -444,15 +444,15 @@ Gitea, das Versionskontrollsystem dieser Diplomarbeit, hat einen Continuous-Inte
Man kann definieren auf welcher Änderung im Git Repository die Pipeline losgetreten wird (Keyword: `on`) und entweder eigene Kommandos aufreihen, oder auf bestehende `actions` zurückgreifen, welche dann der Reihe nach ausgeführt werden (Keyword: `jobs`). Man kann definieren auf welcher Änderung im Git Repository die Pipeline losgetreten wird (Keyword: `on`) und entweder eigene Kommandos aufreihen, oder auf bestehende `actions` zurückgreifen, welche dann der Reihe nach ausgeführt werden (Keyword: `jobs`).
Die meisten Pipelines sind folgendermaßen Aufgebaut: Die meisten Pipelines sind folgendermaßen aufgebaut:
Clone -> Checkout -> Submodule Checkout (optional) -> Dependencies einrichten (zum Beispiel das dotnet SDK) -> Build ausführen. -> Release erstellen und Artefakte veröffentlichen (z.B. in Registries). Aber man kann auch andere Dinge tun, z.B. mithilfe von Künstlicher Intelligenz Code und Dokumentation überprüfen. Clone -> Checkout -> Submodule-Checkout (optional) -> Dependencies einrichten (zum Beispiel das dotnet SDK) -> Build ausführen -> Release erstellen und Artefakte veröffentlichen (z. B. in Registries). Aber man kann auch andere Dinge tun, z. B. mithilfe von künstlicher Intelligenz Code und Dokumentation überprüfen.
Anwendungen von Gitea Actions bei dieser Diplomarbeit: Anwendungen von Gitea Actions bei dieser Diplomarbeit:
- **APT-Package Repository**: Zum Bauen von Oqtane und allen Modulen, verpacken in ein .deb Paket und in die Registry pushen. - **APT-Package Repository**: Zum Bauen von Oqtane und allen Modulen, Verpacken in ein .deb-Paket und Pushen in die Registry.
- **Interfaces Projekt**: Zum Bauen vom Interfaces-Projekt, verpacken in ein NuGet Paket und in die Registry pushen. Die Versionierung des NuGet-Pakets erfolgt dabei automatisiert über Git-Tags, was eine konsistente Verknüpfung zwischen Quellcode-Stand und Paketversion sicherstellt. - **Interfaces-Projekt**: Zum Bauen vom Interfaces-Projekt, Verpacken in ein NuGet-Paket und Pushen in die Registry. Die Versionierung des NuGet-Pakets erfolgt dabei automatisiert über Git-Tags, was eine konsistente Verknüpfung zwischen Quellcode-Stand und Paketversion sicherstellt.
- **ursprünglich: oqtane.framework**: Zum bauen und Verpacken in einen Docker Container und in die Registry pushen. - **ursprünglich: oqtane.framework**: Zum Bauen und Verpacken in einen Docker-Container und Pushen in die Registry.
- **PM Repository**: Zum automatischen Überprüfen der Dokumente, unter anderem, mithilfe von KI, wie zum Beispiel Gemini. - **PM Repository**: Zum automatischen Überprüfen der Dokumente unter anderem mithilfe von KI, wie zum Beispiel Gemini.
Ein Beispiel für eine Konfiguration einer Gitea Action: Ein Beispiel für eine Konfiguration einer Gitea Action:
@@ -492,7 +492,7 @@ jobs:
./alumnihub/opt/alumnihub/Packages/*.nupkg ./alumnihub/opt/alumnihub/Packages/*.nupkg
``` ```
Die Konfiguration führt teilweise vorgefertigte Actions aus, wie zum Beispiel das Einrichten des .NET SDKs oder das Erstellen eines Releases. Aber es werden auch eigene Skripte ausgeführt, wie zum Beispiel das Bauen des .deb Pakets. Dieses ist im Repository unter `./run-build.sh` zu finden. Dieses Skript ist für die Automatisierung des Build-Prozesses zuständig und kümmert sich um das Kompilieren der Anwendung, das Erstellen des Debian-Pakets und das Hochladen in die Registry. Abgelegt ist es im APT-Package Repository unter `./gitea/workflows`. Die Konfiguration führt teilweise vorgefertigte Actions aus, wie zum Beispiel das Einrichten des .NET SDKs oder das Erstellen eines Releases. Aber es werden auch eigene Skripte ausgeführt, wie zum Beispiel das Bauen des .deb-Pakets. Dieses ist im Repository unter `./run-build.sh` zu finden. Dieses Skript ist für die Automatisierung des Build-Prozesses zuständig und kümmert sich um das Kompilieren der Anwendung, das Erstellen des Debian-Pakets und das Hochladen in die Registry. Abgelegt ist es im APT-Package-Repository unter `./gitea/workflows`.
### Debian Paket ### Debian Paket
@@ -525,7 +525,7 @@ Durch diesen Prozess wird sichergestellt, dass jede Version der Software eindeut
### Scrum ### Scrum
Zum verwalten des Projektes haben wir uns an den Grundprinzipien von Scrum orientiert, um flexibel auf Änderungen reagieren zu können. Die Arbeit wurde in 14-tägige Sprints unterteilt. Zum Verwalten des Projektes haben wir uns an den Grundprinzipien von Scrum orientiert, um flexibel auf Änderungen reagieren zu können. Die Arbeit wurde in 14-tägige Sprints unterteilt.
- **Sprint Planning**: Zu Beginn jedes Sprints wurden die Aufgaben aus dem Product Backlog in das Sprint Backlog übernommen. - **Sprint Planning**: Zu Beginn jedes Sprints wurden die Aufgaben aus dem Product Backlog in das Sprint Backlog übernommen.
- **Dailies/Weekly**: Wir hielten wöchentliche Treffen ab, um den Fortschritt zu synchronisieren und Blocker zu identifizieren. - **Dailies/Weekly**: Wir hielten wöchentliche Treffen ab, um den Fortschritt zu synchronisieren und Blocker zu identifizieren.
@@ -567,7 +567,7 @@ Als schlanke und selbst gehostete Open-Source-Alternative zu Plattformen wie Git
\ \
Ein Repository bildet den zentralen Speicherort für einen Projektteil. In Gitea wurden separate Repositories für die einzelnen Module und Themes, das Oqtane Framework, Skripte, die Dokumentation angelegt. Ein zusätzliches Repository bindet alle übrigen Quellcode-Repositories als Submodule ein, das macht die Einrichtung einer neuen Entwicklungsumgebung sehr kompfortabel. Dies ermöglichte eine saubere Trennung der verschiedenen Projektkomponenten. [@gitea_docs] Ein Repository bildet den zentralen Speicherort für einen Projektteil. In Gitea wurden separate Repositories für die einzelnen Module und Themes, das Oqtane Framework, Skripte, die Dokumentation angelegt. Ein zusätzliches Repository bindet alle übrigen Quellcode-Repositories als Submodule ein, das macht die Einrichtung einer neuen Entwicklungsumgebung sehr komfortabel. Dies ermöglichte eine saubere Trennung der verschiedenen Projektkomponenten. [@gitea_docs]
#### Issues #### Issues
@@ -613,13 +613,13 @@ Gitea fungierte zusätzlich als Register für Pakete und Container-Images. Selbs
### Admin Modules ### Admin Modules
Eine C#-Solution, welche einige Module, welche für den Admineinsatz geschrieben worden sind beinhaltet. Dieses Modul besteht aus 3 Teilmodulen. Einem Modul für den Versand von Rundmails, eines für die Einstellung von der Token Lebenszeit bei Tokens, welche per E-Mail verschickt werden und eines, welches das Reporting-System übernimmt. Eine C#-Solution, welche einige für den Admin-Einsatz geschriebene Module beinhaltet. Dieses Modul besteht aus drei Teilmodulen: einem Modul für den Versand von Rundmails, eines für die Einstellung der Token-Lebenszeit bei Tokens, welche per E-Mail verschickt werden, und eines, welches das Reporting-System übernimmt.
#### Mass Mailing #### Mass Mailing
\ \
Das Mass Mailer Modul ist eine administrative Erweiterung für den Alumnihub, die es dem Vorstand ermöglicht, personalisierte Rundschreiben an alle registrierten Mitglieder zu versenden. Da die Pflege der Mitgliederdaten direkt im CMS erfolgt, bietet dieses Modul eine nahtlose Integration ohne den Export von CSV-Listen in externe Newsletter-Tools. Das Mass-Mailer-Modul ist eine administrative Erweiterung für den AlumniHub, die es dem Vorstand ermöglicht, personalisierte Rundschreiben an alle registrierten Mitglieder zu versenden. Da die Pflege der Mitgliederdaten direkt im CMS erfolgt, bietet dieses Modul eine nahtlose Integration ohne den Export von CSV-Listen in externe Newsletter-Tools.
##### Integration von Brevo ##### Integration von Brevo
@@ -629,20 +629,20 @@ Das Mass Mailer Modul ist eine administrative Erweiterung für den Alumnihub, di
Für den tatsächlichen Versand der E-Mails nutzen wir den Cloud-Dienst Brevo. Dieser bietet eine zuverlässige Zustellung (hohe Reputation der Mailserver), sowie die Möglichkeit die Zustell-, Öffnungs- und Klickraten zu beobachten. Das Limit von 300 E-Mails pro Tag stellt uns jedoch in der kostenlosen Variante vor eine Herausforderung. Für den tatsächlichen Versand der E-Mails nutzen wir den Cloud-Dienst Brevo. Dieser bietet eine zuverlässige Zustellung (hohe Reputation der Mailserver), sowie die Möglichkeit die Zustell-, Öffnungs- und Klickraten zu beobachten. Das Limit von 300 E-Mails pro Tag stellt uns jedoch in der kostenlosen Variante vor eine Herausforderung.
`Batch-Processing`: Mails werden nicht sofort ("Fire and Forget") versendet, sondern in eine Versandwarteschlange geschrieben. Nachdem schon die Notifications Infrastruktur, welche sich auch um den Mail versand kümmert, ins Framework eingebaut worden ist, wird diese gleich zum `schedulen` unserer E-Mails genutzt. Immer 100 Mails alle 24 Stunden bis alle Ziele die Mails erhalten haben. Das Limit von 100 / Tag ist konservativ sehr niedrig angesetzt, damit Funktionen wie Passwort Reset Mails nicht (leicht) dadurch beeinflusst werden können. `Batch-Processing`: Mails werden nicht sofort ("Fire and Forget") versendet, sondern in eine Versandwarteschlange geschrieben. Nachdem schon die Notifications-Infrastruktur, welche sich auch um den Mailversand kümmert, ins Framework eingebaut worden ist, wird diese gleich zum `schedulen` unserer E-Mails genutzt. Immer 100 Mails alle 24 Stunden, bis alle Ziele die Mails erhalten haben. Das Limit von 100 / Tag ist konservativ sehr niedrig angesetzt, damit Funktionen wie Passwort-Reset-Mails nicht (leicht) dadurch beeinflusst werden können.
#### Token Lifetime #### Token Lifetime
\ \
Das Token Lifetime Modul wurde geschrieben, um die Token-Lebenszeit konfigurierbar zu machen. Notwendig war das, um die Passwort Reset Links im initialen Mail versand länger gültig sein zu lassen. Durch das `Batch Processing` war es möglich, dass eine Mail erst Tage nach erstellen des Links hinaus geschickt wird und bei einer Standard Ablaufdauer von 2 Tagen sind manche Links schon ungültig, bis sie den Mail Server erreichen. Ziel war es, die Änderung der Lebenszeit für Administratoren im User Interface im Admin Bereich möglich zu machen. Das Token-Lifetime-Modul wurde geschrieben, um die Token-Lebenszeit konfigurierbar zu machen. Notwendig war das, um die Passwort-Reset-Links im initialen Mailversand länger gültig sein zu lassen. Durch das `Batch-Processing` war es möglich, dass eine Mail erst Tage nach Erstellen des Links hinausgeschickt wird, und bei einer Standard-Ablaufdauer von zwei Tagen sind manche Links schon ungültig, bis sie den Mailserver erreichen. Ziel war es, die Änderung der Lebenszeit für Administratoren im User-Interface im Admin-Bereich möglich zu machen.
Technisch bedeutet das, dass die standardmäßig vorkonfigurierten `DataProtectionTokenProviderOptions` explizit konfiguriert werden müssen. [@andrewlock_token_lifetime] Der ASP.NET Core `UserManager`, welcher das generieren der Tokens übernimmt, verwendet einen `DataProtectorTokenProvider` und dieser wiederum kann mithilfe der `DataProtectionTokenProviderOptions` konfiguriert werden. Technisch bedeutet das, dass die standardmäßig vorkonfigurierten `DataProtectionTokenProviderOptions` explizit konfiguriert werden müssen. [@andrewlock_token_lifetime] Der ASP.NET Core `UserManager`, welcher das generieren der Tokens übernimmt, verwendet einen `DataProtectorTokenProvider` und dieser wiederum kann mithilfe der `DataProtectionTokenProviderOptions` konfiguriert werden.
Es gibt 2 Möglichkeiten, wie man dieses Problem Lösen kann: Es gibt zwei Möglichkeiten, wie man dieses Problem lösen kann:
- `der Workaround`: Ein eigenes Modul, welches in seiner `Server/Startup.cs` die benötigten Werte korrekt setzt. Das hat den Vorteil, dass wir keine Änderungen im CMS selbst haben, sondern nur unsere eigene Erweiterung dafür schreiben. Andererseits könnte durch die undeterministische Ladereihenfolge eine `race-condition` auftreten. [@race_conditions_pdf] Darüber hinaus besteht kein gleichzeitiger Zugriff auf die appsettings.json und den IServiceProvider, in dem die Konfiguration gesetzt werden muss. => Nachdem wir in der Konfigurationsphase auch noch keinen Datenbankzugriff haben, können die Werte nicht aus der Datenbank geladen werden, sondern wir müssen auf eine Textdatei zurückgreifen. - `der Workaround`: Ein eigenes Modul, welches in seiner `Server/Startup.cs` die benötigten Werte korrekt setzt. Das hat den Vorteil, dass wir keine Änderungen im CMS selbst haben, sondern nur unsere eigene Erweiterung dafür schreiben. Andererseits könnte durch die undeterministische Ladereihenfolge eine `race-condition` auftreten. [@race_conditions_pdf] Darüber hinaus besteht kein gleichzeitiger Zugriff auf die appsettings.json und den IServiceProvider, in dem die Konfiguration gesetzt werden muss. => Nachdem wir in der Konfigurationsphase auch noch keinen Datenbankzugriff haben, können die Werte nicht aus der Datenbank geladen werden, sondern wir müssen auf eine Textdatei zurückgreifen.
- `die saubere Lösung`: Eine Änderung im Kern von Oqtane. Also wird in unserem Fork von Oqtane die Konfigurationslogik für die Tokenlifetime implementiert. In diesem Fall könnte die Konfigurationslogik direkt in `OqtaneServiceCollectionExtensions.cs` hinzugefügt werden, da hier auch alle anderen Oqtane Spezifischen Konfigurationen gesetzt werden. Das hat den Vorteil, dass der Code aufgeräumter und sauberer ist und die `race-condition` verhindert werden kann. [@race_conditions_pdf] Der initiale Grund dagegen ist, dass wir ein weiteres Git Repository zum warten haben (den Fork vom Oqtane.Framework), welches jetzt nicht mehr mit Upstream Commit-gleich ist. - `die saubere Lösung`: Eine Änderung im Kern von Oqtane. Also wird in unserem Fork von Oqtane die Konfigurationslogik für die Token-Lifetime implementiert. In diesem Fall könnte die Konfigurationslogik direkt in `OqtaneServiceCollectionExtensions.cs` hinzugefügt werden, da hier auch alle anderen Oqtane-spezifischen Konfigurationen gesetzt werden. Das hat den Vorteil, dass der Code aufgeräumter und sauberer ist und die `race-condition` verhindert werden kann. [@race_conditions_pdf] Der initiale Grund dagegen ist, dass wir ein weiteres Git-Repository zu warten haben (den Fork vom Oqtane.Framework), welches jetzt nicht mehr mit Upstream commitgleich ist.
`Der Workaround` ist die Möglichkeit für die wir uns entschieden haben, allerdings ist das nicht die schönste Lösung. Eine eventuell nachfolgende Diplomarbeit kann an dieser Stelle ansetzen und `die saubere Lösung` implementieren. `Der Workaround` ist die Möglichkeit für die wir uns entschieden haben, allerdings ist das nicht die schönste Lösung. Eine eventuell nachfolgende Diplomarbeit kann an dieser Stelle ansetzen und `die saubere Lösung` implementieren.
@@ -650,7 +650,7 @@ Es gibt 2 Möglichkeiten, wie man dieses Problem Lösen kann:
\ \
Eine weitere Anforderung der Diplomarbeit war es Einträge in Modulen wie der `Hall of Fame`, dem `Schwarzen Brett` und dem Premium Bereich (`Engineer Applications`) melden zu können. Am Anfang war es wichtig, dass jeder schnell vorankommt, allerdings haben wir die Kommunikation Teamintern ein wenig verschlafen und dadurch ein paar Funktionen doppelt geschrieben. Dadurch kam es zu Inkonsistenzen in der Verwendung der unterschiedlichen Reporting Systeme. Deswegen haben wir uns am Ende für eine globales Reporting System entschieden. Eine weitere Anforderung der Diplomarbeit war es Einträge in Modulen wie der `Hall of Fame`, dem `Schwarzen Brett` und dem Premium Bereich (`Engineer Applications`) melden zu können. Am Anfang war es wichtig, dass jeder schnell vorankommt, allerdings haben wir die Kommunikation Teamintern ein wenig verschlafen und dadurch ein paar Funktionen doppelt geschrieben. Dadurch kam es zu Inkonsistenzen in der Verwendung der unterschiedlichen Reporting Systeme. Deswegen haben wir uns am Ende für ein globales Reporting-System entschieden.
Angestrebt wurde folgender Ablauf für das Melden eines Eintrags: Angestrebt wurde folgender Ablauf für das Melden eines Eintrags:
@@ -672,11 +672,11 @@ sequenceDiagram
ReportingComponent->>-User: Done ReportingComponent->>-User: Done
``` ```
Im oben dargestellten Ablaufdiagram werden das ReportingComponent und der ReportingHandler vom Reporting System über Dependency Injection bereitgestellt, nicht vom Modul selbst. Dadurch erreichen wir eine bessere Trennung der Zuständigkeiten und halten das S in SOLID ein: `Single responsibility`. Die Module sind jetzt nicht mehr für das Reporting selber verantwortlich, sondern müssen nur das Reportings System einbinden. Im oben dargestellten Ablaufdiagramm werden das Reporting-Component und der Reporting-Handler vom Reporting-System über Dependency Injection bereitgestellt, nicht vom Modul selbst. Dadurch erreichen wir eine bessere Trennung der Zuständigkeiten und halten das S in SOLID ein: `Single responsibility`. Die Module sind jetzt nicht mehr für das Reporting selbst verantwortlich, sondern müssen nur das Reporting-System einbinden.
Darüber hinaus erfüllt dieses Design das **Open/Closed Principle**: Das Reporting-System ist offen für Erweiterungen (neue Module können einfach andocken), aber geschlossen für Modifikationen (der Kern des Reporting-Systems muss nicht für jedes neue Modul geändert werden). Darüber hinaus erfüllt dieses Design das **Open/Closed Principle**: Das Reporting-System ist offen für Erweiterungen (neue Module können einfach andocken), aber geschlossen für Modifikationen (der Kern des Reporting-Systems muss nicht für jedes neue Modul geändert werden).
Damit DI funktioniert muss für den DI Consumer (`also das Modul, welches das Reporting System einbinden möchte`) das Interface zur Kompilierzeit zur Verfügung stehen. Um das zu erreichen habe ich eine neue Klassenbibliothek erstellt: Sie heißt `Interfaces`, wird per Gitea Actions automatisch in ein NuGet-Paket gebaut und in der `Gitea Actions NuGet Registry` veröffentlicht. Die Versionierung folgt dabei strikt den Git-Tags im Repository, was eine saubere Release-History ermöglicht. Dieses NuGet-Paket wird dann in jedem notwendigen Modul als Dependency hinzugefügt und damit kann man Modulübergreifend auf die Services und das `IReportingComponent` zugreifen. Damit DI funktioniert, muss für den DI-Consumer (also das Modul, welches das Reporting-System einbinden möchte) das Interface zur Kompilierzeit zur Verfügung stehen. Um das zu erreichen, habe ich eine neue Klassenbibliothek erstellt: Sie heißt `Interfaces`, wird per Gitea Actions automatisch in ein NuGet-Paket gebaut und in der `Gitea Actions NuGet Registry` veröffentlicht. Die Versionierung folgt dabei strikt den Git-Tags im Repository, was eine saubere Release-History ermöglicht. Dieses NuGet-Paket wird dann in jedem notwendigen Modul als Dependency hinzugefügt, und damit kann man modulübergreifend auf die Services und das `IReportingComponent` zugreifen.
Die Implementierung des `IReportingComponents` stellt nur eine Property (`ReportType`, welche den TypeName der Razor Komponente zurückliefert, damit `DynamicComponent` sie laden kann) und eine Methode (`ConstructParameterList`, welche das Parameter Dictionary erstellt. Nur zwecks Typensicherheit eingefügt) bereit. Mit dem `DynamicComponent` von Razor ist es möglich, per C# Code unterschiedliche Komponenten zu rendern und damit auch die per DI injizierte Klasse. Die Implementierung des `IReportingComponents` stellt nur eine Property (`ReportType`, welche den TypeName der Razor Komponente zurückliefert, damit `DynamicComponent` sie laden kann) und eine Methode (`ConstructParameterList`, welche das Parameter Dictionary erstellt. Nur zwecks Typensicherheit eingefügt) bereit. Mit dem `DynamicComponent` von Razor ist es möglich, per C# Code unterschiedliche Komponenten zu rendern und damit auch die per DI injizierte Klasse.
@@ -788,13 +788,12 @@ Detailansicht: Die Details-Komponente bietet eine fokussierte Ansicht des Eintra
Um die Mitglieder regelmäßig über neue Inhalte zu informieren, wurde ein automatisierter `Cronjob` implementiert. Dieser Job läuft im Hintergrund des Oqtane-Frameworks und führt folgende Schritte aus: Um die Mitglieder regelmäßig über neue Inhalte zu informieren, wurde ein automatisierter `Cronjob` implementiert. Dieser Job läuft im Hintergrund des Oqtane-Frameworks und führt folgende Schritte aus:
- Filterung: Der Job identifiziert alle Einträge, die seit dem letzten Versand erstellt wurden. - **Filterung**: Der Job identifiziert alle Einträge, die seit dem letzten Versand erstellt wurden.
- Zielgruppenselektion: Es werden alle Benutzer identifiziert, die der Rolle "Absolventen" angehören. - **Zielgruppenselektion**: Es werden alle Benutzer identifiziert, die der Rolle "Absolventen" angehören.
- Zusammenstellung: Für jeden dieser Benutzer wird eine personalisierte Email-Notification generiert, welche eine Zusammenfassung der neuen Einträge enthält. - **Zusammenstellung**: Für jeden dieser Benutzer wird eine personifizierte E-Mail-Notification generiert, welche eine Zusammenfassung der neuen Einträge enthält.
- Versand: Die generierten Notifications werden in die Warteschlange der Notification-Infrastruktur eingereiht und sukzessive versendet. - **Versand**: Die generierten Notifications werden in die Warteschlange der Notification-Infrastruktur eingereiht und sukzessive versendet.
Integration des Reporting-Systems
#### Reporting System #### Integration des Reporting-Systems
\ \
@@ -812,11 +811,11 @@ Auf der Serverseite folgt das Modul dem etablierten Muster mit einem `BlackBoard
### Produktion != Staging ### Produktion != Staging
Ein Learning, welches doch relativ schnell aufkam ist im Bereich der IT eigentlich kein unbekanntes. Wir hatten dieses Learning relativ bald, im Frühling 2025, als die ersten Probleme mit dem Deployment von Oqtane aufkamen. Oqtane war zwar in unserer Entwicklungsumgebung sehr einfach zum einrichten gewesen, das Deployment in der Cloud vom Hetzner war jedoch geplagt von Problemen. Im Zeitraum von Mai bis Okober hatten wir keine laufende Produktivumgebung. Dadurch sind wir mit dieser Diplomarbeit auch in [Zeitverzug](#arbeitszeiteinschätzung-zeitverzug) gekommen. Hätte ich mich vor dem Start der Diplomarbeit mit dem Deployment von Oqtane auseinander gesetzt, dann wäre das in [Fehlende Dokumentation](#fehlende-dokumentation) beschriebene Problem früher aufgekommen und der Zeitverzug wäre nicht so groß, oder noch ganz vermeidbar gewesen. Ein Learning, welches doch relativ schnell aufkam, ist im Bereich der IT eigentlich kein unbekanntes. Wir hatten dieses Learning relativ bald, im Frühling 2025, als die ersten Probleme mit dem Deployment von Oqtane aufkamen. Oqtane war zwar in unserer Entwicklungsumgebung sehr einfach einzurichten gewesen, das Deployment in der Cloud von Hetzner war jedoch geplagt von Problemen. Im Zeitraum von Mai bis Oktober hatten wir keine laufende Produktivumgebung. Dadurch sind wir mit dieser Diplomarbeit auch in [Zeitverzug](#arbeitszeiteinschätzung-zeitverzug) gekommen. Hätte ich mich vor dem Start der Diplomarbeit mit dem Deployment von Oqtane auseinandergesetzt, dann wäre das in [Fehlende Dokumentation](#fehlende-dokumentation) beschriebene Problem früher aufgekommen und der Zeitverzug wäre nicht so groß oder noch ganz vermeidbar gewesen.
### Teamleitung (Motivation / Downsizing) ### Teamleitung (Motivation / Downsizing)
Nachdem ich mich von Anfang an volkommen in das Deploymentproblem von Oqtane gestürzt habe, habe ich meine Rolle als Teamleitung etwas schleifen lassen. Dadurch fehlte bei einigen Teammitgliedern initial die Identifikation mit dem Projekt und in weitererfolge auch die Motivation an diesem Projekt mitzuarbeiten. Nachdem im Verlauf des Frühlings und über den Sommer von der hälfte des Teams trotz Besprechungen und Mahnungen keine Beiträge zu dem Projekt kamen, haben Hr. Prof. Gürth und ich uns dazu entschieden uns von 2 Personen vor unterschreiben des Projektantrages zu trennen. Grund dazu war die Angst, die mangelnde Motivation zieht das restliche Team mit hinunter. Wir wollten uns trotz des Downsizings nicht an Funktionalitäten sparen und haben uns für das nächste halbe bis dreiviertel Jahr einen ziemlich strikten Zeitplan vorgenommen. Nachdem ich mich von Anfang an vollkommen in das Deploymentproblem von Oqtane gestürzt habe, habe ich meine Rolle als Teamleitung etwas schleifen lassen. Dadurch fehlte bei einigen Teammitgliedern initial die Identifikation mit dem Projekt und in weiterer Folge auch die Motivation, an diesem Projekt mitzuarbeiten. Nachdem im Verlauf des Frühlings und über den Sommer von der Hälfte des Teams trotz Besprechungen und Mahnungen keine Beiträge zu dem Projekt kamen, haben Hr. Prof. Gürth und ich uns dazu entschieden, uns von zwei Personen vor dem Unterschreiben des Projektantrages zu trennen. Grund dazu war die Angst, die mangelnde Motivation zieht das restliche Team mit hinunter. Wir wollten uns trotz des Downsizings nicht an Funktionalitäten sparen und haben uns für das nächste halbe bis dreiviertel Jahr einen ziemlich strikten Zeitplan vorgenommen.
### Arbeitszeiteinschätzung (Zeitverzug) ### Arbeitszeiteinschätzung (Zeitverzug)
@@ -828,7 +827,7 @@ Es gibt mehrere Gründe dafür:
\ \
- `Fehlende oder nur schlechte Dokumentation von Oqtane`: Einige Probleme im Deployment wurden in langer und mühseliger Arbeit auseinander gebrochen und in weitere immer kleinere Probleme unterteilt. Dadurch, dass wir alle keine Erfahrung mit der Entwicklung und dem Deployment von ASP.NET Code Anwendungen hatten und die Dokumentation doch schlecht war, blieb uns manchmal nichts anderes übrig als mit WireShark den Netzwerktraffic mit zu schneiden und nebenbei im Git Repository die geloggten Zeilen Code zu finden und so das Framework von innen heraus kennen zu lernen. Dadurch hatte ich dann nach einer Einarbeitungszeit von 4 Monaten ziemlich jede Stelle im Sourcecode von Oqtane gesehen und finde mich um das schneller zurecht. - `Fehlende oder nur schlechte Dokumentation von Oqtane`: Einige Probleme im Deployment wurden in langer und mühseliger Arbeit auseinandergebrochen und in weitere immer kleinere Probleme unterteilt. Dadurch, dass wir alle keine Erfahrung mit der Entwicklung und dem Deployment von ASP.NET-Core-Anwendungen hatten und die Dokumentation doch schlecht war, blieb uns manchmal nichts anderes übrig, als mit WireShark den Netzwerktraffic mitzuschneiden und nebenbei im Git-Repository die geloggten Codezeilen zu finden und so das Framework von innen heraus kennenzulernen. Dadurch hatte ich dann nach einer Einarbeitungszeit von vier Monaten ziemlich jede Stelle im Sourcecode von Oqtane gesehen und finde mich umso schneller zurecht.
- `Team-Konsolidierung`: Durch das notwendige Downsizing des Teams mussten Aufgaben neu verteilt werden, was die individuelle Arbeitslast erhöhte und die Konzentration auf die Kernentwicklung zeitweise verzögert hat. - `Team-Konsolidierung`: Durch das notwendige Downsizing des Teams mussten Aufgaben neu verteilt werden, was die individuelle Arbeitslast erhöhte und die Konzentration auf die Kernentwicklung zeitweise verzögert hat.
- `Abhängigkeit von der Infrastruktur`: Dadurch, dass wir bis in den Oktober / November hinein nicht wussten, ob wir weiter bei Oqtane bleiben können, haben die anderen Teammitglieder nicht mit der sinnvollen Entwicklung ihrer Module starten können. Zitat: "Und was wenn wir am Ende doch noch das CMS umstellen müssen?" => Auch wenn der Auftrag war, mit der Modulentwicklung zu starten war die Motivation meiner Teammitglieder nicht so hoch. Selbst wenn sie nicht direkt von der Infrastruktur mit der Ausführung ihrer Aufgaben abhängig waren, motiviert waren sie wegen der Umstände auch nicht. - `Abhängigkeit von der Infrastruktur`: Dadurch, dass wir bis in den Oktober / November hinein nicht wussten, ob wir weiter bei Oqtane bleiben können, haben die anderen Teammitglieder nicht mit der sinnvollen Entwicklung ihrer Module starten können. Zitat: "Und was wenn wir am Ende doch noch das CMS umstellen müssen?" => Auch wenn der Auftrag war, mit der Modulentwicklung zu starten war die Motivation meiner Teammitglieder nicht so hoch. Selbst wenn sie nicht direkt von der Infrastruktur mit der Ausführung ihrer Aufgaben abhängig waren, motiviert waren sie wegen der Umstände auch nicht.
@@ -853,13 +852,13 @@ Ein zentrales Problem in unserer ursprünglichen Arbeitsweise war die Kopplung v
Lösungsansatz: Meetings und Besprechungen asynchron zueinander setzen. Lösungsansatz: Meetings und Besprechungen asynchron zueinander setzen.
- Asynchrone Daily-Updates: Statusberichte erfolgen schriftlich (z. B. in Gitea Issues oder YouTrack), nicht mehr in stundenlangen Call-Marathons. Das nimmt den zeitlichen Druck vom einzelnen Entwickler. Oder zumindest in kurzen Commitnachrichten, welche am Ende des Tages automatisch an alle Teammitglieder zum Überblick gesendet werden (eventuell mit \@username tagging, um eine Person nochmal genau anzusprechen) - Asynchrone Daily-Updates: Statusberichte erfolgen schriftlich (z. B. in Gitea Issues oder YouTrack), nicht mehr in stundenlangen Call-Marathons. Das nimmt den zeitlichen Druck vom einzelnen Entwickler. Oder zumindest in kurzen Commitnachrichten, welche am Ende des Tages automatisch an alle Teammitglieder zum Überblick gesendet werden (eventuell mit \@username-Tagging, um eine Person nochmal genau anzusprechen).
- Review-First-Policy: Ein Feature gilt erst dann als „fertig“, wenn es einen asynchronen Code-Review-Prozess durchlaufen hat. Das Meeting dient nur noch der Klärung von Blockern, nicht der Präsentation von Code. Das war eigentlich schon von Anfang an in unserer `Definition of Done` festgelegt worden. - Review-First-Policy: Ein Feature gilt erst dann als „fertig“, wenn es einen asynchronen Code-Review-Prozess durchlaufen hat. Das Meeting dient nur noch der Klärung von Blockern, nicht der Präsentation von Code. Das war eigentlich schon von Anfang an in unserer `Definition of Done` festgelegt worden.
- Entkoppelung von Meeting und Deadline: Meetings sollten der Synchronisation dienen, während die Abgabe von Arbeitspaketen kontinuierlich (Continuous Integration) erfolgen muss, um Lastspitzen (in der [Gitea Actions](#sec:continuous-integration) Pipeline) am Tag der Besprechung zu vermeiden. - Entkoppelung von Meeting und Deadline: Meetings sollten der Synchronisation dienen, während die Abgabe von Arbeitspaketen kontinuierlich (Continuous Integration) erfolgen muss, um Lastspitzen (in der [Gitea Actions](#sec:continuous-integration) Pipeline) am Tag der Besprechung zu vermeiden.
# Fazit ## Fazit
Die Entwicklung des Alumnihubs auf Basis von Blazor und Oqtane war eine Reise geprägt von technischer Tiefe, organisatorischen Herausforderungen und wertvollen Erkenntnissen über moderne Software-Architektur. Rückblickend lässt sich festhalten, dass die Wahl des .NET-Stacks sowohl die erwarteten Stärken als auch unerwartete Hürden mit sich brachte. Die Entwicklung des AlumniHubs auf Basis von Blazor und Oqtane war eine Reise geprägt von technischer Tiefe, organisatorischen Herausforderungen und wertvollen Erkenntnissen über moderne Software-Architektur. Rückblickend lässt sich festhalten, dass die Wahl des .NET-Stacks sowohl die erwarteten Stärken als auch unerwartete Hürden mit sich brachte.
Technisch gesehen haben sich die statische Typisierung von C# und das integrierte Dependency Injection Framework als äußerst wertvolle Werkzeuge erwiesen. Sie haben die Entwicklung komplexer, modularer Strukturen wie des generischen *Reporting Systems* erheblich vereinfacht und für eine hohe Konsistenz gesorgt. Dennoch zeigt der Vergleich zu anderen Stacks wie React und Go, dass Architektur-Vorteile nicht exklusiv an ein einzelnes Ökosystem gebunden sein müssen. Die Erfahrung mit Protobufs in anderen Kontexten verdeutlicht, dass eine sprachenagnostische Typisierung oft flexibler ist, ohne die Bindung an ein spezifisches Framework-Ökosystem zu erzwingen. .NET bleibt jedoch eine absolut valide und stabile Wahl für Projekte dieser Größenordnung. Technisch gesehen haben sich die statische Typisierung von C# und das integrierte Dependency Injection Framework als äußerst wertvolle Werkzeuge erwiesen. Sie haben die Entwicklung komplexer, modularer Strukturen wie des generischen *Reporting Systems* erheblich vereinfacht und für eine hohe Konsistenz gesorgt. Dennoch zeigt der Vergleich zu anderen Stacks wie React und Go, dass Architektur-Vorteile nicht exklusiv an ein einzelnes Ökosystem gebunden sein müssen. Die Erfahrung mit Protobufs in anderen Kontexten verdeutlicht, dass eine sprachenagnostische Typisierung oft flexibler ist, ohne die Bindung an ein spezifisches Framework-Ökosystem zu erzwingen. .NET bleibt jedoch eine absolut valide und stabile Wahl für Projekte dieser Größenordnung.
@@ -869,5 +868,5 @@ In organisatorischer Hinsicht war die Verkleinerung des Teams ein Wendepunkt. Tr
Persönlich bleibt vor allem die fünfmonatige Auseinandersetzung mit der Deployment-Problematik im Gedächtnis. Dass die Lösung für eine komplexe Infrastruktur-Hürde schließlich in einem unerwarteten Moment außerhalb der Arbeitsumgebung fast schon ironisch auftauchte, unterstreicht eine wichtige Lektion für jeden Entwickler: Beharrlichkeit ist notwendig, aber oft braucht es auch den nötigen Abstand, um den entscheidenden Blickwinkel zu finden. Persönlich bleibt vor allem die fünfmonatige Auseinandersetzung mit der Deployment-Problematik im Gedächtnis. Dass die Lösung für eine komplexe Infrastruktur-Hürde schließlich in einem unerwarteten Moment außerhalb der Arbeitsumgebung fast schon ironisch auftauchte, unterstreicht eine wichtige Lektion für jeden Entwickler: Beharrlichkeit ist notwendig, aber oft braucht es auch den nötigen Abstand, um den entscheidenden Blickwinkel zu finden.
Abschließend lässt sich sagen, dass der Alumnihub nicht nur eine funktionsfähige Plattform für den Absolventenverein darstellt, sondern für mich persönlich als Beweis für die Bedeutung von technischer Neugier, architektonischer Weitsicht und der Fähigkeit zur kritischen Selbstreflexion dient. Abschließend lässt sich sagen, dass der AlumniHub nicht nur eine funktionsfähige Plattform für den Absolventenverein darstellt, sondern für mich persönlich als Beweis für die Bedeutung von technischer Neugier, architektonischer Weitsicht und der Fähigkeit zur kritischen Selbstreflexion dient.
(Mehr dazu unter [Learnings](#sec:kh-learnings)) (Mehr dazu unter [Learnings](#sec:kh-learnings))