1. Chatserver in Docker Compose¶
1.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.
1.2. Demo¶
1.3. Grundlagen¶
1.3.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.
1.3.1.1. 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.
1.3.1.2. Starten einer compose.yml Datei¶
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.
1.3.2. 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.
1.3.3. 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.
1.3.4. 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.
1.3.5. 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].
1.4. Vorgehensweise¶
1.4.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.
1.4.2. 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.
1.4.3. 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.
1.4.4. 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.
1.4.5. 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).
1.5. 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.
1.6. Literaturverzeichnis¶
PostgreSQL (2025) PostgreSQL: The World’s Most Advanced Open Source Relational Database. Verfügbar unter: <https://www.postgresql.org> (Zugriff am: 1. April 2025)
StudySmarter (2025) Objektrelationale Datenbanken. Verfügbar unter: <https://www.studysmarter.de/ausbildung/ausbildung-in-it/it-systemelektroniker/objektrelationale-datenbanken/> (Zugriff am: 1. April 2025).
Spring (2025) How-to: Data Initialization in Spring Boot. Verfügbar unter: <https://docs.spring.io/spring-boot/how-to/data-initialization.html> (Zugriff am: 1. April 2025).
Spring (2025) Reference: SQL Databases. Verfügbar unter: <https://docs.spring.io/spring-boot/reference/data/sql.html> (Zugriff am: 1. April 2025).
Spring (2025) Spring Boot Reference Guide. Verfügbar unter: <https://docs.spring.io/spring-boot/docs/current/reference/html/> (Zugriff am: 1. April 2025).
REST (2025) Representational State Transfer. Verfügbar unter: <https://restfulapi.net/> (Zugriff am: 1. April 2025).
Meta (2025) React - The library for web and native user interfaces. Verfügbar unter: <https://react.dev/> (Zugriff am: 1. April 2025).
Vite (2025) Getting Started Verfügbar unter: <https://vite.dev/guide/> (Zugriff am: 1. April 2025).