From 7fcdc1c788725f866de71fc9dfd8c4d1cb132b57 Mon Sep 17 00:00:00 2001 From: Orangerot Date: Fri, 24 May 2024 17:42:08 +0200 Subject: Initial commit --- 10-entwurfsheft/sections/apidoc.tex | 558 ++++++++++++++++++++++++++++++++ 10-entwurfsheft/sections/aufbau.tex | 89 +++++ 10-entwurfsheft/sections/backend.tex | 28 ++ 10-entwurfsheft/sections/changes.tex | 41 +++ 10-entwurfsheft/sections/einleitung.tex | 22 ++ 10-entwurfsheft/sections/frontend.tex | 150 +++++++++ 10-entwurfsheft/sections/glossar.tex | 371 +++++++++++++++++++++ 10-entwurfsheft/sections/structure.tex | 104 ++++++ 8 files changed, 1363 insertions(+) create mode 100644 10-entwurfsheft/sections/apidoc.tex create mode 100644 10-entwurfsheft/sections/aufbau.tex create mode 100644 10-entwurfsheft/sections/backend.tex create mode 100644 10-entwurfsheft/sections/changes.tex create mode 100644 10-entwurfsheft/sections/einleitung.tex create mode 100644 10-entwurfsheft/sections/frontend.tex create mode 100644 10-entwurfsheft/sections/glossar.tex create mode 100644 10-entwurfsheft/sections/structure.tex (limited to '10-entwurfsheft/sections') diff --git a/10-entwurfsheft/sections/apidoc.tex b/10-entwurfsheft/sections/apidoc.tex new file mode 100644 index 0000000..1eb181c --- /dev/null +++ b/10-entwurfsheft/sections/apidoc.tex @@ -0,0 +1,558 @@ +\newenvironment{urlParameter} +{ + \newcommand{\urlParamItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\noUrlParameter}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{URL-Parameter} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + +\newenvironment{pathParameter} +{ + \newcommand{\pathParamItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\noPathParameter}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{Pfad-Parameter} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + +\newenvironment{jsonKeys} +{ + \newcommand{\jsonKeyItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\nojsonKeys}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{Json-Keys} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + +\section{\Gls{api}} + +\subsection{Hypertext Transfer Protocol} +% könnte auch unter in die Sektion API + +Das Hypertext Transfer Protocol (http) ist ein Protokoll zum übertragen von +medialen Daten. Ein Webserver hört über den Port 80 auf Anfragen und antwortet. +Eine Anfrage besteht aus einer Methode, einem Pfad, Kopfzeilen (\texttt{Header}) +und Inhalt (\texttt{Body}). Die Methode ist eine Konvention, um zu verdeutlichen +was mit der Anfrage geschehen soll. Es können Daten abgefragt (\texttt{GET}), +erstellt (\texttt{POST}), aktualisiert (\texttt{PUT}) oder gelöscht werden +(\texttt{DELETE}). Der Pfad gibt an, für welche Daten die Methode gelten soll. +Die Kopfzeilen beinhalten dabei Metadaten, wie Authentifikationen, \Glspl{cookie}, +Datentypen (MIME-Types), Sprache und das Anwenderprogramm. + +Der Server antwortet mit einem Statuscode, Kopfzeilen und Inhalt. Der Statuscode +teilt mit, ob die Anfrage erhalten wurde ($\geq$ 100), erfolgreich bearbeitet +werden konnte ($\geq$ 200), eine Weiterleitung nötig ist ($\geq$ 300), +die Anfrage einen Fehler enthält ($\geq$ 400) oder +ein interner Serverfehler aufgetreten ist ($\geq$ 500). In den Kopfzeilen können +Berechtigungen gesetzt werden, welche festlegen, wie mit den Daten +umzugehen ist. + +\newpage +\subsection{Geräte} \label{Geräte} +Die \Gls{gpodder}, welche wir für unser Produkt verwenden und erweitern, bietet eine gerätespezifische +Synchronisation an. Diese bieten wir jedoch nicht an, denn wir synchronisieren Änderungen aller +Geräte gleichwertig mit dem Nutzeraccount. +Deshalb werden Geräte innerhalb der API ignoriert. + +\subsection{Authentication API}\label{a:auth} + +\subsubsection{Registrierung}\label{a:register} + +\begin{apiRoute}{post}{/api/2/auth/register.json} + {Registriert einen Nutzer mit einer E-Mail-Adresse und Passwort. + Versendet E-Mail mit Bestätigungslink an die angegebene E-Mail-Adresse.} + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + email: "jeff@example.com", + password: "MyNameIsJeff" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Nutzer wurde erfolgreich angelegt.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Fehler beim Parsen der Anfrage} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage + +\subsubsection{Anmelden}\label{a:login} + +\begin{apiRoute}{post}{/api/2/auth/\{username\}/login.json} + {Gegebenen Nutzer des gegebenen Geräts mithilfe HTTP Basic Auth einloggen.} + \begin{pathParameter} + \pathParamItem{username}{Nutzername des einzuloggenden Nutzers} + \end{pathParameter} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Die Responseheader haben ein gesetztes Session-ID \Gls{cookie}.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Der Client stellt einen \Gls{cookie} für einen falschen Nutzer bereit.} + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: Zugriff ohne Anmeldedaten} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\subsubsection{Abmelden}\label{a:logout} + +\begin{apiRoute}{post}{/api/2/auth/\{username\}/logout.json} + {Löscht den \Gls{cookie} beim Client.} + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Falls der Client keinen \Gls{cookie} gesendet hat oder der Nutzer + erfolgreich ausgeloggt wurde.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Der Client stellt einen \Gls{cookie} für den falschen Nutzer bereit.} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage + +\subsubsection{Passwort ändern}\label{a:changepassword} + +\begin{apiRoute}{put}{/api/2/auth/\{username\}/changepassword.json} + {Passwort des gegebenen Nutzer ändern, indem altes und neues Passwort + übergeben werden. } + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + password: "MyPasswordWasLeaked", + new_password: "SoIMadeANewOne" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Die Responseheader haben ein gesetztes Session-ID \Gls{cookie}.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Der Client stellt einen \Gls{cookie} für einen falschen Nutzer bereit.} + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: Zugriff ohne Anmeldedaten} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\subsubsection{Passwort vergessen}\label{a:forgot} + +\begin{apiRoute}{post}{/api/2/auth/forgot.json} + {Sende eine E-Mail zum Zurücksetzen des Passworts. } + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + email: "jeff@example.com" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: E-Mail wurde erfolgreich versendet.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Fehler beim Parsen der Anfrage} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage +\subsubsection{Passwort zurücksetzen}\label{a:resetpassword} + +\begin{apiRoute}{put}{/api/2/auth/\{username\}/resetpassword.json} + {Passwort des gegebenen Nutzers ändern nachdem dieser sein Passwort + vergessen hat. } + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + \begin{urlParameter} + \urlParamItem{token}{JSON-Web-Token} + \end{urlParameter} + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + password: "APasswordIWontForgetAgain" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Das Passwort wurde erfolgreich geändert.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Fehler beim Parsen der Anfragen. } + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: JWT ist ungültig. } + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage +\subsubsection{Account löschen}\label{a:delete} + +\begin{apiRoute}{delete}{/api/2/auth/\{username\}/delete.json} + {Der Account des gegebenen Nutzers wird gelöscht.} + \begin{pathParameter} + \pathParamItem{username}{Nutzername des zu löschenden Nutzers.} + \end{pathParameter} + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + password: "APasswordIWontHaveToRememberAnymore" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Der Account des gegebenen Nutzers wurde erfolgreich gelöscht.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Der Client stellt einen \Gls{cookie} für einen falschen Nutzer bereit.} + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: Zugriff ohne Anmeldedaten} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage +\subsection{Subscriptions API}\label{a:subs} +\subsubsection{Abrufen aller \Glspl{abo}}\label{a:getSubs} + +\begin{apiRoute}{get}{/subscriptions/\{username\}.json}{/subscriptions/\{username\}/\{deviceid\}.json} + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \pathParamItem{deviceid}{siehe~\nameref{Geräte}} + \end{pathParameter} + + \begin{urlParameter} + \urlParamItem{jsonp}{\Gls{JSONP} wird nicht unterstützt und der Parameter entsprechend ignoriert.} + \end{urlParameter} + + \begin{routeResponse}{application/json} + + \begin{routeResponseItem}{200} + {OK: \Glspl{abo} werden im \Gls{json}-Format zurückgegeben.} + \begin{routeResponseItemBody} +[ + "http://example.org/feed.rss", + "http://example.org/podcast.php", + "http://example.org/foo" +] + \end{routeResponseItemBody} + \end{routeResponseItem} + + \begin{routeResponseItem}{400}{Bad Request: falsches Format} + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: falscher Nutzer oder nicht eingeloggt} + \end{routeResponseItem} + + \end{routeResponse} +\end{apiRoute} + +\newpage +\subsubsection{Abrufen der Informationen aller \Glspl{abo}}\label{a:getTitles} + +\begin{apiRoute}{get}{/subscriptions/titles/\{username\}.json}{/subscriptions/\{username\}/\{deviceid\}.json} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + \begin{routeResponse}{application/json} + + \begin{routeResponseItem}{200} + {OK: \Glspl{abo} werden im angefragten Format zurückgegeben. Dabei werden für ein \Gls{abo} nur die aktuellsten 20 \Glspl{episode} zurückgegeben.} + \begin{routeResponseItemBody} +[ + { + "url": "http://example.com/podcast.php", + "title": "This is a cool podcast" + "timestamp": "2009-12-12T09:00:00", + "episodes": [ + { + "podcast": "http://example.org/podcast.php", + "episode": "http://ftp.example.org/foo.ogg", + "title": "Episode 1: My journy", + "timestamp": "2009-12-12T09:00:00", + "guid": "AU-20190814-1342-5100-A", + "action": "play", + "started": 15, + "position": 120, + "total": 500 + } + ] + } +] + \end{routeResponseItemBody} + \end{routeResponseItem} + + \begin{routeResponseItem}{400}{Bad Request: falsches Format} + \end{routeResponseItem} + + \end{routeResponse} + +\end{apiRoute} +\newpage + +\subsubsection{\Glspl{abo} hochladen}\label{a:uploadSubs} + +\begin{apiRoute}{put}{/subscriptions/\{username\}/\{deviceid\}.json} + {Falls bereits \Glspl{abo} im Nutzeraccount vorhanden, werden diese + zusammengeführt.} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \pathParamItem{deviceid}{siehe~\nameref{Geräte}} + \end{pathParameter} + + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: \Glspl{abo} wurden aktualisiert und leerer String wird zurückgegeben.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400}{Bad Request: falsches Format} + \end{routeResponseItem} + + \begin{routeResponseItem}{401}{Unauthorized: falscher Nutzer} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\subsubsection{Abrufen von \Gls{abo}-Änderungen}\label{a:getSubDelta} + +\begin{apiRoute}{get}{/api/2/subscriptions/\{username\}/\{deviceid\}.json} + {} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \pathParamItem{deviceid}{siehe~\nameref{Geräte}} + \end{pathParameter} + + \begin{urlParameter} + \urlParamItem{since}{Timestamp-Wert der letzten Antwort} + \end{urlParameter} + + \begin{routeResponse}{application/json} + + \begin{routeResponseItemBody} +{ + "add": [ + "http://example.com/feed.rss", + "http://example.org/podcast.php" + ], + "remove": ["http://example.net/foo.xml"], + "timestamp": 12347 +} + \end{routeResponseItemBody} + \end{routeResponse} + +\end{apiRoute} + +\newpage +\subsubsection{Änderungen der \Glspl{abo} hochladen}\label{a:applySubDelta} + +\begin{apiRoute}{post}{/api/2/subscriptions/\{username\}/\{deviceid\}.json} + {} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \pathParamItem{deviceid}{siehe~\nameref{Geräte}} + \end{pathParameter} + + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + "add": [ + "http://example.com/feed.rss", + "http://example.org/podcast.php" + ], + "remove": ["http://example.net/foo.xml"] +} + \end{routeRequestBody} + \end{routeRequest} + + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200}{OK: \Glspl{abo} wurden aktualisiert.} + \begin{routeResponseItemBody} +{ + "timestamp": 1337, + "update_urls": [] +} + \end{routeResponseItemBody} + ``update\textunderscore urls'' wird nicht bereitgestellt, deshalb + wird eine leere Liste zurückgegeben. + \end{routeResponseItem} + \begin{routeResponseItem}{400}{Bad Request: falsches Format} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage +\subsection{Episode Action API}\label{a:episodeActions} + +\subsubsection{Episode Actions hochladen}\label{a:uploadEpisodeActions} + +\begin{apiRoute}{post}{/api/2/episodes/\{username\}.json} + {} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + + \begin{routeRequest} + + \begin{routeRequestBody} +POST /api/2/episodes/some-user.json +[ + { + "podcast": "http://example.org/podcast.php", + "episode": "http://ftp.example.org/foo.ogg", + "title": "Episode 1: My journey", + "timestamp": "2009-12-12T09:00:00" + "guid": "AU-20190814-1342-5100-A", + "action": "play", + "started": 15, + "position": 120, + "total": 500 + } +] + + \end{routeRequestBody} + + \end{routeRequest} + + \begin{jsonKeys} + \jsonKeyItem{podcast}{Feed-URL des \Glspl{podcast} zu welchem die \Gls{episode} gehört (erforderlich)} + \jsonKeyItem{episode}{Medien-URL der \Gls{episode} (erforderlich)} + \jsonKeyItem{device}{Geräte-ID auf welcher die Aktion stattfand.} + \jsonKeyItem{action}{Eins von: download, play, delete, new (erforderlich)} + \jsonKeyItem{timestamp}{UTC-Timestamp im ISO 8601 Format, wann die Aktion stattfand} + \jsonKeyItem{started}{Position in Sekunden, an welcher das Anhören der \Gls{episode} gestartet wurde (nur bei Aktion: play)} + \jsonKeyItem{position}{Position in Sekunden, an welcher das Anhören der \Gls{episode} gestoppt wurde (nur bei Aktion: play)} + \jsonKeyItem{total}{Länge der \Gls{episode} (nur bei Aktion: play)} + + \end{jsonKeys} + + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200}{OK} + \begin{routeResponseItemBody} +{ + "timestamp": 1337, + "update_urls": [] +} + \end{routeResponseItemBody} + ``update\textunderscore urls'' wird nicht bereitgestellt, deshalb + wird eine leere Liste zurückgegeben. + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\subsubsection{Abrufen von Episode Actions}\label{a:getEpisodeActions} + +\begin{apiRoute}{get}{/api/2/episodes/\{username\}.json} + {} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + + \begin{urlParameter} + \urlParamItem{podcast (string)}{URL des \Glspl{podcast}; falls gesetzt, werden nur Aktionen der \Glspl{episode} des gegebenen \Glspl{podcast} zurückgegeben.} + \urlParamItem{device (string)}{Device-ID; siehe~\nameref{Geräte}} + \urlParamItem{since (int)}{Nur \Glspl{episode}-Aktionen ab dem gegebenen Timestamp werden zurückgegeben.} + \urlParamItem{aggregated (bool)}{Wird ignoriert. } + \end{urlParameter} + + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200}{OK} + \begin{routeResponseItemBody} +{ + "actions": [ + { + "podcast": "http://example.org/podcast.php", + "episode": "http://ftp.example.org/foo.ogg", + "title": "Episode 1: My journey", + "timestamp": "2009-12-12T09:00:00" + "guid": "AU-20190814-1342-5100-A", + "action": "play", + "started": 15, + "position": 120, + "total": 500 + } + ], + "timestamp": 12345 +} + \end{routeResponseItemBody} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + diff --git a/10-entwurfsheft/sections/aufbau.tex b/10-entwurfsheft/sections/aufbau.tex new file mode 100644 index 0000000..c0747a9 --- /dev/null +++ b/10-entwurfsheft/sections/aufbau.tex @@ -0,0 +1,89 @@ +\section{Aufbau} + +\subsection{Architektur} + +\subsubsection{Aufteilung in Pakete} + +Das Backend wird in Pakete aufgeteilt, die den einzelnen implementierten +\Glspl{api} der gpodder.net \Gls{api} entsprechen. +Dies entspricht hier den \Glspl{api}: Subscriptions, Episode Actions und Authentication. +Außerdem gibt es ein Model-Paket, welches die in den Logikschichten verwendeten Objekte modelliert. + +Für den Webserver selbst werden die \Glspl{api} um zusätzliche Funktionen erweitert. Denn die vorhandenen \Glspl{api} bieten nicht alle Funktionalitäten, die der Webserver benötigt, um die im Pflichtenheft beschriebenen Kriterien zu erfüllen. +% (TODO: WebAPI bestimmen) + +\subsubsection{Die Architektur in den Paketen} + +In den Paketen selber herrscht eine 3-schichtige Architektur aus den Schichten: Controller, Service und Data-Access (siehe \ref{DAO_Pattern}). + +Die Controller-Schicht nimmt die Anfragen des Clients entgegen, lässt diese in den untergeordneten Schichten verarbeiten und gibt den Status der abgeschlossenen Handlung an den Client zurück. + +Die Service-Schicht ist die \Gls{business} des Programmes und überprüft bspw. Eingaben oder verschickt E-Mails. Sollte ein \Gls{db}aufruf nötig sein, so ruft der Service die Data-Access-Schicht auf und übergibt ihr die nötigen Informationen, was gespeichert, gelöscht, aktualisiert oder gelesen werden soll. + +Die Data-Access-Schicht ist für den Datenzugriff auf \Glspl{db} verantwortlich. Alle verantwortlichen Klassen für einen solchen Zugriff folgen dem \Gls{crud}-Prinzip. +\Gls{crud} steht für die Funktionen Create (erschaffen), Read (lesen), Update (aktualisieren) und Delete (löschen). Entsprechend können Einträge in einer \Gls{db} auch nur erschaffen, gelesen, aktualisiert und gelöscht werden. + +\subsubsection{Das Model-Paket}\label{p:model} + +In einem globalen Model-Paket befinden sich alle Klassen, die für die Modellierungen von Objekten zuständig sind. +Diese Objekte werden später in \Gls{db}abfragen gelesen und mittels objektrelationaler Abbildung (siehe \ref{t:orm}) gespeichert. +In einer Antwort an den Client werden die Objekte, falls benötigt, als Antwort-Klassen gewrappt im \Gls{json}-Format zurückgeschickt. +Auch kann der Client in einer Anfrage ein Objekt im \Gls{json}-Format übergeben, welches dann mithilfe von Spring als \Gls{java}-Objekt der korrespondierenden Anfrage-Klasse interpretiert wird. +Aus diesem wird dann in Controller- und Service-Schicht das Objekt der entsprechenden Model-Klasse extrahiert. + +\subsubsection{Die Datenhaltungsschicht} + +Mit den Paketen ist bereits eine 3-schichtige Architektur aufgebaut. +Damit Daten aber auch gelesen und gespeichert werden können ist eine vierte globale Schicht nötig - die Datenhaltungsschicht. + +Die Datenhaltungsschicht ist für die persistente Speicherung aller Daten zuständig. +Diese werden meist (wie in diesem Fall) in einer \Gls{db} gespeichert. +Der Zugriff auf die Datenhaltungsschicht erfolgt gemäß der intransparenten Schichtenarchitektur nur über die Data-Access-Schicht der Pakete. + +Der Vorteil dieser mehrschichtigen Architektur ist die klare Strukturierung des Programmes, womit der Code leserlicher und einfacher zu warten ist. + +\begin{landscape} + +\subsection{Klassendiagramm Backend} +Das Klassendiagramm zeigt alle für den Entwurf relevanten Klassen des Backends mit ihren öffentlichen Methoden. +Weiter zeigt das Diagramm die Aufteilung der Klassen in Pakete sowie schemenhaft dargestellte Verbindungen zu \Gls{db} und Webserver. + +% \input{assets/diagrams/classdiagram.latex} +\includegraphics[width=\linewidth]{assets/diagrams/classdiagram} +\end{landscape} + +\subsection{Sequenzdiagramme} + +\subsubsection{Authentication \Gls{api}} +\subsubsection*{Registrierung \scriptsize{(\ref{a:register})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-register} +\subsubsection*{Passwort vergessen und zurücksetzen \scriptsize{(\ref{a:forgot}, \ref{a:resetpassword})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-forgotAndResetPW} + +\subsubsection{Subscriptions \Gls{api}} +\subsubsection*{Abonnements hochladen \scriptsize{(\ref{a:uploadSubs})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-uploadSubscriptions} + +\subsubsection*{Abrufen aller Abonnements \scriptsize{(\ref{a:getSubs})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-getSubscriptions} + +\subsubsection{Episode Actions \Gls{api}} +\subsubsection*{Episode Actions hochladen \scriptsize{(\ref{a:uploadEpisodeActions})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-uploadEpisodeActions} + +\subsubsection*{Abrufen aller Episode Actions seit einem Zeitpunkt \scriptsize{(\ref{a:getEpisodeActions})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince} + +\subsubsection*{Abrufen aller Episode Actions \scriptsize{(\ref{a:getEpisodeActions})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-getEpisodeActions} + +\subsection{Komponentendiagramm Backend} +\includegraphics[width=\textwidth]{assets/diagrams/backendComponentDiagram} + +\subsection{Verteilungsdiagram} +\includegraphics[width=\textwidth]{assets/diagrams/deployment} + +\subsection{\Gls{db}-Modell} + +\includegraphics[width=\textwidth]{assets/diagrams/db} + diff --git a/10-entwurfsheft/sections/backend.tex b/10-entwurfsheft/sections/backend.tex new file mode 100644 index 0000000..19efd65 --- /dev/null +++ b/10-entwurfsheft/sections/backend.tex @@ -0,0 +1,28 @@ +\section{Backend} + +Für das Backend wird das \Gls{java}-Framework \Gls{spring} verwendet. Neben seiner Modularität, +bietet es viele Funktionen, die unter +anderem die Entwicklung von Backend-Anwendungen vereinfachen. +Darunter fällt zum Beispiel die Unterstützung von Dependency Injection, dessen +Vorteile bereits beschrieben wurden. +Außerdem unterstützt \Gls{java} \Gls{spring} intern eine Verwaltung von \Glspl{db}, sodass +sich nicht mit dem \Gls{SQL}-Code an sich befasst werden muss. +Auch bezüglich Authentifizierung und Sicherheit bietet \Gls{spring} eigene Funktionalitäten. + +Zusätzlich dazu wird als Build-System für das \Gls{java}-\Gls{spring}-Backend Maven verwendet. +Maven hilft dabei alle Abhängigkeiten des Projekts zu verwalten und automatisiert +den Build Prozess. +Durch die zusätzliche Unterstützung von Versionskontrollsystemen und der Kompatibilität +zu vielen Continuous Integration-Tools wird außerdem die Entwicklung in einem Team erleichtert +und optimiert. Denn dadurch können Build- und Deployment-Prozesse automatisiert werden. +Außerdem bietet Maven Bibliotheken zum Testen sowie zur Generierung von Dokumentationen an. + +Vom Backend benötigte Abhängigkeiten: +\begin{itemize} + \item \Gls{spring} Web + \item \Gls{spring} Security + \item \Gls{spring} Mail Sender + \item \Gls{spring} Data JPA + \item Lombok + \item Rome (\Gls{rss} parsing/fetching) +\end{itemize} \ No newline at end of file diff --git a/10-entwurfsheft/sections/changes.tex b/10-entwurfsheft/sections/changes.tex new file mode 100644 index 0000000..d8ac2ec --- /dev/null +++ b/10-entwurfsheft/sections/changes.tex @@ -0,0 +1,41 @@ + +\section{Änderungen zum Pflichtenheft} + +Im Folgenden werden die Änderungen zum Pflichtenheft aufgelistet. +Hierbei handelt es sich um neue Kriterien, Kriterien die wegfallen und Kriterien die modifiziert wurden. + +\subsection{Neue Kriterien} + +\subsubsection{Cooldown Fetch-Vorgang} +\begin{description} + \item[\textcolor{Green}{\textlangle RS11\textrangle}] \textcolor{Green}{Wenn ein Nuter ein \Gls{abo} oder eine \Gls{episode} eines \Gls{abo} hinzugefügt hat, welche noch nicht + in der \Gls{db} vorhanden ist, werden die Daten vom entsprechenden Server gefetched. Bis eine weitere fetch-Anfrage + zum selben \Gls{abo} gestartet wird, wird ein Cooldown von einer Stunde abgewartet. + Das hilft dabei DoS Angriffe zu vermeiden, da der Server damit nicht durch zu viele Fetch-Anfragen überlastet + werden kann.} +\end{description} + +\subsubsection{Sprache von Browserinformationen übernehmen} +\begin{description} + \item[\textcolor{Green}{\textlangle RC13\textrangle}] \textcolor{Green}{Betritt ein Nutzer zum ersten Mal die Webseite, ruft ein Skript die Browserdaten zur eingestellten Sprache ab. + Die Sprache wird im localStorage gespeichert und ruft entsprechend die Webseite in der jeweiligen Sprache auf. + Ändert der Nutzer die Sprache, so wird dies durch eine Änderung der Sprachvariable im localStorage festgehalten. } +\end{description} + +\subsection{Abgeänderte Kriterien} + + \subsubsection{Sessionspeicherung mit JWT} + + \begin{description} + \item[\textlangle RS10\textrangle] Im Webfrontend angemeldete Benutzer bleiben dort angemeldet. + Hierfür wird ein \Gls{session-token} in einem \Gls{cookie} gespeichert. + \textcolor{blue}{Als \Gls{session-token} wird ein \Gls{json} Web Token verwendet. + Dieser Token wird für die Authentifikation genutzt.} + \end{description} + +\subsection{Entfernte Kriterien} + + \begin{description} + \item[\textlangle RC5\textrangle] + \textcolor{red}{\sout{Die Weboberfläche ist kompatibel mit beliebigen \Glspl{gpodder}.}} + \end{description} diff --git a/10-entwurfsheft/sections/einleitung.tex b/10-entwurfsheft/sections/einleitung.tex new file mode 100644 index 0000000..0fd6114 --- /dev/null +++ b/10-entwurfsheft/sections/einleitung.tex @@ -0,0 +1,22 @@ +\section{Einleitung} + +Im vorangegangenen Pflichtenheft wurde ein Synchronisationsserver für \Glspl{podcast} beschrieben. +In diesem Entwurfsheft wird die Implementierung dieses Synchronisationsservers +mithilfe eines Entwurfes geplant. +Wie im Pflichtenheft beschrieben, wird das finale Produkt +aus einem Backend und einem Frontend bestehen. + +Das Webfrontend dient dazu, dem Nutzer eine Oberfläche zur Accountverwaltung +und Einsicht seiner Synchronisationsstände und Daten zu ermöglichen. +Dieses wird mithilfe des Javascript Frameworks Vue.js erstellt. + +Das Backend dient dazu, HTTP-Anfragen des Frontends und der \Gls{podcatcher} entgegenzunehmen, +zu verarbeiten sowie zu beantworten. +Damit Daten persistent gespeichert werden können, wird eine \Gls{db} eingebunden. +Zur Erstellung des Backends wird \Gls{java} \Gls{spring} verwendet und für die \Gls{db} MariaDB. + +%Neben den Schichten gibt es drei große Zuordnungsbereiche in unserem Produkt. +%Nämlich die Abonnements die ein Nutzer haben kann, deren Episoden +%und die Accountverwaltung. +%Deshalb unterteilen wir die einzelnen Projektklassen +%schichtenübergreifend gemäß dieser Kategorien nochmals in einzelne Pakete. diff --git a/10-entwurfsheft/sections/frontend.tex b/10-entwurfsheft/sections/frontend.tex new file mode 100644 index 0000000..69a3c61 --- /dev/null +++ b/10-entwurfsheft/sections/frontend.tex @@ -0,0 +1,150 @@ +\section{Weboberfläche} + +Die Weboberfläche wird mit dem Frontend-Web-Framework Vue.js erstellt. Mit Vue +werden wiederverwendbare, auf Datenänderungen reagierende Komponenten erstellt. +Die Komponenten nutzen ein fertiges Aussehen von dem Frontend-CSS-Framework +Bootstrap. Außerdem werden Icons der freien Schriftart fontawesome +verwendet. + +Das Projekt wird durch den \Gls{bundler} vite aufgesetzt, gebaut und stellt einen +Entwicklungswebserver mit Echtzeitvorschau bereit. Durch den vue-router wird bei +der \Gls{spa} ein Seitenwechsel durch Manipulation der +Browser-Chronik (History Manipulation) simuliert. Dadurch hat der Nutzer eine +bessere Erfahrung, weil die Seite nicht neu geladen werden muss, wenn zu einem +anderen Menüpunkt gewechselt wird. + +Wenn der Nutzer sich anmeldet wird die E-Mail-Adresse, der \Gls{session-token} und +Spracheinstellungen in dem globallen Zustandsspeicher gespeichert, welcher durch +die Pinia-Abhängigkeit bereitgestellt wird. Dadurch haben alle +\Glspl{uiComponent} einfachen Zugriff auf die Daten und diese müssen nicht über +Props in tiefliegende \Glspl{uiComponent} durchgereicht werden. + +Die Seiten werden als einzelne Kompontenten erstellt. Andere HTML-Strukturen +werden als eigene Komponenten ausgelagert, wenn sie in mehreren Seiten verwendet +werden und zur Reduzierung von Komplexität und Duplikation beitragen und selbst +Logik beinhalten. + +Vom Frontend benötigte Abhängigkeiten: +\begin{itemize} +\item vite +\item vue +\item vue-router +\item Pinia (globaler Zustandsspeicher) +\item bootstrap +\item fontawesome +\item vue-i18n (Support für mehrere Sprachen) +\end{itemize} + +\subsection{Komponentendiagramm Web-Frontend} +\includegraphics[width=\textwidth]{assets/diagrams/componentdiagram} + +\subsection{Komponentenbeschreibung} + +\begin{minipage}{.7\linewidth} + +\subsubsection*{SubscriptionComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Props] \mbox{} \\ + \emph{subscription} \mbox{} Subscription-Objekt, welches Attribute zu einem \Gls{podcast} und + dessen \Glspl{episode} enthält. +\item[Beschreibung] Nimmt ein Subscription-Objekt, zeigt Titel und LastUpdate und +\Glspl{episode} der Subscription an. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/subscription.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} +\begin{minipage}{.7\linewidth} +\subsubsection*{EpisodeComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Props] \mbox{} \\ + \emph{episode} \mbox{} EpisodeAction-Objekt, welches Titel, \Gls{podcast}, + Timestamp, Dauer und Hörfortschritt der \Gls{episode} enthält. +\item[Beschreibung] Nimmt ein EpisodeAction-Object, zeigt Titel, \Gls{podcast}, Dauer, + Hörfortschritt und LastUpdate an. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/episode.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} +\begin{minipage}{.7\linewidth} +\subsubsection*{LastUpdateComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Props] \mbox{} \\ + \emph{timestamp} \mbox{} Date-Objekt, welches die Zeit der letzten Änderung enthält +\item[Beschreibung] Nimmt ein Timestamp und gibt die Zeit seit dem Timestamp in + einem menschenlesbaren Format aus. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/lastupdate.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} +\begin{minipage}{.7\linewidth} +\subsubsection*{HelpComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Beschreibung] Zeigt Hilfestellungen in einem Fenster an. Dieses kann über + die Navigationsleiste aufgerufen werden. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/help.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} +\begin{minipage}{.7\linewidth} +\subsubsection*{NavbarComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Beschreibung] Enthält route-links zu im vue-router definierten Pfaden. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \fbox{\includegraphics[width=\textwidth]{assets/navbar.png}} + \end{figure} +\end{minipage} + + +\vspace{.5cm} +\begin{minipage}{.7\linewidth} +\subsubsection*{PasswordValidatorComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Beschreibung] Überprüft ob der eingegebene Text die Bedingungen für ein + sicheres Passwort erfüllt. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/password-margin.png} + \end{figure} +\end{minipage} + diff --git a/10-entwurfsheft/sections/glossar.tex b/10-entwurfsheft/sections/glossar.tex new file mode 100644 index 0000000..9caad87 --- /dev/null +++ b/10-entwurfsheft/sections/glossar.tex @@ -0,0 +1,371 @@ +\makeglossaries + +\newglossaryentry{spa} +{ + name=Single-Page-Application, + description={ + ist ein Webseiten-Modell, bei welchem dem Nutzer nur ein Webdokument + bereitgestellt wird. Mit einem Skript wird der Inhalt der Seite + dynamisch mit Daten einer API befüllt. Außerdem verwaltet die Seite + (nicht der Server), welcher Inhalt bei welchem Pfad angezeigt wird. Dies + erzeugt geringere Serverlast und eine bessere Nutzererfahrung, da die + Seitenstruktur beim Laden von neuen Inhalten erhalten bleibt} +} + +\newglossaryentry{packagemanager} +{ + name=Paketmanager, + description={ + ist ein Programm, welches Pakete und dessen Abhängigkeiten verwaltet, + installiert, entfernt und aktualisiert. Pakete können andere Programme, + Plugins oder Software-Bibliotheken sein} +} + +\newglossaryentry{bundler} +{ + name=Bundler, + description={ + ist ein Programm, welches genutzte Teile von Abhängigkeiten eines + Software-Projekts in passender Reihenfolge zusammensucht und daraus + Dateien erstellt, die für den Nutzer bereitgestellt werden können. Dabei + kann der Bundler mit zusätzlichen Modulen Dateien erzeugen, die + rückwärtskompatibel oder für den Nutzer schwerer einsehbar sind} +} + +\newglossaryentry{java} +{ + name=Java, + description={ + ist eine objekt-orientierte interpretierte kompilierte + Programmiersprache, welche plattformunabhängig auf einer virtuellen + Maschine ausgeführt wird} +} + +\newglossaryentry{db} +{ + name=Datenbank, + plural=Datenbanken, + description={ + ist ein System um Daten persistent zu speichern und effizient zu + verwalten. Am meisten verbreitet sind relationale Datenbanken, welche + Daten in Tabellen mit Referenzen zu Einträgen anderer Tabellen + speichern. Programme können dann über eine Anfragesprache (Structured + Query Language - \Gls{SQL}) komplexe Operationen auf den Daten ausführen} +} + +\newglossaryentry{docker} +{ + name=Docker, + description={ + ist ein Programm, das virtualisierte Container ausführt. Ein Programm in + so einem Container läuft in seiner eigenen virtuellen Umgebung, wodurch + das Host-System sicher bleibt. Zudem lassen sich die Container leicht + auf andere Systeme verteilen} +} + +% RESTfull-API, JSON, RSS-Feed, Salting and Hasing, OAuth, Cookie, Garbage +% Collection, DSGVO, Podcast, Podcatcher, Episode, Gpodder, + +\newglossaryentry{podcatcher} +{ + name=Podcatcher, + plural=Podcatchern, + description={ + ist ein Programm, über welches man Podcasts entdecken, abonnieren und + Episoden von Podcasts hören kann. Mit einem Account auf einer Plattform, + welche eine Gpodder-API zur Verfügung stellt, können Ereignisse, die von + einem Nutzer ausgehen, auf anderen Podcatchern des Nutzers + synchronisiert werden} +} + +\newglossaryentry{podcast} +{ + name=Podcast, + description={ + ist ein RSS-Feed, dessen Einträge die Episoden darstellen} +} + +\newglossaryentry{episode} +{ + name=Episode, + plural=Episoden, + description={ + ist ein Eintrag in einem Podcast. Eine URL in dem Eintrag zeigt auf eine + Medien-Datei, welche vom Podcatcher abgespielt werden kann} +} + +\newglossaryentry{rest-api} +{ + name=RESTful-API, + description={ + ist ein Schnittstellenentwurf über das Hypertext Transfer Protocol + (HTTP), bei dem die Schnittstellen strukturiert als Pfad an einem + Endpunkt erreichbar sind. Mittels verschiedener HTTP-Methoden können an + der Schnittstelle Daten abgefragt (GET), gesendet (PUT), gelöscht + (DELETE) oder geändert (POST) werden. Die Daten, die über die + Schnittstelle gesendet werden liegen meist im JSON-Format vor} +} + + +\newglossaryentry{gpodder} +{ + name=Gpodder-API, + description={ + wird von gpodder.net benutzt und entwickelt. Die API wird als + Schnittstelle zwischen Podcatchern und Podcast Synchronisationsservern + verwendet. Weitere Details sind unter + "https://gpoddernet.readthedocs.io/en/latest/api/" zu finden} +} + +\newglossaryentry{json} +{ + name=JSON, + description={ + (JavaScript Object Notation) ist ein Datenformat und wird zur + Übertragung von Strukturen und Daten eingesetzt. JSON besteht dabei aus + grundlegenden Datentypen sowie Objekten mit Schlüssel-Wert Paaren und + Listen} +} + +\newglossaryentry{oauth} +{ + name=OAuth, + description={ + (Open Authorization) ist ein offenes Protokoll, welches es Nutzern + ermöglicht, sich mit bereits bestehenden Accounts bei anderen Diensten + zu registrieren. Dabei werden benötigte Daten für die Registrierung über + die bereitgestellte Schnittstelle zur Verfügung gestellt} +} + +\newglossaryentry{garbage-collection} +{ + name=Garbage Collection, + description={ + ist eine automatische Speicherbereinigung, welche nicht mehr benötigten + Speicherplatz wieder freigibt. Die Bereinigung kann dabei in determinierten + Zeitintervallen erfolgen oder durch bestimmte Ereignisse ausgelöst + werden} +} + +\newglossaryentry{salt-hash} +{ + name=Salting und Hashing, + description={ + ist eine Methode um Passwörter so zu kodieren, dass sie nicht als + Klartext gespeichert werden und auch sicher vor Hash-Wörterbüchern sind. + Dafür wird dem Passwort ein bekanntes Wort, der Salt, angefügt, bevor + aus dem kompletten Wort eine Prüfsumme, ein Hash, generiert wird. Beim + Anmelden wird die Prüfsumme der Anmeldung mit der bekannten + Prüfsumme des Passworts verglichen} +} + +\newglossaryentry{rss} +{ + name=RSS, + description={ + (Really Simple Syndication) zeigt strukturiert Listen von Nachrichten + an. Die Änderungen werden im XML-Format in sogenannte RSS-Dateien + geschrieben, welche über einen Link abgerufen werden können} +} + +\newglossaryentry{dsgvo} +{ + name=Datenschutz-Grundverordnung, + description={ + (DSGVO) ist eine im europäischen Wirtschaftsraum + geltende Verordnung. Sie sorgt für eine Reglementierung bei der + Verarbeitung personenbezogener Daten. Unter anderem muss einsehbar sein, + welche Daten von Nutzern erhoben werden. Außerdem muss für einen Nutzer + die Möglichkeit bestehen, seine erhobenen Daten abrufen zu können} +} + +\newglossaryentry{push-pull} +{ + name=Push und Pull, + description={ + sind Methoden, um Daten auszutauschen. Bei der Pull-Methode + stellt Akteur A einem Akteur B eine Anfrage auf Daten und erhält diese + als Antwort. Damit Akteur A und B immer auf dem selben Stand sind, muss + Akteur A chronisch Anfragen an Akteur B stellen. Im Gegensatz dazu steht + die Push-Methode, bei der Akteur B den Akteuren mitteilt, dass er neue + Änderungen hat. Dafür muss Akteur B allerdings wissen mit welchen + anderen Akteuren er in Verbindung steht und diese Verbindung aufrecht + erhalten} +} + +\newglossaryentry{ui-lib} +{ + name=UI-Bibliothek, + plural=UI-Bibliotheken, + description={ + kümmert sich um das Layout einer Webseite. Dabei unterscheidet man + zwischen Design-Bibliotheken (wie Bootstrap), welche fertige + UI-Komponenten bereitstellen, und Layout-Bibliotheken (wie Vue oder + React.js), welche die Komponenten basierend auf Daten dynamisch + anzeigen} +} + +\newglossaryentry{responsive} +{ + name=Responsive, + description={ + Design ist ein Design-Prinzip für Webseiten, bei dem die selbe Webseite ihre + Komponenten dynamisch der Bildschirmbreite anpasst} +} + +\newglossaryentry{pseudoprotocol} +{ + name=Pseudoprotokoll, + description={ + ist ein URL-Schema, auf das Webseiten hören können, wenn sie sich das + URL-Schema im Browser anmelden. Bekannt Pseudoprotokolle sind: + ,,mailto:'', ,,tel:'' oder ,,irc:''} +} + +\newglossaryentry{dashboard} +{ + name=Dashboard, + description={ + ist die erste Seite auf der man landet, wenn man angemeldet ist} +} + +\newglossaryentry{abo} +{ + name=Abonnement, + description={ + ist ein abonnierter Podcast} +} + +\newglossaryentry{discovery} +{ + name=Discovery, + description={ + ist ein Feature der Gpodder-API, welches dem Nutzer eine Reihe von + Podcasts zum abonnieren anbietet} +} + +\newglossaryentry{session-token} +{ + name=Session-Token, + description={ + ist ein Wort, dass vom Client gespeichert wird solange der Nutzer + eingeloggt ist und bei jeder Anfrage an den Server mitgeschickt wird. + Der Server kann den Session-Token einem Nutzer zuordnen und so mit + nutzerspezifischen Daten antworten} +} + +\newglossaryentry{cookie} +{ + name=Cookie, + description={ + ist ein kleiner webseitenspezifischer Speicher im Browser, welcher vom + Server und von der Webseite gesetzt werden kann und bei jeder weiteren + Anfrage an den Server mitgesendet wird. Cookies bleiben entweder + temporär im Browserspeicher, bis der Browser geschlossen wird oder + permanent, bis ein optionales Verfallsdatum erreicht ist} +} + +\newglossaryentry{uiComponent} +{ + name=UI-Komponente, + plural=UI-Komponenten, + description={ + In Vue.js werden die grafischen Elemente einer Webseite in einzelne + Komponenten zerteilt. + Diese reagieren automatisch auf Änderungen und können ohne Neuladen + der Seite ihr Aussehen verändern und somit Änderungen direkt anzeigen} +} + +\newglossaryentry{spring} +{ + name=Spring, + description={ + Ein Java-Framework, welches die Entwicklung von Web-Applikationen erleichtert. + Dazu wird eine Reihe von Werkzeugsets zur Verfügung gestellt. + Unter anderem sind das Spring Web für das Erstellen von Webanwendungen, + Spring Security für die Verwaltung von Benutzerauthentifizierungen und + Spring Data JPA für die Arbeit mit relationalen Datenbanken + } +} + +\newglossaryentry{api} +{ + name=API, + plural=APIs, + description={ + Eine Schnittstelle, welche es ermöglicht auf Funktionalitäten einer Anwendung + zuzugreifen. APIs für Webanwendungen heißen WebAPIs. + Ein Beispiel für eine WebAPI ist die REST-API + } +} + +\newglossaryentry{business} +{ + name=Geschäftslogik, + description={ + Eine Schicht in der Anwendungsentwicklung, in der die Art und Weise, wie das + Programm auf Eingaben reagiert, wie Daten verarbeitet und wie sie gespeichert + werden sollen, festgelegt ist + } +} + +\newglossaryentry{solid} +{ + name=SOLID, + description={ + Eine Sammlung an Prinzipien, welche zu gutem objektorientierten Design führen soll. + Jedes Prinzip steht für einen Buchstaben in SOLID: + \textbf{S}ingle-Responsibility Prinzip, + \textbf{O}pen-Closed Prinzip, + \textbf{L}iskovsches Substitutionsprinzip, + \textbf{I}nterface Segregation Prinzip und + \textbf{D}ependency Inversion Prinzip + } +} + +\newglossaryentry{crud} +{ + name=CRUD, + description={ + CRUD steht für \textbf{C}reate, \textbf{R}ead, \textbf{U}pdate und \textbf{D}elete. + Hierbei handelt es sich um die grundlegenden Funktionen einer Anwendung, + die mit einer Datenbank arbeitet. + Hierbei können Daten angelegt, abgerufen, aktualisiert und gelöscht werden. + Auch in Web-Applikationen ist CRUD mit HTTP über die Anfragen POST, GET, PUT und DELETE + vertreten + } +} + +\newglossaryentry{SQL} +{ + name=SQL, + description={ + SQL (Structured Query Language) ist eine Sprache, die einen strukturierten Zugriff auf Datenbanken ermöglicht. + Daten können hierbei hinzugefügt, abgefragt, geändert und gelöscht werden. + Das besondere hierbei ist der strukturierte Zugriff auf Daten, indem explizit Daten mit bestimmten Kriterien und + Relationen ausgewählt und bearbeitet werden können. + SQL wird fast von allen verbreiteten Datenbanksystemen unterstützt + } +} + +\newglossaryentry{Base64} +{ + name=Base64, + description={ + Mithilfe von Base64 können 8-Bit-Binärdaten in eine ASCII-Zeichenkette + kodiert werden. So werden zum Beispiel E-Mail-Anhänge versendet + } +} + +\newglossaryentry{JSONP} +{ + name=JSONP, + description={ + JSONP ermöglicht die Übertragung von JSON-Daten zwischen verschiedenen Domains. + Dies wäre durch die Same-Origin-Policy nicht möglich. + JSONP nutzt allerdings die Tatsache aus, + dass sich Skripte domainübergreifend übertragen lassen. + Dazu werden die JSON-Daten als Argument einer übergebenen Funktion über + ein Skript-Element eingebunden + } +} diff --git a/10-entwurfsheft/sections/structure.tex b/10-entwurfsheft/sections/structure.tex new file mode 100644 index 0000000..2a6ecb3 --- /dev/null +++ b/10-entwurfsheft/sections/structure.tex @@ -0,0 +1,104 @@ +\section{Entwurfsmuster und Techniken} + +\subsection{Entwurfsmuster} + +\subsubsection{Dependency Injection} + +Die Dependency Injection (dt. Abhängigkeitsinjektion) ist ein Entwurfsmuster, welches die Abhängigkeiten von Objekten bestimmt und an einem zentralen Ort speichert sowie verwaltet. +Sollte ein Objekt also von einem anderen Objekt abhängig sein, so wird an diesem zentralen Ort nach der Abhängigkeit gesucht. +Ist die Abhängigkeit vorhanden, so wird dieses Objekt dann an dem benötigten Ort eingesetzt (injiziert). +Dies geschieht während der Laufzeit. +Der zentrale Ort, an dem die Abhängigkeiten gespeichert werden, wird meist von einem Framework verwaltet. + +Im Falle dieses Projekts ist \Gls{spring} das Framework und der \Gls{spring} Container der zentrale Ort, an dem die Abhängigkeiten gespeichert werden. +Der Vorteil dieses Entwurfsmusters ist, dass Objekte von anderen Objekten abgekoppelt werden, sprich: Das Objekt mit der Abhängigkeit muss nicht mehr von der expliziten Klasse Kenntnis haben und es kann nur mit Interfaces gearbeitet werden, was in den \Gls{solid}-Kriterien das D für Dependency Inversion erfüllt. +Ein weiterer Vorteil ist, dass die Abhängigkeiten innerhalb einer Konfigurationsdatei definiert werden können. +Sprich: Man kann mehrere Implementierungen besitzen, die alle das gleiche Interface implementieren und kann in der Konfigurationsdatei angeben, welche Implementierung gewählt werden soll. + +\subsubsection{Data Access Object (DAO)} +\label{DAO_Pattern} + +Das Data Access Object (kurz: DAO, dt: Datenzugriffsobjekt) ist ein Entwurfsmuster, das eingesetzt wird um den Zugriff auf \Gls{db}en zu vereinfachen und die \Gls{business} von der Datenzugriffslogik zu trennen. +Dazu gibt es zwei Komponenten: das DAO-Interface und die DAO-Implementierung. + +Das DAO-Interface wird von allen DAO-Implementierungen implementiert und bietet alle Datenzugriffsfunktionen an, auf die die \Gls{business} zugreift. +Die DAO-Implementierung ist eine Klasse, die das DAO-Interface implementiert und den tatsächlichen Zugriff auf die \Gls{db} ausführt. + +Der Vorteil dieses Entwurfsmusters ist es mehrere Implementierungen desselben DAO-Interfaces zu besitzen. +In Kombination mit der Dependency Injection ist es einfach zwischen den Implementierungen für verschiedene \Gls{db}en (bspw. MariaDB und My\Gls{SQL}) zu wechseln. +Damit wird der Datenzugriff flexibler. +Im Falle dieses Projekts wird eine DAO-Implementierung für MariaDB verwendet. + +Ein weiterer Vorteil ist die zuvor angesprochene Trennung der Geschäfts- und Datenzugriffslogik. +Da sich die \Gls{business} und Datenzugriffslogik mithilfe dieses Musters in verschiedenen Komponenten befinden, sind diese voneinander getrennt und es wird einfacher die jeweiligen Implementierungen zu testen. +Gleichzeitig verbessert sich damit die Wiederverwendbarkeit des Codes, da die DAO-Implementierungen in anderen Programmen, die mit demselben DAO-Interface arbeiten, eingesetzt werden können. + +Damit erfüllt das DAO-Muster die Kriterien S und O der \Gls{solid}-Kriterien. +Das Single-Responsibility Prinzip wird erfüllt, da der Zugriff auf die \Gls{db} von der \Gls{business} getrennt wird und damit die DAO-Implementierung alleine für den Zugriff auf die \Gls{db} verantwortlich ist. +Das Open/Closed Prinzip wird erfüllt, da die DAO-Implementierung erweitert werden kann, ohne dass der Rest vom Projekt betroffen wird und außerhalb der Klasse nur mit dem DAO-Interface gearbeitet werden kann. + +\subsection{Techniken} + +\subsubsection{JSON Web Token} + +\Gls{json} Web Token (JWT) ist ein offener Standard der in RFC 7519 definiert wird. +Mit einem JWT ist es möglich Informationen sicher in einem kodierten \Gls{json} Objekt zu übertragen. +Die Sicherheit der Daten wird dabei durch eine digitale Signatur gewährleistet. + +Ein JWT besteht aus drei durch Punkte ('.') voneinander getrennten Teilen: +\texttt{Header}, \texttt{Payload} und \texttt{Signatur}. +Der \texttt{Header} besteht dabei typischerweise aus der Information um welchen Typ von Token es sich handelt, also einen JWT, +und der Information welcher Signierungs-Algorithmus verwendet wird. +Diese Informationen werden \Gls{Base64} kodiert und bilden den ersten Teil des JWT. + +Im \texttt{Payload} Teil werden die eigentlichen Informationen \Gls{Base64} kodiert. + +Die \texttt{Signatur} ergibt sich durch die mit einem Punkt voneinander getrennten Kodierungen des \texttt{Headers} und +des \texttt{Payload}-Teils. Diese Zeichenkette wird dann mit einem geheimen Schlüssel +durch den im \texttt{Header} angegebenen Signierungs-Algorithmus signiert. +JWT werden einmalig vom Server erzeugt und beim Client gehalten. Daher ist es +nicht notwendig wie z.B. bei \gls{cookie} basierten Sessions, eine Liste mit gültigen Sessions auf dem Server zu verwalten, was bei mehreren Servern schwierig ist. + +In diesem Projekt werden JWT zur Verifikation der E-Mail-Adresse eines Benutzers und zur Überprüfung der Autorisation bei Anfragen an den Server verwendet. + +Zur Bestätigung der E-Mail-Adresse speichert der Server die zur Verifikation des +Benutzers benötigten Daten in einem JWT. Dieser wird in der URL des Verifikations-Links kodiert. Wenn der Benutzer den Verifikations-Link anklickt, +wird der JWT an den Server weitergeleitet. Dieser überprüft die Signatur des JWT ihn mit seinem geheimen Schlüssel +und kann so die Verifikation der E-Mail-Adresse abschließen. + +Bei dem Login-Vorgang sendet der Client zuerst seine Anmeldedaten (Benutername und Passwort), um sich zu authentifizieren. +Der Server überprüft die Angaben, generiert einen JWT und gibt diesen zurück, falls die Daten korrekt sind. +Bei späteren Anfragen an den Server übermittelt der Client diesen JWT. Der Server überprüft die Validität des JWT und trifft +basierend darauf die Entscheidung, ob die Anfrage bearbeitet oder abgelehnt wird. + +\subsubsection{Objektrelationale Abbildung (Object-relational mapping)}\label{t:orm} + +Objektrelationale Abbildung - kurz ORM von der englischen Bezeichnung \enquote{Object-relational mapping} - ist eine Technik der Softwareentwicklung. +Sie widmet sich dem, mit der persistenten Speicherung von Laufzeit-Objekten zusammenhängenden \enquote{Impedance mismatch} Problem. +Dieses beschreibt die Diskrepanz zwischen den in der Pogrammier- beziehungsweise \Gls{db}welt vorherrschenden Konzepten - nämlich der objektorientierten Programmierung und relationalen \Gls{db}en. +So speichern objektorientierte Programmiersprachen Daten und Verhalten in Objekten mit eindeutiger Identität, wobei Zustand und Verhalten hinter einer Schnittstelle verborgen werden. +Relationale \Gls{db}en hingegen speichern Daten in Tabellen und basieren auf dem mathematischen Konzept der Relationenalgebra. + +Objektrelationale Abbildung bietet eine Möglichkeit diese Diskrepanz zu vermindern, indem sie ein Mapping zwischen Objekten und Datenstrukturen relationaler \Gls{db}en herstellt. +Einem, in einer objektorientierten Programmiersprache geschriebenen, Anwendungsprogramm erscheint dann die verbundene relationale \Gls{db} als objektorientierte \Gls{db}. +Durch ORM wird also sowohl das Ablegen von Objekten mit Attributen und Methoden in relationale \Gls{db}en, als auch das Erzeugen von solchen Objekten aus entsprechenden Datensätzen ermöglicht. +Vorteilhaft ist daran außerdem, dass die objektorientierte Programmiersprache nicht erweitert werden muss. +Des Weiteren existiert für jede Umgebung ausgereifte Software für die Verwendung relationaler \Gls{db}en. +Allerdings ist der Schritt in Richtung objektorientiertem Ansatz immanent ein Schritt weg von den eigentlichen Stärken relationaler \Gls{db}en. + +Der grundlegende Ansatz ist die Abbildung von Klassen auf Tabellen, wobei die Spalten den Attributen und die Zeilen den Objekten der Klasse zugeordnet sind. +Dabei entspricht der Primärschlüssel der Tabelle der Objektidentität und Objektreferenzen werden mithilfe von zusätzlichen Fremdschlüsseln repräsentiert. +%Um Vererbung abzubilden gibt es drei grundlegende Möglichkeiten. +%Erst einmal kann für eine Vererbungshierarchie auch genau eine gemeinsame Tabelle mit allen Attributen verwendet werden, in der ein Diskriminator bestimmt, zu welcher Klasse ein Objekt gehört. +%Als zweite Option kann pro Unterklasse eine zusätzliche verknüpfte Tabelle eingeführt werden. +%Letztlich kann auch pro konkreter Klasse eine Tabelle verwendet werden, wobei die Tabelle für die abstrakte Basisklasse entfällt. + +Die von diesem Mapping betroffenen Klassen aus dem Model-Paket (\ref{p:model}) des Backends sind User, SubscriptionAction, Subscription, EpisodeAction und \Gls{episode}. + +Konkret für dieses Projekt findet ORM als Technik durch die Implementierung der Jakarta Persistence \Gls{api} (JPA) Anwendung. +Dafür wird das von \Gls{spring} zur Implementierung von JPA-basierten Datenzugriffsschichten bereitgestellte Modul \Gls{spring} Data JPA genutzt. +Als JPA-Implementation wiederum wird das Open-Source-Persistenz- und ORM-Framework Hibernate für \Gls{java} verwendet. +Dabei erfolgen Abfragen der persistierten Objekte über die Abfragesprache Jakarta Persistence Query Language (JPQL), welche dann mittels JDBC in den entsprechen \Gls{SQL}-Dialekt für MariaDB übersetzt. +%Hier sei angemerkt, dass JPQL eine Untermenge der Hibernate Query Language (HQL) ist. + +\newpage -- cgit v1.2.3