.. index:: projekt [1], docker-chat [1], chat [1], docker [1]
Chatserver in Docker Compose
============================
.. index:: einleitung [1]
Einleitung
----------
In diesem Projekt realisieren wir einen vernetzten Docker-Chat, bei dem Nachrichten in einem gemeinsamen Chat-Raum
über eine REST-Schnittstelle ausgetauscht und in einer Datenbank abgespeichert werden. Dabei greifen wir auf Docker,
Java und PostgreSQL zurück, um eine robuste und flexible Architektur zu gewährleisten. Der Fokus unserer Untersuchungen
liegt dabei besonders auf Docker Compose, welches den Entwicklungsprozess von Full-Stack-Anwendungen enorm vereinfachen
soll. Das Ziel ist es, die Teammitglieder anhand eines praktischen Beispiels mit Docker Compose und z. B. den
internen Netzwerken vertraut zu machen. Nachdem wir uns in kurzen Meetings über die zentralen Anforderungen und die grobe
Projektstruktur abgestimmt hatten, wurden die Aufgabenbereiche im Team aufgeteilt. Durch das wöchentliche Zusammenkommen
lassen sich Fortschritte schnell überprüfen und entstandene Probleme zeitnah beheben.
.. index:: demo [1]
Demo
----
.. raw:: html
.. index:: grundlagen [1]
Grundlagen
----------
.. index:: docker-compose [1]
Docker Compose
``````````````
`[Docker Compose] `_ ist ein Open-Source-Werkzeug, das die Orchestrierung von
Multi-Container-Anwendungen in Docker vereinfacht.
Es ermöglicht das Definieren und Verwalten von Containern über eine YAML-Datei (``compose.yml``).
Dadurch können Entwickler verschiedene Dienste einer Anwendung (z. B. Datenbank, Backend und Frontend) mit einem
einzigen Befehl starten bzw. stoppen.
Docker Compose nutzt die Containerisierungstechnologie von Docker und ermöglicht somit die Nutzung von vordefinierten
Images aus Docker Hub oder eigens erstellten Docker-Images sowie die Verwendung von Volumes zur Datenpersistenz und
Umgebungsvariablen. Docker Compose bietet darüber hinaus beispielsweise eine erweiterte Netzwerkkonfiguration an.
Vorteile von Docker Compose
...........................
- **Einfache Verwaltung**: Mehrere Container lassen sich zusammen mit einem einzigen Befehl starten.
- **Portabilität**: Anwendungen können leicht auf verschiedenen Systemen ausgeführt werden.
- **Isolierte Umgebungen**: Jeder Dienst läuft in einem eigenen Container, wodurch Konflikte zwischen Abhängigkeiten
vermieden werden.
- **Einfache Konnektivität**: Es können mehrere, falls nötig getrennte, Netzwerke angelegt werden. Innerhalb eines
Netzwerks ist eine gegenseitige Kommunikation mit den ContainerNamen als Hostnames möglich.
Starten einer `compose.yml` Datei
.................................
.. code-block:: sh
docker compose up -d --build
Dieser Befehl startet alle Container. Durch die Option ``-d`` wird die Compose-Datei im "detached mode", also losgelöst
von der aktuellen Terminal-Session im Hintergrund ausgeführt. Mit ``--build`` wird das neu bauen eines eigenen lokalen
Container-Images erzwungen. Um die laufenden Container zu überprüfen, kann ``docker compose ps`` ausgeführt werden.
.. index:: rest-api [1]
REST-API
````````
Eine REST-API (Representational State Transfer Application Programming Interface) ermöglicht die standardisierte
Kommunikation zwischen Client und Server unter Nutzung von HTTP-Methoden wie GET, POST, PUT und DELETE [RESTDocs]_.
In unserem Docker-Chat-Projekt dient die REST-API als Vermittler: Chatnachrichten werden empfangen, gespeichert und bei
Bedarf wieder an den Client ausgeliefert. Dank der losen Kopplung zwischen Frontend und Backend kann das System bei
steigenden Anforderungen skaliert und um neue Funktionen erweitert werden, ohne größere Anpassungen an der Gesamtlösung
vornehmen zu müssen.
.. index:: postgresql [1], datenbank [1], objektrelational [1], objektorientiert [1], relational [1]
PostgreSQL
``````````
PostgreSQL ist ein objektrelationales Datenbanksystem [PSQL]_.
Eine objektrelationale Datenbank (ORDB) kombiniert relationale und objektorientierte Datenbanken.
Das ermöglicht es, komplexe Strukturen darzustellen und miteinander in Verbindung zu setzen.
Außerdem sind dadurch einige Funktionen wie z. B. Vererbung und Polymorphie möglich. Sie werden in Form von Tabellen
mit Spalten, Beziehungen und Methoden organisiert [StudySmarter]_.
Weiterhin ist PostgreSQL Open Source und bekannt für Zuverlässigkeit, Robustheit und Leistungsfähigkeit.
Da wir bereits mit PostgreSQL gearbeitet haben und weitere Erfahrungen damit sammeln möchten, haben wir uns für diese
Datenbank-Software entschieden.
.. index:: spring-boot [1]
Spring Boot
```````````
Spring Boot ist ein Framework, das die Entwicklung von Java-Anwendungen erheblich vereinfacht, indem es auf
Konventionen statt Konfiguration setzt und wesentliche Komponenten vorkonfiguriert bereitstellt [SpringDocs]_.
Im Docker-Chat-Projekt übernimmt Spring Boot das Routing der REST-Endpunkte und regelt das Zusammenspiel mit der
Datenbank. Die klare Trennung von Controller-, Service- und Modellklassen erleichtert sowohl das Testen als auch das
Wiederverwenden von Code. Darüber hinaus unterstützt Spring Boot eine unkomplizierte Integration von Datenbanktreibern
und weiteren Bibliotheken, womit eine schnelle Inbetriebnahme möglich ist.
.. index:: react [1], typescript [1], web [1]
React
`````
Der Fokus des Projekts lag nicht darauf, eine möglichst umfangreiche und ausgefallene Benutzerschnittstelle zu
designen, sondern vor allem auf der Idee des Chats verschiedener Clients, die alle in Docker laufen. Trotzdem war es
selbstverständlich der Anspruch, ein schönes, simples, aber modernes Web-Frontend zu entwickeln, das einen einfachen
und benutzerfreundlichen Zugang der einzelnen Clients zum Chat-Raum bietet. Deswegen wurde sich für React in
Kombination mit TypeScript entschieden. React ist eine JavaScript-/TypeScript-Bibliothek, entwickelt von Meta.
Die Entscheidung fiel auf React, weil sich damit schnell und unkompliziert moderne, benutzerfreundliche Oberflächen
bauen lassen. Neue Nachrichten können dank des integrierten State-Managements direkt angezeigt werden, ohne die Seite
neu zu laden. Mit JSX lässt sich das Layout einfach in Code schreiben, was die Entwicklung zusätzlich
beschleunigt [React]_.
.. index:: vorgehensweise [1]
Vorgehensweise
--------------
.. index:: vorbereitung [1]
Vorbereitung & Erste Schritte
`````````````````````````````
Um einen reibungslosen Ablauf bei der Erstellung, Bearbeitung und Finalisierung der Projekte zu
ermöglichen, haben wir uns eigenständig in die vorbereitenden Maßnahmen eingelesen, danach ein Meeting abgehalten, die
Aufgaben verteilt und alles vorbereitet, damit es gleich mit dem ersten Projekt losgehen kann. Für den Überblick haben
wir im Wiki des Repositories einen Meeting-Ordner_ erstellt. Dieser hilft uns dabei, eine Übersicht über die Aufgaben
zu erhalten, Einigungen festzuhalten und abschließend die Aufgaben zu verteilen. Dadurch können wir organisiert und
effizient arbeiten und schnelle Entscheidungen treffen. Aus dem gleichen Grund treffen wir uns zusätzlich zur Abgabe
einmal die Woche, um den aktuellen Stand zu besprechen und offene Themen gemeinsam zu finalisieren.
Dazu fallen u.a. das Mergen in den Main-Branch, Einigen auf Lösungen und Festsetzen der weiteren Aufgaben.
.. _Meeting-Ordner: https://gitlab.informatik.hs-augsburg.de/dva/berichte-2025/53/-/wikis/Meetings
.. index:: website [1], bericht [1]
Bericht auf die Website laden
`````````````````````````````
Als Teil der Vorbereitung wurde der Bericht auf unsere Gruppe angepasst und anschließend auf die
Benutzer-Hochschulwebsite_ kopiert. Zu Beginn kamen einige Schwierigkeiten auf. Hierzu zählte die Erzeugung eines
SSH-Schlüssels, die Erstellung und Verlinkung auf der THA-Website und das anschließende Kopieren der HTML-Datei.
Anfangs versuchten wir, einen synchronisierten Link zur HTML mittels rsync zu erzeugen. Dies hat nach mehreren Versuchen
jedoch leider nicht geklappt, sodass wir die Dateien anschließend manuell kopiert haben. Der SSH-Schlüssel musste neu
erstellt werden und ermöglichte anschließend den Zugriff auf die THA-Website und die Verlinkung mit dem Projekt mittels
IDE.
.. _Benutzer-Hochschulwebsite: https://www.hs-augsburg.de/homes/adrian93/DVAPRAK25/Bericht53/sphinx.html
.. index:: docker-compose-setup [1], docker-setup [1]
Docker Compose
`````````````````````````````
Die erste Arbeit des Projekts bestand darin, eine Dummy-Compose-Datei zu erstellen. Mit ihr wurde auch gleich eine
Dateistruktur angelegt, die den Tech-Stack abbildet und sowohl die Entwicklung der Software als auch die Teamarbeit
erleichtern sollte. Die vorläufige Version der Compose-Datei definierte zwar die Komponenten der Anwendung und auch
deren Ordner-Pfade und Netzwerkanbindungen zueinander, enthielt aber noch keine tatsächlich ausführbare Software.
Nachdem sowohl das Datenbank-Team als auch das Backend-Team ihre Software-Basis final entschieden hatten, wurden passende
Images von Docker Hub ausgewählt. Für das Backend musste sogar ein eigenes Dockerfile, bestehend aus zwei Stages,
angelegt werden, um die Spring-Boot-Anwendung vor dem Ausführen auch zu kompilieren und dadurch keine weitere
Nutzerinteraktion zum Updaten der Anwendung notwendig zu machen. Eine angepasste Dockerfile wurde aus ähnlichen
Gründen später auch für den Webserver notwendig.
Zum Schluss bemerkten wir einen Fehler in unserer Software-Architektur, welcher unser Ziel, Netzwerke im Docker
Compose zu untersuchen, schwieriger zu erreichen machte. Wir hatten zuvor von dem Konzept "Serverseitiges JavaScript"
bzw. "Server Side Rendering" gehört und dachten, wir könnten unser Frontend auf drei Webservern im Compose laufen
lassen, mit einem Browser auf die drei unterschiedlichen Ports zugreifen und die HTTP-Requests an das Backend werden
dann von diesen Servern geschickt. Dies wäre in der Realität wohl irgendwie möglich gewesen, hätte aber bedeutet, dass
der Server natürlich nicht ohne eine entsprechende Programmierung, z. B. Button-Click-Events vom Browser, erhalten hätte.
Da dies jedoch die Komplexität zu sehr erhöht hätte, haben wir uns dazu entschieden, dem Browser die statischen
Website-Dateien ganz klassisch mit einem Webserver anzubieten und die API-Calls vom Browser dafür an einen Proxy-Server
zu senden, welcher diese an den außerhalb des Compose nicht erreichbaren Backend-Server weiterleitet. Dadurch war es
uns dennoch möglich, Docker-Compose-Netzwerke möglichst anschaulich zu demonstrieren.
.. index:: datenbank-setup [1], spring-boot-setup [1]
Datenbank und Java Spring Boot
``````````````````````````````
Für Java Spring Boot begannen wir mit einer kurzen Einarbeitung in die Framework-Struktur und die benötigten
Abhängigkeiten. Anschließend setzten wir ein minimales Setup auf und ermöglichten den Server vorerst ohne
Datenbankanbindung mit rein lokaler Speicherung zu verwenden. Dies wurde dann von anderen Teammitgliedern durch die
tatsächliche Datenbankanbindung ersetzt.
Nach der Aufteilung der Teams hat jedes Subteam mit der Bearbeitung seiner Teilaufgaben begonnen.
Unsere Aufgabe war es, die PostgreSQL-Datenbank mit Java Spring Boot aufzusetzen. Da wir bisher noch nicht damit
gearbeitet hatten, recherchierten wir die Funktionsweise und die möglichen Implementierungen für unser Szenario.
Die Dokumentation erwies sich dabei als sehr hilfreiche Informationsquelle [SpringBoot]_. Nachdem wir einen Überblick
erhalten haben, implementierten wir das erste Setup. Da an anderer Stelle bereits der Backend-Server aufgesetzt wurde,
fügten wir das Skript an die passende Stelle an. Zu Beginn traten Fehlermeldungen aufgrund der Projektstruktur und der
Nutzung des falschen JDK auf. Diese lösten wir Schritt für Schritt, bis die Datenbank stand und bereit war für das Speichern
und Zurückgeben von Nachrichten.
Nach der Recherche, wie man persistente Speicherung in Java, insbesondere Spring Boot, umsetzt,
wurde der MessageService so umgebaut, dass er nicht mehr ausschließlich Nachrichten in einer internen Liste speichert,
sondern nun persistent in einer PostgreSQL-Datenbank ablegt [SpringBootSQL]_.
Die ursprüngliche Message-Klasse wurde dafür in ein JPA-Entity umgewandelt,
indem sie mit der @Entity-Annotation versehen wurde. Außerdem wurden einige Felder geändert,
es wurden für das MVP nicht nötige Felder entfernt. Anschließend wurde ein MessageRepository implementiert,
das von JpaRepository erbt und standardisierte CRUD-Operationen, abstrahiert als Java-Funktionen, bereitstellt.
Im überarbeiteten MessageService wird nun das Repository verwendet, um alle Operationen,
also in diesem Fall das Anlegen und Abrufen von Message-Objekten, direkt in der Datenbank durchzuführen.
.. index:: frontend [1], vite [1], css [1]
Frontend
````````
Nachdem die Anforderungen geklärt, sich auf eine webbasierte Benutzeroberfläche geeinigt und schließlich React als zur
Umsetzung einzusetzende Technologie ausgewählt worden war (siehe Abschnitt Grundlagen/React), ging es daran, möglichst
schnell und unkompliziert ein React-Projekt aufzusetzen. Dafür wurde Vite verwendet, da es eine besonders einfache und
schnelle Möglichkeit bietet, moderne React-Projekte zu starten [Vite]_. Nach dem einfachen und schnellen Setup wurde
die Grundstruktur der Komponenten und ein Service zur Kommunikation mit der API angelegt. Es wurde eine
Chat-App-Komponente erstellt, welche sowohl die Texteingaben und Buttons als auch die separate ChatRoom-Komponente
rendert. Kleine Schwierigkeiten bereitete zunächst nur die API-Anfrage, welche einen CORS-Fehler zurückgab. Dieser
konnte jedoch schnell im Backend behoben werden. Der Benutzeroberfläche wurde durch CSS ein modernes, verständliches
Design gegeben sowie einige kleine Verschönerungen oder Features (z. B. Senden durch das Drücken der Enter-Taste oder
passende Icons für das Favicon und den Sendeknopf).
.. index:: fazit [1], ausblick [1]
Fazit & Ausblick
----------------
Durch das Projekt konnten wir neue interessante Technologien entdecken und anwenden. Hervorzuheben sind die Verwendung von
Java Spring Boot in der Webentwicklung, die vielzähligen Möglichkeiten von Docker und Docker Compose, sowie die Nutzungsmöglichkeiten
von React und PostgreSQL. Das ermöglicht uns, ein breiteres Spektrum an Wissen aufzubauen, um für zukünftige Problemfälle schnell Lösungen bereitstellen zu können.
Auch wenn die Funktionen eher minimal ausfallen, gibt uns das Wissen über diese Technologien einen entscheidenden Vorteil.
Die simple Implementierung und die Vielfältigkeit von Spring Boot waren für einige Teammitglieder neu und ermöglichen es
in der Zukunft allen, eine Datenbank für Webanwendungen simpler und effizienter aufzusetzen.
Nach dem anfänglichen Aufwand der Einarbeitung erweist sich die Implementierung einer persistenten Speicherung in
Spring Boot mit einer PostgreSQL-Datenbank als unkompliziert. Die Verwendung von JPA abstrahiert die zugrunde liegenden
Datenbankoperationen erheblich und trägt damit wesentlich zur Vereinfachung der Entwicklungsarbeit bei. Gleichzeitig
besteht jedoch die Gefahr, dass Entwickler die Funktionsweise der darunterliegenden Systeme nicht mehr vollständig
nachvollziehen, was potenziell zu Fehlerquellen führen kann.
.. index:: literaturverzeichnis [1]
Literaturverzeichnis
--------------------
.. [PSQL] PostgreSQL (2025) *PostgreSQL: The World's Most Advanced Open Source Relational Database*.
Verfügbar unter: (Zugriff am: 1. April 2025)
.. [StudySmarter] StudySmarter (2025) *Objektrelationale Datenbanken*.
Verfügbar unter:
(Zugriff am: 1. April 2025).
.. [SpringBoot] Spring (2025) *How-to: Data Initialization in Spring Boot*.
Verfügbar unter:
(Zugriff am: 1. April 2025).
.. [SpringBootSQL] Spring (2025) *Reference: SQL Databases*.
Verfügbar unter:
(Zugriff am: 1. April 2025).
.. [SpringDocs] Spring (2025) *Spring Boot Reference Guide*.
Verfügbar unter:
(Zugriff am: 1. April 2025).
.. [RESTDocs] REST (2025) *Representational State Transfer*.
Verfügbar unter:
(Zugriff am: 1. April 2025).
.. [React] Meta (2025) *React - The library for web and native user interfaces*.
Verfügbar unter:
(Zugriff am: 1. April 2025).
.. [Vite] Vite (2025) *Getting Started*
Verfügbar unter:
(Zugriff am: 1. April 2025).