Lernzettel

Dieser Lernzettel ist nach den Vorlesungsunterlagen entstanden. Jegliche Inhalte sind nicht von mir verifiziert und können Fehler enthalten. Ich empfehle, aussagen wie "X ist besser als Y" zu hinterfragen und selbst zu recherchieren. Ich übernehme keine Haftung für die Richtigkeit der Inhalte.

Entwicklungsansätze

Native Apps
  • Plattformabhängig (iOS, Android)
  • Sprache: Swift, Kotlin
Vorteile Nachteile
Direkte Hardwarezugriffe Hohe Entwicklungskosten
Hohe Performance Zwei Codebases -> Hohe Wartungskosten
Vollständige Nutzung der Plattform-Features Längere Entwicklungszeit
Cross-Plattform Apps
  • Plattformunabhängig (iOS, Android)
  • Sprache: JavaScript, Dart
Vorteile Nachteile
Geringe Entwicklungskosten Eingeschränkte Performance
Ein Codebase für alle Plattformen Eingeschränkter Zugriff auf Hardware-Features
Schnellere Entwicklung Abhängigkeit von Frameworks
Einheitliches UI App integriert sich nicht in das Systemdesign
Progressive Web Apps (PWAs)
  • Webanwendungen, die wie native Apps funktionieren
  • Sprache: HTML, CSS, JavaScript
  • Offline-Funktionalität, Push-Benachrichtigungen
Vorteile Nachteile
Plattformunabhängig Eingeschränkter Zugriff auf Hardware-Features
Einfache Verteilung Eingeschränkte Performance & native Funktionen
Geringe Entwicklungskosten Abhängigkeit von Browserkompatibilität

CAP Theorem

  • Consistency -> Alle Clients sehen die gleichen Daten; Replikationskonsistenz
  • Availability -> Jeder Client erhält eine Antwort auf seine Anfrage
  • Partition Tolerance -> System funktioniert auch bei Netzwerkpartitionierung
    • Partitionierung -> Netzwerkfehler, der die Kommunikation zwischen Knoten verhindert

Kombinationen von CAP

CA

  • Konsistenz und Verfügbarkeit
  • Beispiel: Relationale Datenbanken

CP

  • Konsistenz und Partitionstoleranz
  • Beispiel: Zookeeper, HDFS, Redis, MongoDB

AP

  • Verfügbarkeit und Partitionstoleranz
  • Beispiel: DNS, Cassandra

BASE & ACID

BASE
  • Basically Available: Daten sind stets verfügbar.
  • Soft State: Systemzustand kann sich ohne Input ändern.
  • Eventual Consistency: Daten werden mit der Zeit konsistent.
    Beispiel: Cassandra, DynamoDB
ACID
  • Atomarität: Transaktion = alles oder nichts.
  • Konsistenz: Datenbank bleibt immer in gültigem Zustand.
  • Isolation: Transaktionen laufen isoliert.
  • Dauerhaftigkeit: Erfolgreiche Änderungen bleiben erhalten.
    Beispiel: PostgreSQL, MySQL

Anti-Corruption-Layer (ACL)

  • Schicht, die zwischen zwei Systemen vermittelt.
  • Verhindert, dass Änderungen in einem System das andere beeinflussen.
  • Übersetzt Datenformate und -strukturen
  • Bsp.: XML <-> JSON, SOAP <-> REST

Coupling

Tight Coupling
  • Starke Abhängigkeit zwischen Modulen.
  • Änderungen in einem Modul erfordern Änderungen in anderen Modulen.
  • Beispiel: Monolithische Anwendungen, enge Kopplung zwischen Frontend und Backend.
Loose Coupling
  • Geringe Abhängigkeit zwischen Modulen.
  • Änderungen in einem Modul erfordern keine Änderungen in anderen Modulen.
  • Beispiel: Microservices, REST-APIs, Event-Driven Architecture.

Vorteile von Loose Coupling:

  • Flexibilität ABER auch Komplexität
  • Wartbarkeit
  • Wiederverwendbarkeit

Architekturmuster

Microservices
  • Unabhängige, kleine Dienste.
  • Jeder Dienst hat eigene Datenbasis.
  • Kommunikation über APIs.
  • Vorteile: Flexibilität, Skalierbarkeit, Wartbarkeit.
  • Nachteile: Komplexität, Overhead, Netzwerk-Latenz.

Databases

Services

API Gateway

User

Gateway

Auth Service

Product Service

Order Service

Auth DB

Product DB

Order DB

Monolith
  • Eine große Anwendung.
  • Alle Komponenten sind eng miteinander verbunden.
  • Vorteile: Einfachheit, Performance, weniger Overhead.
  • Nachteile: Geringe Flexibilität, schwer zu skalieren, schwer zu warten.

Monolith Application

User

User Interface

Business Logic

Database

Aspekt Microservices Monolith
Entwicklung Komplexer Einfacher
Deployment Einzeln Ganzes System
Skalierung Individuell Ganzes System
Wartbarkeit Flexibel Schwerfällig

Datenmanagement und Kommunikation

Datenpersistenz

Local Storage

Beispiel: IndexedDB, SQLite

Vorteile Nachteile
Offline-Verfügbarkeit Eingeschränkte Synchronisation
Schneller Zugriff Eingeschränkte Kapazität
Kontrolle über Daten Datenverlust bei App-Deinstallation
Remote Storage

Beispiel: Cloud-Datenbanken wie Firebase, AWS DynamoDB

Vorteile Nachteile
Skalierbarkeit Abhängigkeit von Internetverbindung
Zentrale Datenverwaltung (-> multi Device möglich Datenschutzbedenken
Einfache Synchronisation Kosten

Datenkommunikation

REST (Representational State Transfer)
  • Verwendet HTTP-Methoden (GET, POST, PUT, DELETE)
  • Datenformat: JSON, XML
Push Notifications
  • Echtzeit-Benachrichtigungen an Benutzer
  • Beispiel: Firebase Cloud Messaging (FCM), Apple Push Notification Service (APNS)
  • Vorteile: Echtzeit-Interaktion, Benutzerbindung
  • Nachteile: Abhängigkeit von Internetverbindung
  • Anwendung: Chat-Apps, soziale Netzwerke, E-Commerce

Synchronisation

Polling
  • Regelmäßige Abfragen des Servers nach neuen Daten
  • Beispiel: Wetter-App fragt alle 5 Minuten nach neuen Wetterdaten
  • Vorteile: Einfach zu implementieren
  • Nachteile: Hoher Ressourcenverbrauch, Verzögerung bei neuen Daten
Long Polling
  • Server hält Verbindung offen, bis neue Daten verfügbar sind
  • Beispiel: Chat-App wartet auf neue Nachrichten
  • Vorteile: Echtzeit-Updates, weniger Ressourcenverbrauch
  • Nachteile: Komplexität, Server-Ressourcenverbrauch, Datenlatenz
WebSockets
  • Voll-duplex-Kommunikation zwischen Client und Server
  • Beispiel: Echtzeit-Chat, Online-Spiele
  • Vorteile: Echtzeit-Updates, bidirektionale Kommunikation, minimale Latenz
  • Nachteile: Komplexität, Server-Ressourcenverbrauch
Technik Latenz Ressourcenverbrauch Komplexität
Polling Hoch Hoch Niedrig
Long Polling Mittel Mittel Mittel
WebSockets Niedrig Niedrig Hoch

Optimistische Updates

  • UI wird sofort aktualisiert, bevor Serverantwort eintrifft.
    • Annahme: Serverantwort wird erfolgreich sein.
  • Beispiel: Social Media App, die einen neuen Kommentar sofort anzeigt.
  • Vorteile: Schnelle Reaktion, bessere Benutzererfahrung.
  • Nachteile: Risiko von Inkonsistenzen, zusätzliche Logik erforderlich.

Sicherheit

Datenschutz
  • Schutz personenbezogener Daten
  • Einhaltung von Datenschutzgesetzen (z.B. DSGVO)
  • Beispiel: Anonymisierung von Daten, Einwilligung des Benutzers
  • Maßnahmen: Datenverschlüsselung, Zugriffskontrollen, regelmäßige Audits
Verschlüsselung
  • Schutz von Daten vor unbefugtem Zugriff
  • Data at Rest: Verschlüsselung von gespeicherten Daten
  • Data in Transit: Verschlüsselung von Daten während der Übertragung
    • Beispiel: HTTPS, TLS, AES
  • Best Practices: Verwendung starker Algorithmen, regelmäßige Schlüsselrotation
Authentifizierung
  • Überprüfung der Identität eines Benutzers
  • Beispiel: Benutzername und Passwort, OAuth, JWT
  • Methoden:
    • Passwort: Einfache, aber anfällig bei schwachen Passwörtern
    • Biometrisch: Hohe Sicherheit, aber Datenschutzbedenken
    • Multifaktor: hohe Sicherheit, geringerer Komfort

Fortgeschrittene Konzepte in React

DOM (Document Object Model)
  • Programmierschnitstelle für Webdokumente
  • Strukturierte Darstellung von HTML-Dokumenten (DOM-Baum)
  • Ermöglicht Manipulation von HTML-Elementen, Attributen und Inhalten
  • Beispiel: Ändern des Textinhalts eines Elements, Hinzufügen von Klassen
  • Vorteile: Dynamische Webseiten, Interaktivität
  • Nachteile: Direktes Manipulieren des DOM kann zu Performance-Problemen führen
Virtual DOM
  • In Memory-Repräsentation des echten DOM
  • React verwendet Virtual DOM, um Änderungen effizient zu verwalten
  • Änderungen werden zuerst im Virtual DOM vorgenommen und dann mit dem echten DOM synchronisiert
    1. Diffing-Algorithmus vergleicht Virtual DOM mit echtem DOM
    2. Nur die geänderten Teile werden aktualisiert
  • Vorteile: Höhere Performance, weniger direkte DOM-Manipulation
  • Nachteile: Zusätzliche Abstraktion, Lernkurve für Entwickler
CSR (Client-Side Rendering)
  • Rendering von Webseiten im Browser des Clients
    1. Minimale HTML-Struktur wird vom Server gesendet
    2. JavaScript lädt benötigte Daten nach
    3. JS rendert die Seite im Browser
  • Beispiel: React, Angular, Vue.js
  • Vorteile: Interaktive Benutzeroberflächen, schnelle Navigation, schnelleres Rendering nach dem ersten Laden, Geringe Serverlast
  • Nachteile: Erster Seitenaufruf kann langsam sein, SEO-Probleme, JavaScript-Abhängigkeit
ServerBrowserUserServerBrowserUserÖffnet WebseiteAnforderung: Minimal HTML + JSAntwort: Minimal HTML + JSFührt JavaScript ausAnforderung: Zusätzliche DatenAntwort: DatenRendert SeiteAnzeige der gerenderten Seite
SSR (Server-Side Rendering)
  • Rendering von Webseiten auf dem Server
    1. Benutzer fordert eine Seite an
    2. Server generiert HTML-Seite
    3. Vollständige HTML-Seite wird an den Client gesendet
    4. Browser rendert die Seite direkt
  • Vollständige HTML-Seite wird an den Client gesendet
  • Beispiel: Next.js, PHP, Flask
  • Vorteile: Schnellerer erster Seitenaufruf, bessere SEO, weniger JavaScript-Abhängigkeit, "einfachere Sicherheit"
  • Nachteile: Höhere Serverlast, langsame Interaktionen, komplexere Implementierung
ServerBrowserUserServerBrowserUserÖffnet WebseiteAnforderung: Vollständige SeiteAntwort: Gerenderte HTMLAnzeige der Seite
Kriterium CSR SSR
Initial Load Langsam Schnell
SEO Herausforderung Besser
Serverlast Niedrig Hoch
Benutzerinteraktion Flüssig Langsam
JavaScript-Abhängigkeit Hoch Niedrig

React State

State
  • Daten, die sich im Laufe der Zeit ändern können
  • Eigenschaften:
    • lokal & komponentenspezifisch
    • verursacht Neurendering der Komponente
    • bleibt zwischen Renderzyklen erhalten
  • Beispiel: Eingabewerte in einem Formular, Sichtbarkeit eines Modals
Props
  • Eigenschaften, die von einer übergeordneten Komponente an eine untergeordnete Komponente weitergegeben werden
  • Eigenschaften:
    • unveränderlich (immutable)
    • dienen zur Konfiguration von Komponenten
    • Beispiel: Titel eines Modals, Callback-Funktion für einen Button

State in Funktionskomponenten:

import React, { useState } from 'react';

function Counter() {
  // useState-Hook initialisiert den State 'count' mit dem Wert 0
  // setCount ist die Funktion, um den State zu aktualisieren
  const [count, setCount] = useState(0);

  return (
    <div>
      {/* Anzeige des aktuellen Zählerwerts */}
      <p>Aktueller Zähler: {count}</p>
      {/* Button, der den Zählerwert bei jedem Klick um 1 erhöht */}
      <button onClick={() => setCount(count + 1)}>Zähler erhöhen</button>
    </div>
  );
}

State-Updates & Batching:

  • Asynchrone Updates: State-Update wird nicht sofort angewendet
  • Batching: Mehrere State-Updates werden zusammengefasst
  • Beispiel: Mehrere setState-Aufrufe in einem Event-Handler
import React, { useState } from 'react';
function Counter() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    // Erster setCount-Aufruf mit dem aktuellen Wert von count (0)
    // React plant dieses Update, führt es aber nicht sofort aus
    setCount(count + 1);
    
    // Zweiter setCount-Aufruf verwendet immer noch den gleichen aktuellen Wert (0)
    // Dieser zweite Aufruf überschreibt den ersten in der Warteschlange
    setCount(count + 2);
    
    // Zu diesem Zeitpunkt ist count immer noch 0, da die Updates noch nicht angewendet wurden
    // React batcht (gruppiert) diese Updates und führt sie nach dem Event-Handler aus
    
    // Das Endergebnis wird 2 sein (0+2), nicht 3, da beide Updates den gleichen Ausgangswert verwenden
    
    // Um Updates sequentiell zu verarbeiten, würde man eine funktionale Form verwenden:
    // setCount(prevCount => prevCount + 1);
    // setCount(prevCount => prevCount + 2);
    // Das Ergebnis wäre dann 3
  };

  return (
    <div>
      <p>Aktueller Zähler: {count}</p>
      <button onClick={handleClick}>Zähler erhöhen</button>
    </div>
  );
}

Komplexe State-Objekte:

  • Objekte oder Arrays: State kann komplexe Datenstrukturen enthalten
  • Immutability: State-Objekte sollten nicht direkt verändert werden
import React, { useState } from 'react';
function UserProfile() {
  // Initialisiere einen komplexen State mit einem Objekt
  // Das Objekt enthält zwei Eigenschaften: name und age
  const [user, setUser] = useState({ name: 'Max', age: 25 });

  const updateName = (newName) => {
    // Verwende die funktionale Form von setState für Zugriff auf vorherigen State
    setUser((prevUser) => {
      // Wichtig: Erstelle ein neues Objekt mit dem Spread-Operator (...)
      // Dies sorgt für Immutability - der vorherige State wird nicht mutiert
      // ...prevUser kopiert alle bestehenden Eigenschaften
      // name: newName überschreibt nur die name-Eigenschaft
      return { ...prevUser, name: newName };
    });
    
    // FALSCH wäre: user.name = newName; setUser(user);
    // Das würde den originalen State direkt mutieren
  };

  return (
    <div>
      {/* Zugriff auf Eigenschaften des State-Objekts mit der Punktnotation */}
      <p>Name: {user.name}</p>
      <p>Alter: {user.age}</p>
      {/* Bei Klick wird updateName aufgerufen und der Name auf 'Anna' gesetzt */}
      <button onClick={() => updateName('Anna')}>Namen ändern</button>
    </div>
  );
}

Component Lifecycle

Klassische Lifecycle-Methoden:

  • Mounting: constructor, render, componentDidMount
  • Updating: render, componentDidUpdate
  • Unmounting: componentWillUnmount

Hooks Lifecycle:

useEffect
  • Hook für Nebenwirkungen (Side Effects)
  • Beispiel: Datenabruf, DOM-Manipulation, Timer
  • Syntax: useEffect(() => { /* Effekt */ }, [Abhängigkeiten])
  • Abhängigkeiten: Array von Werten, die den Effekt auslösen
  • Ausführungszeitpunkt:
    • Nach dem Rendern der Komponente (ohne Abhängigkeiten)
    • Bei Änderungen der Abhängigkeiten
    • Einmalig beim Mounten (leeres Abhängigkeits-Array)
  • Cleanup-Funktion: Verhindert Speicherlecks, wird vor dem nächsten Effekt oder beim Unmounten aufgerufen
import React, { useState, useEffect } from 'react';
function Timer() {
  const [seconds, setSeconds] = useState(0);

  // useEffect wird nach dem ersten Rendern und bei jeder Aktualisierung ausgeführt
  useEffect(() => {
    // setInterval startet einen Timer, der alle 1000ms (1 Sekunde) setSeconds aufruft
    const interval = setInterval(() => {
      setSeconds((prevSeconds) => prevSeconds + 1);
    }, 1000);

    // Cleanup-Funktion: stoppt den Timer, wenn die Komponente unmontiert wird
    return () => clearInterval(interval);
  }, []); // Leeres Array bedeutet, dass der Effekt nur einmal beim Mounten ausgeführt wird

  return <p>Timer: {seconds} Sekunden</p>;
}

Weitere Hooks:

  • useLayoutEffect: Synchroner Effekt, wird vor dem Browser-Painting ausgeführt
  • useMemo: Memoisiert Werte, um unnötige Berechnungen zu vermeiden
  • useCallback: Memoisiert Funktionen, um unnötige Neudefinitionen zu vermeiden
  • useRef: Referenz auf DOM-Elemente oder Werte, die nicht neu gerendert werden sollen
import React, { useState, useLayoutEffect, useCallback, useMemo, useRef } from 'react';

function ExampleComponent() {
  const [count, setCount] = useState(0);
  const inputRef = useRef(null);

  // useLayoutEffect wird synchron nach dem Rendern ausgeführt
  useLayoutEffect(() => {
    // Fokus auf das Eingabefeld setzen
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  // useCallback memoisiert die Funktion, um unnötige Neudefinitionen zu vermeiden
  const increment = useCallback(() => {
    setCount((prevCount) => prevCount + 1);
  }, []);

  // useMemo memoisiert den Wert, um unnötige Berechnungen zu vermeiden
  const squaredCount = useMemo(() => count * count, [count]);

  return (
    <div>
      <input ref={inputRef} type="text" />
      <p>Aktueller Zähler: {count}</p>
      <p>Quadrat des Zählers: {squaredCount}</p>
      <button onClick={increment}>Zähler erhöhen</button>
    </div>
  );
}

Rendering-Verhalten:

  • Wann wird eine Komponente neu gerendert?
    • State-Änderung
    • Props-Änderung
    • Parent-Komponente wird neu gerendert
  • Optimierung:
    • React.memo: Verhindert unnötige Neurenderings von Funktionskomponenten
    • shouldComponentUpdate: Verhindert unnötige Neurenderings von Klassenkomponenten

State & Lifecycle Best Practices:

  • State minimal halten: Nur notwendige Daten speichern
  • Berechnen statt speichern: Berechnete Werte direkt im Rendern ableiten
  • Seiteneffekte isolieren: Klare Trennung von Logik und UI
  • Cleanup-Funktionen verwenden: Ressourcen freigeben, Listener entfernen
  • Abhängigkeits-Array korrekt verwenden: Alle Abhängigkeiten angeben, um unerwartete Fehler zu vermeiden
State Management in React
  • Lokales State-Management: useState, useReducer
  • Wann globales State-Management verwenden?
    • State wird von mehreren Komponenten benötigt
    • State ist komplex und erfordert zusätzliche Logik
  • State-Management-Bibliotheken:
    • Context API: Einfache globale State-Verwaltung für kleine Anwendungen
    • Redux: Strukturierte State-Verwaltung für komplexe Anwendungen
    • MobX: Reaktive State-Verwaltung mit weniger Boilerplate-Code
    • Recoil: Atomare State-Verwaltung mit Hooks
    • Zustand: Minimalistische State-Verwaltung mit Hooks
Ansatz Einfachheit Performance Skalierbarkeit Community
Context API Hoch Mittel Niedrig Mittel
Redux Mittel Hoch Hoch Hoch
MobX Mittel Hoch Mittel Mittel
Recoil Hoch Hoch Mittel Niedrig
Zustand Hoch Hoch Mittel Niedrig

Best Practices für State Management:

  • Strukturierung des States: Normalisierung & Modularität
  • Umgang mit asynchronen Aktionen: Middleware (Redux Thunk, Redux Saga)
  • Vermeidung häufiger Fehler:
    • Unnötige Neurenderings
    • Tiefe Verschachtelung