Die Welt hat sich ins Internet verlagert, und Webanwendungen sind zu den neuen Arbeitsplätzen und Handelsläden geworden. Um der Vielfalt an Zwecken gerecht zu werden, denen moderne Webanwendungen dienen, muss jede von ihnen für hohe Leistung und Anpassbarkeit ausgelegt sein.
Webanwendungsarchitekturen lösen dieses Problem.
Die Webanwendungsarchitektur legt fest, wie die verschiedenen Komponenten einer webbasierten Anwendung aufgebaut sind. Diese Architektur ist sehr spezifisch für die Art und den Zweck der Webanwendung. Wenn du die falsche Architektur für deine Webanwendung wählst, kann das verheerende Folgen für dein Unternehmen haben.
In diesem Leitfaden werden wir das Konzept der Webanwendungsarchitektur aufschlüsseln und verstehen, wie sie sich auf die Erfahrung des Endnutzers mit deiner Anwendung auswirkt. Am Ende werden wir uns auch einige der besten Praktiken ansehen, die du anwenden kannst, um das Beste aus deiner Webanwendung herauszuholen.
Was ist eine Webanwendungsarchitektur?
Um die Diskussion zu beginnen, wollen wir mit der Definition der Webanwendungsarchitektur beginnen.
Einfach ausgedrückt, ist die Architektur einer Webanwendung eine Übersicht darüber, wie die verschiedenen Komponenten deiner Webanwendung miteinander interagieren.
Sie kann so einfach sein wie die Definition der Beziehung zwischen dem Client und dem Server. Sie kann aber auch so komplex sein, dass sie die Beziehungen zwischen einem Schwarm von Container-Backend-Servern, Load Balancern, API-Gateways und benutzerorientierten Single-Page-Frontends definiert.
Dabei geht es selten um die Wahl der Programmiersprache, in der du deinen Code schreibst.
Die Art und Weise, wie du deine Webanwendung gestaltest, spielt sowohl für die Benutzerfreundlichkeit als auch für deine Kostenoptimierung eine wichtige Rolle. Hier siehst du ein Beispiel für die Architektur einer Web-App auf dem Papier:
Warum ist die Architektur von Webanwendungen wichtig?
Die Architektur einer Webanwendung ist zweifelsohne einer der wichtigsten Bestandteile deiner Webanwendung. Wenn du dich bei der Entwicklung deiner Webanwendung für eine bestimmte Architektur entscheidest, hast du viele Vorteile, wenn es um die Wartung und Weiterentwicklung deiner Anwendung geht.
Durch die Wahl der richtigen Architektur werden diese Vorteile jedoch noch verstärkt.
Hier sind einige der wichtigsten Gründe, warum du die Einführung einer Webanwendungsarchitektur ernsthaft in Betracht ziehen solltest.
Leichte Anpassung an geschäftliche Erfordernisse
Deine App ist ein wichtiges Tor zu deinem Unternehmen, und die Bedürfnisse deines Unternehmens entwickeln sich mit dem sich verändernden Markt. Wenn du mithalten willst, muss deine App flexibel genug sein, um sich an die sich ändernden Anforderungen deines Unternehmens anzupassen. Wenn du eine App entwickelst, ohne an die eingebaute Flexibilität zu denken, wirst du zwangsläufig immer mehr Zeit und Mühe aufwenden müssen, um kleine Anpassungen in deiner App vorzunehmen.
Die richtige Architektur einer Webanwendung berücksichtigt bereits einige der Änderungen, die dein Unternehmen in Zukunft brauchen könnte. Wenn du zum Beispiel weißt, dass du eine E-Commerce-Anwendung baust, die skalierbar ist und eines Tages eine Vielzahl von Diensten für eine große Anzahl von Kunden bereitstellen soll, bietet dir eine Microservices-Architektur mehr Flexibilität als eine monolithische.
Wenn du hingegen eine interne Anwendung für dein Unternehmen mit nur ein oder zwei festen Anforderungen entwickelst, kannst du dich für einen einfacheren Monolithen entscheiden, um die Entwicklung zu beschleunigen und deine Codebasis sauber zu halten.
Organisierte Entwicklung
Wie bereits erwähnt, bietet dir die richtige Web-App-Architektur einen bequemeren Fahrplan für die Entwicklung. Die Architektur sorgt für genügend Modularität in deinem System, um Komponenten bei Bedarf zu isolieren, und du hast die Freiheit, die richtige Projektstruktur für jedes deiner Module und Komponenten zu wählen.
Wenn du dich in die App-Entwicklung stürzt, ohne eine Architektur im Kopf zu haben, läufst du Gefahr, Zeit und Geld damit zu verschwenden, deine Komponenten neu zu organisieren und neue Regeln aufzustellen, um die Zusammenarbeit zwischen deinen Teammitgliedern zu erleichtern – Zeit und Geld, die du anderweitig hättest verwenden können.
Besseres Codebase-Management
Du schreibst nicht nur den Code deiner App, sondern verbringst auch viel Zeit damit, ihn zu verwalten. Das Organisieren deiner Projektdateien, das Aufteilen deiner App in Module und das Einrichten benutzerdefinierter Pipelines sind nur einige der Aufgaben, die für eine reibungslose Entwicklung aktiv gepflegt werden müssen.
Die richtige Web-App-Architektur macht es dir leicht, Änderungen vorzunehmen. Du kannst komponentenspezifische Best Practices implementieren, die Schwachstellen deiner App voneinander trennen und die einzelnen Funktionen unabhängig und lose gekoppelt halten. Es ist nicht so, dass diese Dinge nicht auch ohne Architektur möglich wären, aber die richtige Architektur macht das alles viel einfacher.
Wenn du dich an eine vordefinierte Architektur hältst, kannst du deine Anwendungen auch schneller entwickeln. Die richtige Architektur in Kombination mit einer soliden Versionskontrollstrategie ermöglicht es deinen Entwicklern, parallel zueinander zu arbeiten und Funktionen schneller zu entwickeln.
Eine Web-App-Architektur macht deine Anwendung auch zukunftssicher. Sobald du eine solide Strategie für die Organisation der Komponenten deiner Anwendung festgelegt hast, kannst du diese Komponenten problemlos nach und nach auf neuere Technologien umstellen, ohne deine gesamte Anwendung neu entwickeln zu müssen.
Verbesserte Sicherheit
Die meisten Web-App-Architekturen berücksichtigen bei der Strukturierung der Komponenten auch die Sicherheit. Entwickler/innen können im Voraus Maßnahmen und Praktiken planen, um die Sicherheit der App zu verbessern, bevor sie an die Nutzer/innen ausgeliefert wird.
Der Aufbau einer OTT-Video-Streaming-App, die sowohl kostenpflichtige als auch kostenlose Inhalte anbietet, ist zum Beispiel mit Microservices sinnvoller, da die Microservices-Architektur es dir ermöglicht, deine App in geschäftsfreundliche Komponenten aufzuteilen, wie z. B. die Benutzerauthentifizierung und das Streaming von kostenlosen oder kostenpflichtigen Inhalten. Wenn dein Benutzerauthentifizierungsmodul ausfällt, kannst du deine App so konfigurieren, dass der Zugriff auf das Modul für kostenpflichtige Inhalte eingeschränkt wird, bis die Benutzerauthentifizierung wieder funktioniert, während das Modul für kostenlose Inhalte für deine Benutzer weiterhin verfügbar ist.
In einem anderen Fall, in dem dieselbe Anwendung als eng gekoppelter Monolith konzipiert wurde, würde ein Ausfall des Authentifizierungsdienstes entweder einen Ausfall der Anwendung oder die kostenlose Bereitstellung von kostenpflichtigen Inhalten bedeuten – ein Ergebnis, das du um jeden Preis vermeiden willst.
Wie funktioniert die Webanwendungsarchitektur?
Bevor wir darüber sprechen, wie die Architektur von Webanwendungen funktioniert, ist es wichtig zu verstehen, wie eine einfache Website funktioniert:
- Der Nutzer gibt die URL deiner App in die Adressleiste des Browsers ein oder klickt auf einen Link.
- Der Browser sucht die URL in den DNS-Servern und identifiziert die IP-Adresse deiner App.
- Der Browser sendet eine HTTP-Anfrage an deine App.
- Deine App antwortet mit dem richtigen Inhalt (normalerweise eine Webseite).
- Der Browser rendert die Webseite auf dem Bildschirm.
Wenn du ein bisschen tiefer eintauchst, siehst du, wie eine Webanwendung eine Anfrage bearbeitet:
- Der Nutzer sendet eine Anfrage an deine App über deine Benutzeroberfläche im Frontend.
- Wenn du einen entsprechenden Cache eingerichtet hast, prüft die App zunächst, ob er einen gültigen Datensatz enthält, der direkt an den Kunden zurückgeschickt werden kann. Wenn ja, wird der gecachte Inhalt zurückgeschickt und die Anfrage wird als abgeschlossen markiert.
- Wenn kein Cache vorhanden ist, wird die Anfrage an den Load Balancer weitergeleitet.
- Der Load Balancer ermittelt eine Serverinstanz, die für die Bearbeitung der Anfrage verfügbar ist, und leitet sie weiter.
- Die Serverinstanz verarbeitet die Anfrage und ruft bei Bedarf externe APIs auf.
- Sobald die Ergebnisse an einem Ort gesammelt wurden, sendet der Server die Antwort an den Load Balancer zurück.
- Der Load Balancer sendet die Antwort an das API-Gateway, das sie wiederum an den Nutzer im Frontend-Client weiterleitet. Die Anfrage wird dann als abgeschlossen markiert.
Arten der Webanwendungsarchitektur
Nachdem du nun eine grundlegende Vorstellung davon hast, was eine Webanwendungsarchitektur ist, werfen wir einen detaillierten Blick auf einige der beliebtesten Arten von Webanwendungsarchitekturen, die im Internet verwendet werden.
Single-Page-Architektur
Die Architektur einer Single-Page-Anwendung (SPA) ist so einfach wie ihr Name: Die gesamte Anwendung basiert auf einer einzigen Seite. Sobald der Nutzer deine Anwendung aufruft, muss er nicht mehr zu anderen Webseiten navigieren. Die App ist so dynamisch gestaltet, dass sie die Bildschirme abruft und darstellt, die den Anforderungen der Nutzer/innen entsprechen, während sie durch die App navigieren.
SPAs sind großartig, wenn es darum geht, den Endnutzern oder Verbrauchern ein schnelles und nahtloses Erlebnis zu bieten. Allerdings fehlt ihnen der Touch einer traditionellen Website, und sie lassen sich nur schwer für SEO optimieren.
Vorteile der SPA-Architektur
Zu den Vorteilen der SPA-Architektur gehören:
- Du kannst hochgradig interaktive Webanwendungen erstellen.
- SPAs sind einfach zu skalieren.
- Die Optimierung von SPAs für die Leistung erfordert keinen großen Aufwand.
Nachteile der SPA-Architektur
Einige der Nachteile der SPA-Architektur sind:
- SPAs schränken die Flexibilität bei Hyperlinks und SEO ein.
- Das anfängliche Rendern ist in der Regel langsam.
- Die Navigation durch die App kann unintuitiv sein.
Progressive Webanwendungsarchitektur
Die Progressive Web Application (PWA)-Architektur baut auf der Single Page Architecture auf und bietet Offline-Funktionen für deine Webanwendung. Technologien wie Capacitor und Ionic werden verwendet, um PWAs zu erstellen, die den Nutzern auf allen Plattformen ein einheitliches Erlebnis bieten.
Ähnlich wie SPAs sind PWAs reibungslos und nahtlos. Mit der zusätzlichen Möglichkeit, auf den Geräten der Nutzer/innen installiert zu werden (über Service Worker), erhalten deine Nutzer/innen eine einheitlichere Erfahrung mit deiner Anwendung.
Gleichzeitig kann es schwierig sein, solche Apps für SEO zu optimieren, und Updates für installierte Apps lassen sich nur schwer einspielen.
Vorteile der PWA-Architektur
Es gibt viele Vorteile der PWA-Architektur, darunter:
- Apps laufen sehr flüssig und bieten plattformübergreifende Kompatibilität.
- Die Skalierbarkeit ist einfach.
- Offline-Zugriff und gerätenative APIs wie Background Worker und Push-Benachrichtigungen sind für Entwickler/innen zugänglich.
Nachteile der PWA-Architektur
Einige der Nachteile der PWA-Architektur können sein:
- Es gibt eine begrenzte Unterstützung für Linkmanagement und SEO.
- Die Übertragung von Updates auf Offline-PWAs ist komplexer als bei nativen Apps.
- Es gibt nur eine begrenzte Unterstützung für PWAs in verschiedenen Webbrowsern und Betriebssystemen.
Serverseitig gerenderte Architektur
Beim serverseitigen Rendering (SSR) werden die Frontend-Webseiten auf einem Backend-Server gerendert, nachdem sie vom Nutzer angefordert wurden. Dies trägt dazu bei, die Last auf dem Client-Gerät zu verringern, da es eine statische HTML-, CSS- und JS-Webseite erhält.
SSR-Apps sind bei Blogs und E-Commerce-Websites sehr beliebt. Das liegt daran, dass sie Linkmanagement und SEO ganz einfach machen. Außerdem ist der erste Rendervorgang bei SSR-Apps recht schnell, da der Client keinen JS-Code verarbeiten muss, um die Bildschirme zu rendern.
Vorteile der SSR-Architektur
Einige der Vorteile der SSR-Architektur werden im Folgenden aufgeführt:
- Diese Apps sind ideal für SEO-lastige Websites.
- Die erste Seite wird in den meisten Fällen fast sofort geladen.
- Du kannst sie mit einem Caching-Dienst kombinieren, um die Leistung deiner App weiter zu verbessern.
Nachteile der SSR-Architektur
Zu den Nachteilen der SSR-Architektur gehören:
- Sie wird nicht für komplexe oder umfangreiche Webseiten empfohlen, da der Server Zeit braucht, um die Seite vollständig zu generieren, was zu einem verzögerten ersten Rendering führen kann.
- Es wird vor allem für Apps empfohlen, die sich nicht so sehr auf die Benutzeroberfläche konzentrieren und nur auf eine höhere Skalierbarkeit oder Sicherheit Wert legen.
Vorgerenderte Anwendungsarchitektur
Die Architektur für vorgerenderte Anwendungen ist auch als statische Website-Generierungsarchitektur bekannt. Bei dieser Architektur werden die Frontend-Webseiten der Anwendung vorgeneriert und als einfache HTML-, CSS- und JS-Dateien auf dem Server gespeichert. Sobald ein Nutzer eine Seite anfordert, wird sie direkt abgerufen und angezeigt. Das macht die Web-App sehr schnell, mit minimalen Ladezeiten. Allerdings verlängert diese Architektur auch die Entwicklungszeit der App, da die Webseiten während des Entwicklungsprozesses gerendert werden.
Vorgerenderte Web-Apps sind ideal, wenn du statische Inhalte wie Blogs oder Produktdetails erstellen willst, die sich nicht oft ändern. Du kannst auch Vorlagen verwenden, um dein Webseitendesign zu vereinfachen. Allerdings ist es fast unmöglich, mit dieser Architektur dynamische Webanwendungen zu erstellen. Wenn du eine Suchseite erstellen willst, die die Suchanfrage in ihren Pfad aufnimmt (so etwas wie https://myapp.com/search/foo+bar
), bist du hier an der falschen Adresse.
Da jede mögliche Route der App während des Build-Prozesses vorgerendert wird, ist es unmöglich, dynamische Routen wie oben beschrieben zu erstellen, da es unendlich viele Möglichkeiten gibt, die während des Build-Prozesses nicht vorgerendert werden können (und es macht auch keinen Sinn, dies zu tun).
Vorteile einer vorgerenderten Architektur
Einige der wichtigsten Vorteile einer vorgerenderten Anwendungsarchitektur sind:
- Webseiten werden in reinem HTML, CSS und JS generiert; daher ist ihre Leistung ähnlich wie die von Apps, die mit Vanilla JS erstellt wurden.
- Wenn du alle möglichen Wege deiner App kennst, wird SEO super einfach.
Nachteile der vorgerenderten Architektur
Wie jedes Architekturmodell hat auch die vorgerenderte Architektur ihre Nachteile:
- Dynamische Inhalte können mit diesen Apps nicht ausgeliefert werden.
- Jede Änderung an der Web-App bedeutet, dass die App komplett neu aufgebaut und bereitgestellt werden muss.
Isomorphe Anwendungsarchitektur
Isomorphe Anwendungen sind eine Mischung aus serverseitig gerenderten Anwendungen und SPAs. Das bedeutet, dass solche Apps zunächst auf dem Server als normale serverseitig gerenderte App gerendert werden. Sobald sie vom Client empfangen werden, hydratisiert sich die App selbst und fügt das virtuelle DOM für eine schnellere und effizientere Client-Verarbeitung hinzu. Dadurch wird die App im Wesentlichen zu einer Single-Page-Anwendung.
Isomorph bringt das Beste aus beiden Welten zusammen. Dank der SPA erhältst du eine superschnelle Verarbeitung und Benutzeroberfläche auf dem Client. Dank des serverseitigen Renderings erhältst du außerdem eine schnelle Darstellung und umfassende Unterstützung für SEO und Verlinkung.
Vorteile der isomorphen Architektur
Hier sind nur einige der Vorteile der isomorphen Anwendungsarchitektur:
- Isomorphe Apps lassen sich superschnell rendern und bieten volle Unterstützung für SEO.
- Diese Apps funktionieren auch auf dem Client gut, da sie sich nach dem Laden in eine SPA verwandeln.
Nachteile der isomorphen Architektur
Einige der Nachteile einer isomorphen Anwendungsarchitektur können sein:
- Die Einrichtung einer solchen App erfordert qualifiziertes Personal.
- Die Möglichkeiten des Tech-Stacks sind begrenzt, wenn es um die Entwicklung einer isomorphen App geht. Du kannst nur aus einer Handvoll (meist) JS-basierter Bibliotheken und Frameworks wählen.
Serviceorientierte Architektur
Die serviceorientierte Architektur ist eine der beliebtesten Alternativen zum traditionellen monolithischen Aufbau von Anwendungen. Bei dieser Architektur werden die Webanwendungen in Dienste unterteilt, die jeweils eine funktionale Einheit des Unternehmens darstellen. Diese Dienste sind lose miteinander gekoppelt und interagieren über das Medium der Nachrichtenübermittlung miteinander.
Eine serviceorientierte Architektur erhöht die Stabilität und Skalierbarkeit deiner Anwendung. Allerdings ist die Größe der Dienste in der SOA nicht klar definiert und in der Regel an geschäftliche und nicht an technische Komponenten gebunden, so dass die Wartung manchmal ein Problem darstellen kann.
Vorteile der serviceorientierten Architektur
Zu den wichtigsten Vorteilen einer serviceorientierten Architektur gehören:
- Diese Architektur hilft dabei, hoch skalierbare und zuverlässige Apps zu entwickeln.
- Komponenten sind wiederverwendbar und werden gemeinsam genutzt, um den Entwicklungs- und Wartungsaufwand zu verringern.
Nachteile der serviceorientierten Architektur
Hier ist eine Liste mit möglichen Nachteilen der serviceorientierten Architektur:
- SOA-Anwendungen sind dennoch nicht zu 100 % flexibel, da die Größe und der Umfang der einzelnen Dienste nicht festgelegt sind. Es kann Dienste in der Größe von Unternehmensanwendungen geben, die schwer zu warten sind.
- Die gemeinsame Nutzung von Komponenten führt zu Abhängigkeiten zwischen den Diensten.
Microservices-Architektur
Die Microservices-Architektur wurde entwickelt, um die Probleme der serviceorientierten Architektur zu lösen. Microservices sind noch mehr modulare Komponenten, die zu einer Webanwendung zusammengefügt werden. Der Schwerpunkt von Microservices liegt jedoch darauf, jede Komponente klein und in einem begrenzten Kontext zu halten. Bounded Context bedeutet im Wesentlichen, dass der Code und die Daten jedes Microservices mit minimalen Abhängigkeiten von anderen Microservices gekoppelt sind.
Die Microservices-Architektur ist wahrscheinlich die beste Architektur, um Anwendungen zu entwickeln, die eines Tages auf Tausende und Millionen von Nutzern skalieren sollen. Jede Komponente ist belastbar, skalierbar und einfach zu warten. Allerdings erfordert die Aufrechterhaltung des DevOps-Lebenszyklus für eine Microservices-basierte App zusätzlichen Aufwand, weshalb sie für kleinere Anwendungsfälle möglicherweise nicht geeignet ist.
Vorteile der Microservices-Architektur
Einige Vorteile der Microservices-Architektur sind:
- App-Komponenten sind hochgradig modular, unabhängig und können in größerem Umfang wiederverwendet werden als die Komponenten der serviceorientierten Architektur.
- Jede Komponente kann unabhängig skaliert werden, um dem schwankenden Nutzeraufkommen gerecht zu werden.
- Microservices-basierte Anwendungen sind hochgradig fehlertolerant.
Nachteile der Microservices-Architektur
Ein Nachteil der Microservices-Architektur kann sein:
- Für kleinere Projekte kann die Microservices-Architektur einen zu hohen Wartungsaufwand bedeuten.
Serverlose Architektur
Die serverlose Architektur ist ein weiterer heißer Kandidat in der Welt der Web-App-Architekturen. Bei dieser Architektur geht es darum, deine Anwendung in die Funktionen zu zerlegen, die sie ausführen soll. Diese Funktionen werden dann auf FaaS-Plattformen (Function-as-a-Service) als Funktionen gehostet, die aufgerufen werden, wenn Anfragen eingehen.
Im Gegensatz zu den meisten anderen Architekturen auf dieser Liste laufen Apps, die mit der serverlosen Architektur entwickelt wurden, nicht die ganze Zeit über. Sie verhalten sich wie Funktionen – sie warten darauf, dass sie aufgerufen werden, und wenn sie aufgerufen werden, führen sie den definierten Prozess aus und geben ein Ergebnis zurück. Dadurch reduzieren sie die Wartungskosten und sind ohne großen Aufwand hoch skalierbar. Allerdings ist es schwierig, mit solchen Komponenten langwierige Tasks auszuführen.
Vorteile der serverlosen Architektur
Hier sind die wichtigsten Vorteile der serverlosen Architektur:
- Serverlose Apps sind hochgradig und leicht skalierbar. Sie können sich sogar in Echtzeit an den eingehenden Datenverkehr anpassen, um die Belastung deiner Infrastruktur zu verringern.
- Solche Apps können das Pay-per-Use-Preismodell von serverlosen Plattformen nutzen, um die Infrastrukturkosten zu senken.
- Serverlose Apps sind recht einfach zu entwickeln und bereitzustellen, da du nur eine Funktion schreiben und sie auf einer Plattform wie Firebase Functions, AWS Lambda usw. hosten musst.
Nachteile der serverlosen Architektur
Nachfolgend sind einige Nachteile der serverlosen Architektur aufgeführt:
- Langwierige Aufgaben können in einer solchen Architektur teuer sein.
- Wenn eine Funktion nach langer Zeit eine Anfrage erhält, nennt man das einen Kaltstart. Kaltstarts sind langsam und können für deine Endnutzer/innen ein schlechtes Erlebnis darstellen.
Schichten der Webanwendungsarchitektur
Die Architekturen der Webanwendungen, die du oben gesehen hast, sehen zwar alle recht unterschiedlich aus, aber ihre Komponenten lassen sich logisch in bestimmte Schichten einteilen, die dabei helfen, ein Geschäftsziel zu erreichen.
Präsentationsschicht
Die Präsentationsschicht umfasst alle Elemente einer Webanwendung, die für die Endnutzer sichtbar sind. In erster Linie besteht die Präsentationsschicht aus dem Frontend-Client. Sie enthält aber auch die Logik, die du in deinem Backend geschrieben hast, um dein Frontend dynamisch zu machen. So hast du die Möglichkeit, deinen Nutzern eine auf ihr Profil und ihre Bedürfnisse zugeschnittene Benutzeroberfläche zu bieten.
Drei grundlegende Technologien werden zum Aufbau dieser Schicht verwendet: HTML, CSS und JavaScript. HTML entwirft dein Frontend, CSS gestaltet es und JS haucht Leben hinein (d.h. steuert das Verhalten, wenn die Nutzer/innen damit interagieren). Zusätzlich zu diesen drei Technologien kannst du jede Art von Framework verwenden, um die Entwicklung zu vereinfachen. Einige gängige Frontend-Frameworks sind Laravel, React, NextJS, Vue, GatsbyJS, etc.
Business-Schicht
Die Business-Schicht ist für die Arbeitslogik deiner App verantwortlich und verwaltet sie. In der Regel handelt es sich um einen Backend-Dienst, der Anfragen vom Kunden entgegennimmt und verarbeitet. Sie kontrolliert, worauf der Nutzer zugreifen kann und bestimmt, wie die Infrastruktur genutzt wird, um Nutzeranfragen zu bedienen.
Im Fall einer Hotelbuchungs-App dient deine Client-App als Portal, in das die Nutzer Hotelnamen und andere relevante Daten eingeben können. Sobald der Nutzer jedoch auf die Suchschaltfläche klickt, erhält die Business-Schicht die Anfrage und startet die Logik für die Suche nach verfügbaren Hotelzimmern, die deinen Anforderungen entsprechen. Der Kunde erhält dann einfach eine Liste von Hotelzimmern, ohne zu wissen, wie diese Liste erstellt wurde oder warum die Listenelemente so angeordnet sind, wie sie gesendet wurden.
Das Vorhandensein einer solchen Schicht stellt sicher, dass deine Geschäftslogik für deinen Kunden und letztendlich für die Nutzer nicht sichtbar ist. Die Isolierung der Geschäftslogik ist bei sensiblen Vorgängen wie der Zahlungsabwicklung oder der Verwaltung von Gesundheitsdaten sehr hilfreich.
Persistenzschicht
Die Persistenzschicht ist für die Kontrolle des Zugriffs auf deine Datenspeicher zuständig. Sie fungiert als zusätzliche Abstraktionsebene zwischen deinen Datenspeichern und deiner Business-Schicht. Sie nimmt alle datenbezogenen Aufrufe von den Business-Schichten entgegen und verarbeitet sie, indem sie sichere Verbindungen zur Datenbank herstellt.
Diese Schicht besteht normalerweise aus einem Datenbankserver. Du kannst diese Schicht selbst einrichten, indem du eine Datenbank und einen Datenbankserver in deiner On-Prem-Infrastruktur bereitstellst, oder du entscheidest dich für eine ferngesteuerte/verwaltete Lösung von einem der führenden Cloud-Infrastrukturanbieter wie AWS, GCP, Microsoft Azure usw.
Komponenten der Webanwendung
Nachdem du nun weißt, was eine Webanwendungsarchitektur ausmacht, wollen wir uns die einzelnen Komponenten einer Webanwendung genauer ansehen. Wir gliedern diese Diskussion in zwei große Bereiche: serverseitige Komponenten und clientseitige Komponenten, oder Backend- und Frontend-Komponenten.
Serverseitige Komponenten
Serverseitige Komponenten sind die Komponenten, die sich im Backend deiner Webanwendung befinden. Sie sind für die Nutzer nicht direkt sichtbar und enthalten die wichtigste Geschäftslogik und Ressourcen für deine Webanwendung.
DNS & Routing
DNS ist dafür verantwortlich, zu kontrollieren, wie deine Anwendung im Web dargestellt wird. DNS-Einträge werden von HTTP-Clients (das kann auch ein Browser sein) verwendet, um die Komponenten deiner App zu finden und Anfragen an sie zu senden. DNS wird auch intern von deinen Frontend-Clients verwendet, um den Standort deiner Webserver und API-Endpunkte aufzulösen, um Anfragen zu senden und Benutzeraktionen zu verarbeiten.
Der Load Balancer ist eine weitere beliebte Komponente der Architektur von Webanwendungen. Ein Load Balancer wird verwendet, um HTTP-Anfragen auf mehrere identische Webserver zu verteilen. Mehrere Webserver dienen der Redundanz, um die Fehlertoleranz zu erhöhen und den Datenverkehr so zu verteilen, dass die Leistung hoch bleibt.
API-Endpunkte werden verwendet, um Backend-Dienste für die Frontend-Anwendung bereitzustellen. Sie erleichtern die Kommunikation zwischen dem Client und dem Server und manchmal sogar zwischen mehreren Servern.
Datenspeicherung
Die Datenspeicherung ist ein wichtiger Bestandteil der meisten modernen Anwendungen, denn es gibt immer einige Anwendungsdaten, die über mehrere Benutzersitzungen hinweg aufbewahrt werden müssen. Es gibt zwei Arten der Datenspeicherung:
- Datenbanken: Datenbanken werden verwendet, um Daten für einen schnellen Zugriff zu speichern. Normalerweise speichern sie eine kleine Menge an Daten, auf die deine Anwendung regelmäßig zugreift.
- Data Warehouses: Data Warehouses sind für die Aufbewahrung von historischen Daten gedacht. Diese werden in der Regel nicht sehr oft in der Anwendung benötigt, aber regelmäßig verarbeitet, um Geschäftseinblicke zu gewinnen.
Caching
Caching ist eine optionale Funktion, die oft in Web-App-Architekturen implementiert wird, um Inhalte schneller an die Nutzer/innen zu liefern. Ein großer Teil der App-Inhalte wiederholt sich oft für eine gewisse Zeit, wenn auch nicht immer. Anstatt sie aus einem Datenspeicher abzurufen und zu verarbeiten, bevor sie an den Nutzer zurückgeschickt werden, werden sie oft zwischengespeichert. Hier sind die beiden beliebtesten Arten des Cachings, die in Webanwendungen verwendet werden:
- Data Caching: Data Caching ermöglicht es deiner Anwendung, einfach und schnell auf regelmäßig verwendete Daten zuzugreifen, die sich nicht oft ändern. Technologien wie Redis und Memcache ermöglichen das Zwischenspeichern von Daten, um teure Datenbankabfragen zu sparen, die nötig sind, um die gleichen Daten immer wieder abzurufen.
- Caching von Webseiten: Ein CDN (Content Delivery Network) cached Webseiten auf die gleiche Weise wie Redis Daten. Ähnlich wie nur Daten, die sich nicht oft ändern, zwischengespeichert werden, werden normalerweise nur statische Webseiten für das Caching empfohlen. Bei serverseitig gerenderten Webanwendungen bringt das Caching nicht viel, da ihre Inhalte sehr dynamisch sein sollen.
Jobs und Dienste
Neben der Benutzeroberfläche (Frontend) und der Bearbeitung von Anfragen (Backend) gibt es eine weitere, etwas weniger beliebte Kategorie von Webanwendungskomponenten. Jobs sind oft Hintergrunddienste, die Aufgaben erledigen sollen, die nicht zeitkritisch oder synchron sind.
CRON-Jobs sind Jobs, die in einem bestimmten Zeitraum immer wieder ausgeführt werden. Diese Jobs werden im Backend geplant, um Wartungsroutinen automatisch zu bestimmten Zeiten auszuführen. Einige häufige Anwendungsfälle sind das Löschen von Duplikaten/alten Datensätzen aus der Datenbank, das Versenden von Erinnerungs-E-Mails an Kunden usw.
Clientseitige Komponenten
Clientseitige Komponenten sind diejenigen, die entweder direkt oder indirekt für deine Nutzer/innen sichtbar sind.
Es gibt hauptsächlich zwei Arten von Komponenten in dieser Kategorie.
Frontend-Benutzeroberfläche
Die Benutzeroberfläche ist der visuelle Aspekt deiner Anwendung. Sie ist das, was deine Nutzer sehen und womit sie interagieren, um auf deine Dienste zuzugreifen.
Die Frontend-Benutzeroberfläche basiert meist auf drei gängigen Technologien: HTML, CSS und JavaScript. Die Frontend-Benutzeroberfläche kann eine eigenständige Anwendung mit einem eigenen Softwareentwicklungszyklus sein.
Diese Benutzeroberflächen enthalten nicht viel von deiner Geschäftslogik, da sie direkt für deine Benutzer sichtbar sind. Wenn ein böswilliger Benutzer versucht, deine Frontend-Anwendung zurückzuentwickeln, kann er Informationen über die Funktionsweise deines Unternehmens erhalten und illegale Aktivitäten wie Markenimitation und Datendiebstahl durchführen.
Da die Benutzeroberfläche des Frontends den Nutzern direkt zugänglich ist, solltest du sie so optimieren, dass sie möglichst schnell lädt und schnell reagiert. Manchmal kann dies dazu beitragen, dass du deinen Nutzern ein besseres Erlebnis bietest und dadurch dein Geschäftswachstum steigerst.
Clientseitige Geschäftslogik
Manchmal musst du vielleicht einen Teil deiner Geschäftslogik auf deinem Client speichern, um einfachere Vorgänge schnell ausführen zu können. Clientseitige Logik, die sich normalerweise in deiner Frontend-Anwendung befindet, kann dir helfen, den Weg zum Server zu überspringen und deinen Nutzern ein schnelleres Erlebnis zu bieten.
Dies ist eine optionale Funktion der clientseitigen Komponenten. In manchen Fällen wird die Geschäftslogik der App vollständig auf der Client-Seite gespeichert (vor allem, wenn du ohne einen traditionellen Backend-Server arbeitest). Mit modernen Lösungen wie BaaS kannst du in deiner Frontend-App von unterwegs aus auf gängige Vorgänge wie Authentifizierung, Datenspeicherung, Dateispeicherung usw. zugreifen.
Es gibt Möglichkeiten, diesen Code zu verschleiern oder zu verkleinern, bevor du ihn deinen Nutzern zur Verfügung stellst, um das Risiko eines Reverse-Engineerings zu minimieren.
Modelle von Webanwendungskomponenten
Es gibt mehrere Modelle von Webanwendungsarchitekturen, die jeweils darauf basieren, wie Webserver mit ihren Datenspeichern verbunden sind.
Ein Server, eine Datenbank
Das einfachste Modell ist ein Webserver, der eine Verbindung zu einer Datenbankinstanz herstellt. Ein solches Modell ist einfach zu implementieren und zu warten und kann ohne großen Aufwand in die Produktion übernommen werden.
Aufgrund seiner Einfachheit eignet sich dieses Modell zum Lernen und für kleine experimentelle Anwendungen, die keinem hohen Datenverkehr ausgesetzt sind. Neulinge können diese Anwendungen leicht einrichten und ausprobieren, um die Grundlagen der Web-App-Entwicklung zu erlernen.
Allerdings sollte dieses Modell nicht in der Produktion eingesetzt werden, da es sehr unzuverlässig ist. Ein Problem mit dem Server oder der Datenbank kann zu Ausfallzeiten und Geschäftseinbußen führen.
Mehrere Server, eine Datenbank
Bei diesem Modell wird die Anwendung mit mehreren Servern und einer gemeinsamen Datenbank redundant ausgelegt.
Da mehrere Webserver gleichzeitig auf die Datenbank zugreifen, kann es zu Inkonsistenzen kommen. Um dies zu vermeiden, sind die Webserver zustandslos. Das bedeutet, dass die Server die Daten nicht über mehrere Sitzungen hinweg aufbewahren, sondern sie lediglich verarbeiten und in der Datenbank speichern.
Apps, die mit diesem Modell erstellt werden, sind mit Sicherheit zuverlässiger als die mit dem vorherigen Modell, da das Vorhandensein mehrerer Webserver die Fehlertoleranz der Web-App erhöht. Da die Datenbank jedoch immer noch eine gemeinsame Instanz ist, ist sie das schwächste Glied in der Architektur und kann eine Fehlerquelle sein.
Mehrere Server, mehrere Datenbanken
Dieses Modell ist eines der gängigsten, traditionellen Modelle für die Entwicklung von Webanwendungen.
In diesem Fall stellst du deine Anwendungslogik auf mehreren identischen Webserver-Instanzen bereit, die hinter einem Load Balancer zusammengeschlossen sind. Dein Datenspeicher wird ebenfalls über mehrere Datenbankinstanzen verwaltet, um die Fehlertoleranz zu erhöhen.
Du kannst deine Datenbank auch auf die verfügbaren Instanzen aufteilen, um die Leistung zu verbessern, oder Duplikate des gesamten Datenspeichers zur Redundanz vorhalten. In jedem Fall führt der Ausfall einer einzelnen Datenbankinstanz nicht zu einem kompletten Ausfall der Anwendung.
Dieses Modell wird wegen seiner Zuverlässigkeit und Skalierbarkeit sehr geschätzt. Die Entwicklung und Wartung von Anwendungen nach diesem Modell ist jedoch relativ kompliziert und erfordert teure, erfahrene Entwickler/innen. Daher wird dieses Modell nur empfohlen, wenn du in großem Maßstab entwickelst.
Anwendungsdienste
Während die drei oben genannten Modelle gut für monolithische Anwendungen geeignet sind, gibt es ein weiteres Modell für modulare Anwendungen.
Das Modell der Anwendungsdienste unterteilt eine Anwendung in kleinere Module, die auf Geschäftsfunktionen basieren. Diese Module können so klein wie eine Funktion oder so groß wie ein Dienst sein.
Die Idee dahinter ist, dass jede Geschäftsfunktion unabhängig und skalierbar ist. Jedes dieser Module kann sich eigenständig mit der Datenbank verbinden. Du kannst sogar eigene Datenbankinstanzen einrichten, um die Skalierbarkeit deines Moduls zu gewährleisten.
Bei nicht-monolithischen Anwendungen ist dieses Modell sehr beliebt. Ältere monolithische Anwendungen werden oft auf dieses Modell migriert, um die Vorteile der Skalierbarkeit und Modularität zu nutzen. Die Verwaltung von Anwendungen, die nach einem solchen Modell aufgebaut sind, erfordert jedoch oft erfahrene Entwickler/innen, vor allem mit Erfahrung in DevOps und CI/CD.
Best Practices für die Architektur von Webanwendungen
Hier sind einige Best Practices, die du in deinem Webanwendungsprojekt umsetzen kannst, um das Beste aus deiner gewählten Webanwendungsarchitektur herauszuholen.
1. Mach dein Frontend reaktionsfähig
Das kann nicht oft genug betont werden: Strebe immer responsive Frontends an. Egal wie groß und komplex deine Webanwendung intern ist, deine Nutzerinnen und Nutzer bekommen alles über die Frontend-Webseiten, Apps und Bildschirme zu sehen.
Wenn deine Nutzer diese Bildschirme als unintuitiv oder langsam empfinden, werden sie nicht lange genug bleiben, um das technische Wunderwerk, das deine Webanwendung ist, wertschätzen zu können.
Deshalb ist es sehr wichtig, leicht zugängliche, benutzerfreundliche und schlanke Frontends zu entwickeln.
Im Internet gibt es zahlreiche UI/UX Best Practices, die dir dabei helfen, herauszufinden, was für deine Nutzer/innen am besten funktioniert. Hier findest du Fachleute, die sich mit benutzerfreundlichen Designs und Architekturen auskennen, die es deinen Nutzern ermöglichen, das Beste aus deinen Apps herauszuholen.
Wir raten dir, dir über die Reaktionsfähigkeit deines Frontends Gedanken zu machen, bevor du dein Produkt für deine Nutzer/innen bereitstellst.
2. Ladezeiten überwachen
Deine Frontends müssen nicht nur leicht verständlich sein, sondern auch schnell laden.
Laut Portent werden die höchsten Konversionsraten im E-Commerce auf Seiten mit Ladezeiten zwischen 0 und 2 Sekunden erzielt, und laut Unbounce geben rund 70 % der Verbraucher zu, dass die Ladezeit einer Seite ein wichtiger Faktor bei ihrer Entscheidung für einen Kauf bei einem Online-Händler ist.
Wenn du mobile-native Anwendungen entwickelst, kannst du dir in der Regel nicht sicher sein, welche Spezifikationen die Geräte deiner Nutzer/innen haben. Jedes Gerät, das die Anforderungen deiner App nicht erfüllt, wird in der Regel als nicht geeignet für die App erklärt.
Im Web ist das jedoch ganz anders.
Wenn es um Webanwendungen geht, können deine Nutzer/innen vom neuesten Apple Macbook M1 Pro bis hin zu alten Blackberry- und Nokia-Telefonen alles benutzen, um deine App anzusehen. Die Optimierung deines Frontends für eine so große Bandbreite von Nutzern kann manchmal schwierig sein.
Dienste wie LightHouse und Google PageSpeed fallen ein, wenn es um die Leistung deines Frontends geht. Du solltest solche Tools nutzen, um deine Frontend-App zu testen, bevor du sie in die Produktion überführst. Die meisten dieser Tools liefern dir eine Liste mit umsetzbaren Tipps, mit denen du die Leistung deiner App so weit wie möglich verbessern kannst.
Die letzten 5-10% der App-Leistung sind in der Regel spezifisch für deinen Anwendungsfall und können nur von jemandem behoben werden, der deine App und ihre Technologien gut kennt. Es kann nie schaden, in die Web-Performance zu investieren!
3. Bevorzuge PWAs, wo immer möglich
Wie bereits erwähnt, sind PWAs die Designs der Zukunft. Sie eignen sich für die meisten Anwendungsfälle und bieten das einheitlichste Erlebnis auf allen wichtigen Plattformen.
Du solltest so oft wie möglich PWA für deine App verwenden. Das native Erlebnis im Web und auf dem Handy ist für deine Nutzerinnen und Nutzer von großer Bedeutung und kann auch deinen eigenen Arbeitsaufwand reduzieren.
PWAs laden außerdem schnell, sind einfach zu optimieren und schnell zu erstellen. Wenn du dich für PWAs entscheidest, kannst du deinen Fokus frühzeitig von der Entwicklung auf das Geschäft verlagern.
4. Halte deine Codebasis sauber und prägnant
Eine saubere Codebasis kann dir helfen, die meisten Probleme zu erkennen und zu lösen, bevor sie Schaden anrichten. Hier sind einige Tipps, die du befolgen kannst, um sicherzustellen, dass deine Codebasis dir nicht mehr Probleme bereitet, als sie sollte.
- Fokus auf die Wiederverwendung von Code: Die Beibehaltung von Kopien desselben Codes in deiner gesamten Codebasis ist nicht nur überflüssig, sondern kann auch dazu führen, dass sich Unstimmigkeiten einschleichen und deine Codebasis schwer zu pflegen ist. Achte darauf, den Code so oft wie möglich wiederzuverwenden.
- Plane deine Projektstruktur: Softwareprojekte können mit der Zeit sehr groß werden. Wenn du nicht mit einer geplanten Struktur der Codeorganisation und der Ressourcen beginnst, verbringst du am Ende vielleicht mehr Zeit mit dem Suchen von Dateien als mit dem Schreiben von nützlichem Code.
- Schreibe Unit-Tests: Jeder Teil des Codes kann kaputt gehen. Es ist nicht möglich, alles manuell zu testen. Deshalb brauchst du eine feste Strategie für die Automatisierung von Tests für deine Codebasis. Test-Runner und Code-Coverage-Tools können dir dabei helfen, herauszufinden, ob deine Unit-Tests die gewünschten Ergebnisse liefern.
- Hohe Modularität: Wenn du Code schreibst, solltest du immer auf Modularität achten. Wenn du Code schreibst, der eng mit anderen Teilen gekoppelt ist, wird es schwierig, ihn zu testen, wiederzuverwenden und bei Bedarf zu ändern.
5. Automatisiere deine CI/CD-Prozesse
CI/CD steht für Continuous Integration/Continuous Deployment (kontinuierliche Integration/kontinuierliche Bereitstellung). CI/CD-Prozesse sind entscheidend für die Entwicklung deiner Anwendung, da sie dir helfen, dein Projekt einfach zu erstellen, zu testen und zu verteilen.
Du willst sie aber nicht jedes Mal manuell ausführen müssen. Du solltest stattdessen Pipelines einrichten, die automatisch auf der Grundlage der Aktivitäten deines Projekts ausgelöst werden. Du könntest zum Beispiel eine Pipeline einrichten, die deine Tests automatisch ausführt, wenn du deinen Code in dein Versionskontrollsystem einträgst. Es gibt auch viele komplexere Anwendungsfälle, wie z. B. das Erzeugen von plattformübergreifenden Artefakten aus deinem Code-Repository, wenn ein Release erstellt wird.
Die Möglichkeiten sind endlos, es liegt also an dir, herauszufinden, wie du das Beste aus deinen CI/CD-Pipelines herausholen kannst.
6. Sicherheitsfunktionen einbinden
Die meisten modernen Apps bestehen aus mehreren Komponenten. Nimm die folgende App als Beispiel:
Client-Anfragen werden über ein API-Gateway an die App weitergeleitet. Dieses erlaubt derzeit nur direkte Anfragen an das Home-Modul der App, aber in Zukunft könnte es den Zugriff auf weitere Komponenten ermöglichen, ohne den Umweg über das Home-Modul.
Als Nächstes prüft das Home-Modul eine externe Authentifizierungs-BaaS, bevor es den Zugriff erlaubt. Sobald der Kunde authentifiziert ist, kann er die Seiten „Profil aktualisieren“ oder „Profil anzeigen“ aufrufen. Diese beiden Seiten interagieren mit einer gemeinsamen, verwalteten Datenbanklösung, die die Profildaten verwaltet.
Wie du siehst, sieht die Anwendung wie eine sehr einfache und minimale Version eines Online-Personenverzeichnisses aus. Du kannst dein eigenes Profil hinzufügen/aktualisieren oder andere verfügbare Profile ansehen.
Hier ist eine kurze Legende zu den verschiedenen Komponenten der Architektur:
- Blaue Kästchen: App-Module, die möglicherweise als Microservices oder serverlose Funktionen gehostet werden.
- Rote Kästchen: Externe BaaS-Komponenten, die für die Authentifizierung und die Datenbank sorgen.
- Grüne Kästchen: Routing-Komponente, die eingehende Anfragen vom Client moderiert.
- Schwarzes Kästchen: Deine Client-Anwendung, die für den Nutzer sichtbar ist.
Die Komponenten in jeder der oben genannten Farben sind für verschiedene Arten von Sicherheitsbedrohungen anfällig. Hier sind einige Sicherheitsvorkehrungen, die du treffen kannst, um dein Risiko zu minimieren:
- App-Module (blau): Da es sich hierbei um serverlose Funktionen handelt, hier ein paar Tipps, um ihre Sicherheit zu erhöhen:
- Isoliere App-Geheimnisse und verwalte sie unabhängig von deinem Quellcode
- Verwalte die Zugriffskontrolle durch IAM-Dienste
- Verbessere deine Tests, um mit Techniken wie SAST auch nach Sicherheitsbedrohungen zu suchen
- Externe Dienste (rot):
- Richte Zugriffskontrollen über ihre IAM-Module ein, um den Zugriff zu regeln
- Entscheide dich für eine API-Ratenbegrenzung
- Richte für Dienste wie Datenbanken feinere Kontrollrechte ein, z. B. wer auf die Profildaten zugreifen darf, wer die Nutzerdaten sehen darf und mehr. Viele Dienste, wie z. B. Firebase, bieten einen detaillierten Satz solcher Regeln an.
- Routing-Komponente (grün):
- Wie alle anderen Komponenten implementierst du Zugriffskontrollen
- Autorisierung einrichten
- Überprüfe die standardmäßigen Best Practices wie CORS
- Client:
- Stelle sicher, dass keine Anwendungsgeheimnisse für deinen Client verfügbar sind
- Verschleiere den Code deines Clients, um das Risiko eines Reverse Engineering zu minimieren
Dies sind zwar nur eine Handvoll Vorschläge, aber sie machen deutlich, dass die Sicherheit von Apps kompliziert ist und es in deiner Verantwortung liegt, dafür zu sorgen, dass du keine losen Enden hinterlässt, die Angreifer für sich nutzen können. Du kannst dich nicht auf eine zentrale Sicherheitskomponente verlassen, um dein Unternehmen zu schützen; die App-Sicherheit ist über deine gesamte App-Architektur verteilt.
7. Sammle Nutzerfeedback
Nutzerfeedback ist ein wichtiges Instrument, um zu verstehen, wie gut deine App in Bezug auf die geschäftliche und technische Leistung abschneidet. Du kannst die leichteste und reibungsloseste App der Welt bauen, aber wenn sie deinen Nutzern nicht das bietet, was sie erwarten, sind alle deine Bemühungen umsonst.
Es gibt verschiedene Möglichkeiten, Nutzerfeedback zu sammeln. Die herkömmliche Methode ist eine schnelle und anonymisierte Umfrage. Du kannst aber auch eine ausgefeiltere Lösung wählen, wie z. B. eine Heatmap der Aktivitäten deiner Nutzer/innen.
Die Wahl der Methode zur Sammlung von Feedback ist weniger wichtig als die Umsetzung des gesammelten Feedbacks. Kunden lieben Unternehmen, die sich ihre Probleme anhören. Giganten wie McDonald’s und Tesla tun das, und das ist einer der Gründe, warum sie auf ihren Märkten weiterhin erfolgreich sind.
Zusammenfassung
Das Internet ist eine riesige Spielwiese mit einer Vielzahl von Anwendungen, die alle auf ihre eigene Art und Weise gestaltet sind. Verschiedene Architekturen ermöglichen es Webanwendungen, sich zu diversifizieren, zu florieren und den Nutzern auf der ganzen Welt Dienste anzubieten.
In diesem Leitfaden haben wir die verschiedenen Modelle der Webanwendungsarchitektur aufgeschlüsselt und dir gezeigt, wie wichtig sie für das Wachstum einer Anwendung sind.
Gibt es eine Web-App-Architektur, die du besonders magst? Oder gibt es eine andere, die du gerne mit der Welt teilen würdest? Lass es uns in den Kommentaren unten wissen!
Schreibe einen Kommentar