1. Einführung und Ziele
1.1. Aufgabenstellung
Das Ziel dieses Systems ist es, eine Materialsammlung für die "Modulare Online Plattform für Studierende" (kurz: "MOPS"), bereitzustellen. Dabei verwalten wir sämtliche Dateien, die Studierende, Korrektoren und Organisatoren benötigen, in einem File-System. Das System basiert dabei auf einer Gruppenstruktur. Solche Gruppen können sowohl Veranstaltungen als auch Lerngruppen repräsentieren.
Mögliche Anwendungsszenarien:
Name | Szenario |
---|---|
Ein Mitglied möchte eine Datei einsehen. |
Das Mitglied navigiert zur Gruppendateiübersicht. Dort navigiert es in den gewünschten Ordner, in dem die entsprechende Datei liegt. |
Ein Administrator lädt einzelne Datei hoch. |
Der Administrator einer Gruppe navigiert zur Gruppendateiübersicht. Dort kann er mit einem Upload-Button den Dateiupload beginnen. |
Ein Mitglied möchte Verzeichnis erstellen. |
Ein Mitglied einer Gruppe navigiert zur Gruppendateiübersicht. Dort kann es mit dem "Neuer Ordner"-Button einen neues Verzeichnis anlegen. |
Ein Organisator möchte Verzeichnis mit Dateien löschen. |
Der Organisator navigiert zur Gruppendateiübersicht. Dort klickt er das gesuchte Verzeichnis an und wählt "Verzeichnis löschen". Er bestätigt die Löschaktion und das Verzeichnis wird gelöscht |
1.2. Qualitätsziele
Unsere Entwicklung haben wir an folgender Prioritätensetzung orientiert, um eine bestmögliche Lernumgebung für alle Nutzergruppen zu gewährleisten.
Qualitätsziel | Erläuterung |
---|---|
Performance |
Jeder Request muss in kürzester Zeit bearbeitet werden, um dem Benutzer ein möglichst flüssige Nutzungserlebnis zu bieten. |
Zuverlässigkeit |
Das System muss zu jedem Zeitpunkt erreichbar und voll funktionsfähig sein, um Nutzer nicht in Problemlagen durch fehlenden Zugriff zu bringen. Außerdem soll der Wartungsaufwand durch modulare Aufbauweise minimiert werden. |
Sicherheit |
In jedem Fall werden die Daten der Nutzer vertraulich behandelt und Fremdzugriff verhindert. |
1.3. Involvierte Gruppen
Um die Erwartungen und Anforderungen der Nutzergruppen unserer Plattform zu erfüllen, haben wir im Folgenden eine Aufstellung der Erwartungshaltungen zusammengefasst, um diese in unserer Entwicklung zu berücksichtigen.
Nutzergruppe | Erwartungshaltung |
---|---|
Studierende |
Studierende brauchen eine Plattform, die einfachen Zugriff auf die Inhalte ihrer Lernveranstaltungen ermöglicht. Sie möchten sich mit ihren Mitstudierenden organisieren können und erwarten eine zuverlässige und nach heutigem Stand der Technik performante Plattform. Ausfälle durch Wartungsarbeiten und ungeplante Systemausfälle müssen minimiert werden, um eine vertrauenswürdige Umgebung zu schaffen. |
Organisatoren |
Organisatoren legen Wert auf eine intuitive Plattform, der es trotzdem nicht an Funktionalität mangelt. Hierbei spielt ein übersichtliches Layout eine entscheidende Rolle. Die Verlässlichkeit und Zuverlässigkeit des Systems hat weiterhin Priorität, um verlängerte Arbeitszeiten zu vermeiden. Organisatoren möchten viele Veranstaltungen einfach verwalten können, Materialien einsehen und organisieren, sowie Aktivitäten von Kursteilnehmern einsehen. |
Systemadministratoren |
Systemadministratoren legen Wert auf eine hohe Wartbarkeit und Zuverlässigkeit des Systems. Im Idealfall muss das System zur Wartung/Datensicherung nicht ganz abgeschaltet werden, es reichen vorübergehende Modulabschaltungen oder gar keine Funktionsreduktion. Eindeutige Modularisierung, ausführliche aber sinnvolle Laufzeitanalyse, sowie verständliche Dokumentation sind hierbei besonders hilfreich für die Überprüfung, Systemwartung und Erweiterbarkeit. |
2. Randbedingungen
Um unerwartete Verzögerung im Entwicklungsablauf zu vermeiden, haben wir in der Planungsphase und während der Implementierung der Kernkomponenten berücksichtigt, welche Randbedingungen und technische sowie fachliche Einschränkungen den Ablauf der Entwicklung beeinflussen könnten.
Bedingung | Erläuterung |
---|---|
Zusammenarbeit mit anderen Gruppen |
Schnittstellen mit anderen Gruppen beeinflussen unsere Implementierung. Vor allem, wenn diese Gruppen selbst noch keine abgeschlossenen Konzepte entworfen haben. |
Fehlende Ordnerstruktur in MinIO |
Da MinIO nur eine virtuelle Ordnerstruktur unterstützt, ist die Anlegung einer eigenen Ordnerstruktur in einer externen Datenbank notwendig. |
Keycloak erschwert UnitTests |
Aufgrund der Sicherheitskomponenten werden die Controller-Tests stark verkompliziert, da die Sicherheitsfeatures gemockt werden müssen. |
All diese möglichen Probleme haben wir jedoch schlussendlich überwunden und konnten das Produkt ohne größere Schwierigkeiten fertig stellen.
3. Kontextabgrenzung
Kommunikationsbeziehung | Eingabe | Ausgabe |
---|---|---|
Weiterleitung von der Gruppenbildung |
Das Gruppenbildungsmodul stellt eine Anfrage mit der GruppenID um einen Weiterleitungslink zu erhalten. |
Wir liefern einen auf die GruppenID bezogenen Link zurück. |
Überprüfen des Adminstatus eines Nutzer in einer Gruppe |
Wir erfragen mit einer GruppenID und dem Benutzernamen die Berechtigungen eines Nutzers in einer bestimmten Gruppe. |
Wir erhalten als Bestätigung einen Boolean zurück. |
Abfrage aller Gruppenupdates |
Wir erfragen die Veränderungen in den Gruppendaten seit der letzten Anfrage. |
Wir erhalten eine Liste aller veränderten Gruppen zurück. |
Erfragen der Mitglieder einer Gruppe |
Wir erfragen mittels einer GruppenID die Mitgliederdaten dieser spezifischen Gruppe. |
Wir erhalten eine Liste von Nutzerdaten zurück. |
- Weiterleitung von der Gruppenbildung (Anfrage der Gruppenurl)
-
Anfrage:
Parameter | Description |
---|---|
|
The group id. |
Antwort (.json Objekt):
Path | Type | Description |
---|---|---|
|
|
The id of the group. |
|
|
The id of the group’s root directory. |
|
|
The url of the group’s root directory. |
Beispielrequest:
$ http GET 'http://localhost:8080/material1/group/1/url'
Beispielantwort:
{"group_id":1,"root_dir_url":"/material1/dir/2","root_dir_id":2}
- Gruppenbildungsservice
-
Der Service, der unsere Gruppenverwaltung abwickelt, muss mit der Gruppenbildung interagieren. Dazu haben wir folgende Anfragen an die REST-API der Gruppenbildung implementiert:
-
doesGroupExist
: fragt ab, ob eine gegebene Gruppe existiert -
isUserInGroup
: fragt ab, ob ein gegebener User in einer gegebenen Gruppe Mitglied ist -
isUserAdminInGroup
: fragt ab, ob ein gegebener User in einer gegebenen Gruppe Administrator ist -
returnAllGroups
: gibt alle Gruppen zurück, die seit einem bestimmten Zeitpunkt, der durch den ParametereventId
bestimmt wird, verändert worden sind -
returnUsersOfGroup
: gibt alle Mitglieder einer Gruppe zurück -
returnGroupsOfUsers
: gibt alle Gruppen eines Users zurück
-
Dabei werden von uns nicht alle dieser Anfragen genutzt. Dennoch sind sie implementiert um in Zukunft die Erweiterbarkeit zu vereinfachen. Diese verschiedenen Anfragen sind notwendig für die verschiedenen Funktionalitäten, die unsere Anwendung bereitstellt:
-
Anzeige aller Gruppen
-
Garbage Collection (Überprüfen ob eine Gruppe noch existiert)
-
Überprüfung von Berechtigungen eines Users
-
Erstellung einer Platzhalterberechtigung für alle Rollen
Da uns während der Entwicklungszeit der externe Service nicht zur Verfügung stand, haben wir einen Stub entwickelt,
welcher festgesetzte Rollen zurückgibt. Wir haben uns dazu an dem REST-API Wiki der Gruppenbildung
orientiert.
Prometheus
Im PrometheusComponent
werden unsere Prometheus Statistiken registriert.
Zur Verfügung gestellt werden:
Global:
-
Speicherplatzverbrauch
-
Dateianzahl
-
Ordneranzahl
-
Gruppenanzahl
Für jede Gruppe einzeln (mit der Gruppen-UUID der Gruppenbildung getaggt):
-
Speicherplatzverbrauch
-
Dateianzahl
-
Ordneranzahl
4. Lösungsstrategie
4.1. Docker-Compose
Beim Design der docker-compose Konfigurationen hatten wir komplizierte Anforderungen.
Ziel:
In Production soll ein docker-compose […] up
die Anwendung und alle Dependencies (also nur die Datenbank) starten.
In Development soll ein anderes docker-compose […] up
nur die Dependencies (die Datenbank und MinIO) starten, damit
die Anwendung separat aus der IDE gestartet werden kann.
Zusätzlich soll es eine Möglichkeit geben, die Anwendung für Tutoren zu demonstrieren. Noch ein anderer
docker-compose […] up
Befehl soll alle Dependencies (diesmal die Datenbank und MinIO) und die Anwendung lokal
starten.
In all diesen Fällen sollen sämtliche Umgebungsvariablen und Startparameter richtig konfiguriert sein.
Gelöst wird dies durch das Benutzen von mehreren docker-compose
.yml Dateien, die zusammen geladen werden.
Denn docker-compose
erlaubt es, bereits definierte Services in einer anderen Konfigurationsdatei zu ergänzen.
Also gibt es nun:
docker-compose.yml
: Enthält die Definition des Datenbank-Containers ohne Konfiguration.
docker-compose.dev.yml
: Enthält die Definition des MinIO-Containers mit Konfiguration und zusätzlich die Konfiguration
für den in der docker-compose.yml
definierten Datenbank-Container.
docker-compose.prod.yml
: Enthält die Definition des Anwendungs-Containers mit Build-Anweisungen und Konfiguration und
zusätzlich die Konfiguration für den in der docker-compose.yml
definierten Datenbank-Container.
docker-compose.demo.yml
: Überschreibt Teile der Konfiguration des Anwendungs-Containers aus der
docker-compose.prod.yml
und fügt ein weiteres depends_on
auf den MinIO-Container aus docker-compose.dev.yml
ein.
Geladen werden die Konfigurationen aus den Dateien dev.env
und prod.env
.
Eine vollständige Liste der Umgebungsvariablen findet sich in der prod.env
und diese sind dort auch dokumentiert.
Somit kann man sich für jeden Fall aus den docker-compose
-Konfigurationsdateien die passende Konfiguration
zusammenbauen.
4.2. Controller Tests
Eine Schwierigkeit der Controller-Tests liegt in der Berücksichtigung der sicherheitsrelevanten Aspekte von
spring-security
und keycloak
.
Wir hatten uns zunächst dafür entschieden diesen Teil zu mocken.
Anfangs hatten wir Probleme, @WithMockUser
zu verwenden, daher entschieden wir uns für das Mocken des gesamten
SecurityContext. Durch SecurityContextUtil
wird dieser jedem ControllerTest zur Verfügung gestellt.
Später wurden wir dann auf die Bibliothek spring-security-test-keycloack-addons
aufmerksam, die das Testen
massiv vereinfacht. Nun kann man die Annotation @WithMockKeycloakAuth
benutzen, um Tests mit einer gültigen Keycloak
Authentifizierung auszustatten.
4.3. Datenbank
Zur Auswahl standen hier MySQL, MariaDB und PostgreSQL, da wir mit diesen Datenbanken bereits Erfahrungen gesammelt haben.
Der Großteil unserer Erfahrungen beruhte bisher auf MySQL, also haben wir uns zunächst für die neuere Variante MariaDB entschieden.
Leider dauert das Starten der MariaDB-Datenbank in einem Docker-Container recht lange (ähnlich wie MySQL) und es ist umständlich, diesen Docker-Container dauerhaft aktiviert zu haben. Aus diesem Grund haben wir uns entschieden, während des Testings H2 zu verwenden. H2 ist eine Embedded In-Memory Datenbank, die sehr schnell startet und die wir bereits kennengelernt haben.
Nach einiger Zeit sind wir dann doch auf PostgreSQL umgestiegen, weil es von den Server-Admins der HHU bevorzugt wird und für ein Testdeployment auf Heroku notwendig ist. Das war ein schmerzloser Prozess, da wir bis dahin noch nichts mit der echten Datenbank gemacht haben.
4.4. Logging
Für das Logging haben wir uns zunächst auf zwei Fälle beschränkt.
Zum einen info
und error
.
Die info
werden ausschließlich in den Controller geschrieben, da es sonst zu redundanten Lognachrichten kommen würde.
Dies ist ausreichend, da jede Aktion unserer Applikation von einem Controller gestartet wird.
Die error
-Nachrichten werden an jeder Stelle geschrieben, wo auch eine Exception
geschmissen wird.
Dies soll es einfach im Log machen den Stacktrace
nach zu verfolgen.
Als Technologie benutzen wir SLF4J
(Simple Logging Face for Java).
Dies ist nativ in Spring-Boot 2.x
und lässt sich mit einer einfachen Annotation (SLF4J
) jeder Klasse hinzufügen.
Wir haben SLF4J
nicht konfiguriert, also wird standardmäßig Logback
benutzt.
Wir haben uns dafür entschieden, weil unsere Internetrecherche die meisten Treffer dazu ergeben hat und unser Tutor uns unabhängig davon zu geraten hat. Die Einfachheit des Tools ist ebenfalls ein Entscheidungsgrund gewesen.
5. Bausteinsicht
5.1. Gesamtabgrenzung
- Enthaltene Bausteine
Komponente | Funktion | Schnittstellen |
---|---|---|
Keycloak |
Authentifizierung und Authentifikation der User. |
Wir erzeugen uns dafür ein eigenes Account Objekt. |
Gruppenbildung |
Erzeugen und Verwalten von Gruppen. |
Erfragen von Gruppendaten und Berechtigungen. |
5.2. Ebene 1
- Enthaltene Bausteine
Komponente | Funktion | Schnittstellen |
---|---|---|
Presentation Layer |
Enthält alles, um die Darstellung der Daten für den Benutzer zu ermöglichen. |
Es werden Anfragen an die Business Layer gestellt, um von dort die benötigten Daten zu erhalten. |
Business Layer |
Enthält die Services, welche die eigentliche Logik der Anwendung darstellen. Hier werden die rohen Daten verarbeitet und für die Presentation Layer nutzbar gemacht. |
Es werden Anfragen an die Persistence Layer gestellt, um von dort die benötigen rohen Daten zu erhalten. |
Persistence Layer |
Enthält die Repositories, welche die Anfragen der Business Layer an die Database Layer weiterleiten. |
Es werden Anfragen an die Database Layer gestellt, um die Daten aus der Datenbank bzw. MinIO zu erhalten. |
Database Layer |
Enthält die Datenbank sowie MinIO, welche die absoluten Rohdaten enthalten. |
Es werden keine Anfragen gestellt, die Database Layer erhält nur Anfragen. |
5.3. Ebene 2
Ebene 2 zeigt die einzelnen Schichten im Detail und deren Komponenten. Darstellung nach gleichem Schema wie zuvor.
Erklärung:
Von der HTML Presentation aus wird je nach Route ein bestimmter Controller in der Presentation Layer angesteuert.
Jeder Controller repräsentiert damit einen Routenanfang /material1/{routenBeginn}
.
Hierdurch werden zu große Controller vermieden und Übersicht geschaffen.
Die Services der Business Layer bearbeiten die Anfragen der Controller und stellen diese die angefragten Daten zur Verfügung. Die von ihnen benötigten Daten holen sie sich wiederum von der Persitence Layer.
In der Persistence Layer liegen die Repositorys, die im Prinzip direkte Schnittstellen zwischen den Anfragen der Services und der Database Layer darstellen. Sie verarbeiten also deren Anfragen und leiten sie an die Database Layer weiter.
Die Database Layer wiederum beinhaltet alle Daten, die bei uns gespeichert werden. Sie stellen diese ja nach Anfrage der Repositorys zur Verfügung.
- Presentation Layer
Komponente | Funktion | Schnittstellen |
---|---|---|
DirectoryController |
Stellt den Inhalt eines angefragten Ordners zur Verfügung. |
Dateiinformation bekommt er vom |
ExceptionController |
Fängt sämtliche Exceptions, die im Projekt geworfen werden und stellt diese bereit. |
Ein Administrator kann hier alle Exceptions einsehen. |
FileController |
Stellt angefragte Dateien zur Verfügung. |
Alle Informationen bekommt er vom |
GroupController |
Stellt den Inhalt einer Gruppe zur Verfügung. |
Gruppeninformationen werden beim |
GroupsController |
Stellt alle Gruppen eines Benutzers zur Verfügung. |
Alle Gruppeninformationen erhält er vom |
Material1Controller |
Stellt Weiterleitungen für den initialen Aufruf und für das Logout bereit. |
Dafür muss er auf keine weitere Komponente zugreifen. |
HTML Presentation |
UI |
Stellt Anfragen an die Controller. |
- Business Layer
Komponente | Funktion | Schnittstellen |
---|---|---|
DeleteService |
Ermöglicht das Löschen von kompletten Ordnern inklusive aller Unterordner sowie enthaltenen Dateien. |
Um die Berechtigungen für das Löschen zu überprüfen, wird auf den |
SearchService |
Stellt die Suche nach Dateien in Ordnern und Unterordnern bereit. |
Um die Such-Operationen durchzuführen, werden der |
DirectoryService |
Stellt sämtliche Funktionen zur Verwaltung eines Ordners zur Verfügung. Das beinhaltet das Erstellen, Löschen und Durchsuchen von Ordnern, das Wechseln zu einem Unterordner, sowie das Hochladen einer Datei in den Ordner. |
Die Berechtigungen des Ordners werden vom |
FileInfoService |
Verwaltet alle Metadaten einer Datei in Form des Objektes |
Um die benötigten Daten zu erhalten wird eine Anfrage an das |
FileService |
Verwaltet die Funktionen einer Datei. Dabei wird die Datei selbst mit ihren Metadaten verknüpft. Ermöglicht außerdem das Löschen von Dateien. |
Mittels einer Anfrage an den |
GarbageCollector |
Löscht in regelmäßigen Zeitintervallen verwaiste Metadaten von Dateien oder Binaries. Außerdem werden nicht mehr existierende Gruppen inklusive all ihrer Inhalte gelöscht. |
Holt sich die Metadaten von Dateien vom |
GruppenbildungsService |
Erlaubt es Anfragen an die |
Kommunziert nur über eine REST-API mit der |
GroupUpdater |
Überprüft in regelmäßigen Zeitintervallen die aktuell existierenden Gruppen und aktualisiert die bei uns gespeicherten Daten. |
Vom |
GroupService |
Stellt den Zugang zu unserer Gruppen-Datenbank bereit. |
Befragt die Datenbank über das |
LatestEventIdService |
Stellt den Zugang zu der gespeicherten |
Befragt die Datenbank über das |
PermissionService |
Stellt die Berechtigungen eines Ordners zur Verfügung. |
Befragt die Datenbank über das |
PrometheusComponent |
Registriert die Prometheus Statistiken, um diese einem externen Tool zur Verfügung zu stellen. |
Beim |
SecurityService |
Überprüft die Rollenberechtigungen eines Nutzers. |
Er vergleicht die Berechtigungen, die aus dem |
ZipService |
Zippt einen Ordner. |
Die Informationen dazu bekommt er vom |
TimeService |
Stellt die aktuelle Zeit bereit. |
Befragt die Java Zeit-API nach der aktuellen Zeit. |
- Persistence Layer
Komponente | Funktion | Schnittstellen |
---|---|---|
DirectoryPermissionRepository |
Ist die Schnittstelle zur |
Stellt eine Anfrage an die |
DirectoryRepository |
Ist die Schnittstelle zur |
Stellt eine Anfrage an die |
FileInfoRepository |
Ist die Schnittstelle zur |
Stellt eine Anfrage an die |
GroupRepository |
Ist die Schnittstelle zur |
Stellt eine Anfrage an die |
LatestEventIdRepository |
Ist die Schnittstelle zur |
Stellt eine Anfrage an die |
FileRepository |
Ist die Schnittstelle zu |
Stellt eine Anfrage mit einer DateiID an |
- Database Layer
Komponente | Funktion | Schnittstellen |
---|---|---|
Datenbank |
Stellt sämtliche Datenbankobjekte zur Verfügung. |
Es werden ausschließlich Anfragen an die |
MinIO |
Stellt benötigte Dateien zur Verfügung. |
Es werden ausschließlich Anfragen an |
5.4. Controller Paths
- DirectoryController
-
Dieser hat folgende Routen:
Parameter | Description |
---|---|
|
The directory id. |
Parameter | Description |
---|---|
|
The directory id. |
Parameter | Description |
---|---|
|
The directory id. |
Parameter | Description |
---|---|
|
The directory id. |
Parameter | Description |
---|---|
|
The directory id. |
Parameter | Description |
---|---|
|
The directory id. |
Parameter | Description |
---|---|
|
The directory id. |
Parameter | Description |
---|---|
|
The directory id. |
- ExceptionController
-
Dieser hat die folgenden Routen:
$ http GET 'http://localhost:8080/material1/error'
- FileController
-
Dieser hat folgende Routen:
Parameter | Description |
---|---|
|
The file id. |
Parameter | Description |
---|---|
|
The file id. |
Parameter | Description |
---|---|
|
The file id. |
Parameter | Description |
---|---|
|
The file id. |
- GroupController
-
Dieser hat folgende Routen:
Parameter | Description |
---|---|
|
The group id. |
Parameter | Description |
---|---|
|
The group id. |
Parameter | Description |
---|---|
|
The group id. |
- GroupsController
-
Dieser hat folgende Routen:
$ http GET 'http://localhost:8080/material1/groups'
- Material1Controller
-
Dieser hat die folgenden Routen:
$ http GET 'http://localhost:8080/material1'
6. Entwurfsentscheidungen
6.1. Aufbau der Datenbank
6.1.1. Directory
Das Datenobjekt Directory
stellt einen Ordner dar.
Directory
hat ein Attribut name
. Dieses stellt den Namen des Ordners dar.
Außerdem hat der Entity Directory
ein Attribut group
.
Dort wird eine ID gespeichert, welche die Gruppe des Ordners identifiziert.
Zusätzlich hat ein Ordner eine eindeutige ID
.
6.1.2. DirectoryPermissions
Das Datenbankobjekt DirectoryPermissions
stellt die Berechtigungen für die erstellten Ordner dar.
Die DirectoryPermissions
haben eine eindeutige ID
.
6.1.3. DirectoryPermissionsEntry
Das Datenbankobjekt DirectoryPermissionsEntry
stellt Berechtigungsinformationen dar.
DirectoryPermissionsEntry
hat eine eindeutige ID
.
Außerdem hat DirectoryPermissionsEntry
ein Attribut role
. role
stellt die Rolle (Student, Organisator) in der
Gruppe dar. Für die Rolle gibt es Informationen, ob der Benutzer lesen, schreiben oder löschen darf. Aus diesem Grund
haben wir 3 boolean
Attribute für Lesen, Schreiben und Löschen erstellt (canRead
, canWrite
, canDelete
).
6.1.4. FileInfo
In dem Datenbankobjekt FileInfo
sind Informationen einer Datei dargestellt.
Das Attribut owner
speichert den Namen des Inhabers der Datei.
Außerdem speichern wir die Größe (size
), den Namen (name
) und den Typ der Datei (type
).
Zusätzlich hat jede FileInfo
eine eindeutige ID
.
6.1.5. FileTag
Hier speichern wir Tags für die Files (Dateien). Tags sind Kategorien oder Etiketten, die zur Suche und Filterung von
Dateien nützlich sind. Das Attribut name
speichert den Namen des Tags.
Außerdem hat jeder Tag eine eindeutige ID
.
6.1.6. Beziehungen
Directory hat FileInfo
Hier haben wir eine 1 zu N
Beziehung, da ein Ordner (Directory
) mehrere Dateien (FileInfo
) enthalten kann.
Im Gegenzug kann eine Datei in genau einem Ordner liegen. Entweder sie liegt im Root-Directory, oder in einem
Unterordner.
Directory parent Directory
Directory
hat eine rekursive Beziehung mit sich selbst. Ein Ordner kann mehrere Unterordner haben und jeder
Ordner liegt in höchstens einem Ordner. Ein Ordner kann auch in keinem Ordner liegen, da wir beschlossen haben, dass
eine Gruppe auch ein Ordner ist. Dies wird als Stammordner (Root-Directory
) der Gruppe bezeichnet.
FileInfo hat FileTag
Hier haben wir eine 1 zu N
Beziehung. Logischerweise kann eine Datei mehrere Tags haben, aber immer wenn ein Tag
erstellt wird, wird er genau zu einer Datei zugeordnet.
Directory hat DirectoryPermissions
Jedem Ordner wird genau eine Berechtigung zugeteilt. Dadurch wissen wir, welche Zugriffsberechtigungen die Rollen haben.
Eine Berechtigung (DirectoryPermissions
) kann mehreren Ordnern zugeteilt werden, da wir beschlossen haben, dass nur
die one-level Ordner eine Berechtigung haben.
Alle Unterordner erben die Berechtigungen der one-Level Ordner. Außerdem hat das Root-directory auch eine eigene
Berechtigung (DirectoryPermissions
), denn hier können auch Dateien gespeichert werden.
DirectoryPermissions hat DirectoryPermissionsEntry
Hier haben wir eine 1 zu N
Beziehung.
Das Datenbankobjekt DirectoryPermissions
erhält Berechtigungsinformationen von
DirectoryPermissionsEntry
. Somit kann eine Permission
mehrere Berechtigungsinformationen für bestimmte Rollen haben.
Wiederum wird immer eine DirectoryPermissionsEntry
zu genau einer DirectoryPermissions
zugeordnet.
6.2. ArchUnit Tests
Zur Vorbereitung haben wir uns die Vorlesung zu ArchUnit Vorlesung 14 PP2
durchgelesen und bearbeitet. Danach haben
wir die Tests AGGREGATE_ROOT_PUBLIC_NOTHING_ELSE
, ONE_AGGREGATE_ROOT_PER_PACKAGE
und CHECK_LAYERED_ARCHITECTURE
geschrieben und dazu eine Hilfsannotation AggregateRoot
erstellt.
Die Funktion des ersten Tests ist die Überprüfung dass nur AggregateRoots
public
und alle anderen Klassen private
sind.
Der zweite Test, stellt sicher, dass es nur eine einzige AggregateRoot
pro Package gibt, welche mit anderen
Packages kommunizieren kann.
Der dritte Test überprüft, ob die Schicht-Architektur eingehalten wird.
Die zusätzliche Annotation ermöglicht es, die AggregateRoot
zu markieren, um die Überprüfung zu erleichtern. Die
Lesbarkeit und Verständlichkeit der Klassen und Packages werden dadurch auch verbessert.
Hier haben wir drei neue Tests hinzugefügt:
Der vierte Test ALL_CONTROLLERS_SHOULD_RESIDE_IN_MOPS_PRESENTATION
sorgt dafür, dass alle Controller im
Presentation-Layer liegen, da dies bei der Schicht-Architektur eine wichtige Vorgabe ist.
Der fünfte Test EVERYTHING_IN_PRESENTATION_SHOULD_BE_A_CONTROLLER
ist eine Ergänzung des vorherigen Test und stellt sicher,
dass sich alle Controller im Presentation
-Package befinden, da sie nur von dort erreichbar sein sollen.
Der letzte Test, ARE_THERE_ANY_CYCLES_WITHIN_PACKAGES
, ist eine Überprüfung, dass keine cycles
im Projekt auftreten. Dies wird durch die Schicht-Architektur
vorausgesetzt ist, aber dies ist ein zusätzlicher Test zur Sicherstellung,
ob dies auch wirklich eingehalten wird, falls z.B. der LayerCheck nicht alles erwischt kann dies ein Auffangmechanismus sein.
Dann haben wir noch zwei weitere Tests für Services implementiert:
Der nun siebte Test SERVICES_ARE_ANNOTATED_WITH_SERVICE
untersucht, ob alle Services auch richtig mit @Service
annotiert sind.
Der achte Test, der mit dem siebten sehr in Einstimmung ist, überprüft, ob alle Klassen, die mit @Service
annotiert sind, auch Service im Namen haben, damit die Struktur des Projekts nicht verwirrend wird.
6.3. FileStorage
Als Schnittstelle für die Dateispeicherung haben wir MinIO gewählt, weil es vorgegeben wurde. Die erforderliche Konfiguration erfolgt über Umgebungsvariablen um einen möglichst flexiblen Einsatz zu ermöglichen.
Die erforderlichen Umgebungsvariablen lauten:
-
MATERIAL1_MINIO_HOST
-
MATERIAL1_MINIO_PORT
-
MATERIAL1_MINIO_BUCKET_NAME
-
MINIO_ACCESS_KEY
-
MINIO_SECRET_KEY
Im Entwicklungsprozess sind diese durch Default-Werte (welche in der application.properties
) vorgegeben.
Die Komponente stellt eine einfache Schnittstelle für die Dateispeicherung dar und kann einfach ersetzt werden, wenn eine andere Lösung zur Speicherung von Dateien genutzt wird.
7. Glossar
Begriff | Definition |
---|---|
Directory |
Ein (virtueller) Ordner im System. |
Directory Permissions |
Die Berechtigungen (Lesen, Hochladen, Löschen) die jede Rolle hat; einem Ordner zugeordnet. |
File Info |
Die Metadaten einer Datei (Name, Größe, Typ) im System. |
FileListEntry |
Kombiniert die Metadaten einer Datei und ihre Berechtigungen für eine einfachere Anzeige im HTML-Template. |
MinIO |
Die verwendete Cloud-Speicherlösung, die zur Speicherung der Nutzerdateien benutzt wird. |
PostgreSQL |
Das verwendete Datenbankmanagementsystem. Hier werden Verzeichnisse und Metadaten gespeichert. |
Root Directory |
Ein Ordner, der eine Gruppe im System repräsentiert. Jede Gruppe hat genau ein Root Directory. |
Rolle |
Die Berechtigungsstufe eines Nutzers im System; je nach Kontext entweder global oder lokal in einem Kurs. |
Test (Modultest) |
Modultests überprüfen dauerhaft die Funktion aller Komponenten (Module) des Systems. Dadurch wird sichergestellt, dass keine Fehler während der Entwicklung übersehen werden. |