06 Softwarearchitektur Teil 2: Architekturmuster und Anwendung
January 2026 (5857 Words, 33 Minutes)
1. Einführung: Von Verständnis zur Anwendung
In Teil 1 haben wir erkundet:
- Warum Architektur wichtig ist — die Brücke zwischen agiler Entwicklung und nachhaltigen Systemen
- Architektonische Designentscheidungen — wie nicht-funktionale Anforderungen die Systemstruktur formen
- Das 4+1-Sichtenmodell — Systeme aus mehreren Perspektiven verstehen (Logisch, Prozess, Entwicklung, Physisch und Szenarien)
Jetzt bewegen wir uns vom Verstehen von Architektur zur Anwendung. Diese Vorlesung führt Architekturmuster ein — bewährte Lösungen, die Tausende von Systemen validiert haben — und zeigt, wie man sie auf Ihr Road Profile Viewer-Projekt anwendet.
1.1 Das Versprechen der Muster
Wenn ein Entwickler sagt “wir verwenden MVC”, verstehen andere Entwickler sofort:
- Wie Verantwortlichkeiten aufgeteilt sind
- Wo Geschäftslogik vs. UI-Code zu finden ist
- Wie neue Features hinzugefügt werden können, ohne bestehende zu beschädigen
Muster sind nicht nur Code-Strukturen — sie sind gemeinsames Vokabular, das effiziente Kommunikation und vorhersagbares Systemverhalten ermöglicht.
2. Lernziele
Am Ende dieser Vorlesung werden Sie in der Lage sein:
- Zu erklären, was Architekturmuster sind und warum sie als bewährte Lösungen für wiederkehrende Probleme wertvoll sind
- MVC und Schichtenarchitektur-Muster anzuwenden, um interaktive Anwendungen mit angemessener Trennung der Verantwortlichkeiten zu strukturieren
- Zu bewerten, wann verteilte Muster wie Client-Server und Microservices basierend auf Projektanforderungen eingesetzt werden sollten
- Eine weiterentwickelte Architektur für den Road Profile Viewer zu entwerfen, die Einfachheit mit zukünftiger Skalierbarkeit ausbalanciert
3. Architekturmuster: Bewährte Lösungen für wiederkehrende Probleme
3.1 Was ist ein Architekturmuster?
Ein Architekturmuster ist eine Beschreibung einer Systemorganisation, die erfolgreich in verschiedenen Softwaresystemen verwendet wurde.
Muster sind keine Erfindungen — sie sind Entdeckungen. Architekten bemerkten, dass bestimmte Strukturen in erfolgreichen Systemen immer wieder auftauchen und dokumentierten sie, damit andere davon profitieren können.
Warum Muster verwenden?
- Bewährt: Tausende von Systemen haben diese Ansätze validiert
- Kommunizierbar: “Wir verwenden MVC” vermittelt anderen Entwicklern sofort die Struktur
- Analysierbar: Bekannte Muster haben bekannte Kompromisse
- Wiederverwendbar: Framework-Unterstützung, Dokumentation und Community-Wissen
Musterkategorien:
| Kategorie | Zweck | Beispielmuster |
|---|---|---|
| Strukturierung interaktiver Systeme | Organisation von Mensch-Computer-Interaktionen | MVC, MVP, MVVM |
| Organisation der Systemstruktur | Organisation von Komponenten und Objekten | Schichten, Pipes und Filter, Repository |
| Unterstützung verteilter Systeme | Ermöglichung verteilter Ressourcen und Dienste | Client-Server, Microservices, Event-Driven |
3.2 Kategorie 1: Strukturierung interaktiver Systeme
3.2.1 Model-View-Controller (MVC)
MVC trennt eine Anwendung in drei miteinander verbundene Komponenten:
| Komponente | Verantwortlichkeit | Road Profile Viewer Beispiel |
|---|---|---|
| Model | Daten und Geschäftslogik; weiß nichts über UI | RoadProfile-Klasse, GeometryCalculator |
| View | Zeigt Daten dem Benutzer an; empfängt Benutzereingaben | Dash-Layout, Plotly-Diagramme |
| Controller | Verarbeitet Benutzereingaben; aktualisiert Model und View | Dash-Callbacks |
Warum MVC?
- Trennung der Verantwortlichkeiten: UI-Designer können an Views arbeiten, während Entwickler an Models arbeiten
- Testbarkeit: Models können ohne UI getestet werden
- Flexibilität: Dasselbe Model kann mehrere Views haben (Web, Mobile, CLI)
3.2.2 MVC in Web-Frameworks
Die meisten Web-Frameworks implementieren MVC (oder Varianten wie MVP, MVVM):
Django (Python):
Terminologie-Hinweis: Django verwendet MTV (Model-Template-View) anstelle von MVC. Das sorgt für Verwirrung, da Djangos “View” eigentlich der Controller in MVC-Terminologie ist:
MVC-Begriff Django-Begriff Django-Datei Model Model models.pyView (Präsentation) Template templates/*.htmlController (Logik) View views.py
# models.py — Model (gleich in MVC und MTV)
class RoadProfile(models.Model):
name = models.CharField(max_length=100)
data = models.JSONField()
# views.py — Django nennt das "View", aber es fungiert als MVC Controller
# Es verarbeitet HTTP-Anfragen und koordiniert Model ↔ Template
def profile_detail(request, profile_id):
profile = RoadProfile.objects.get(id=profile_id)
return render(request, 'profile.html', {'profile': profile})
# templates/profile.html — Django "Template" = MVC View (Präsentation)
<h1></h1>
<div id="chart"></div>
Flask (Python):
# models.py
@dataclass
class RoadProfile:
id: str
name: str
coordinates: list[tuple[float, float]]
# routes.py (Controller)
@app.route('/profiles/<profile_id>')
def show_profile(profile_id):
profile = ProfileRepository.get(profile_id)
return render_template('profile.html', profile=profile)
# templates/profile.html (View)
# ... Jinja2-Template
3.2.3 Road Profile Viewer: MVC anwenden
Lassen Sie uns den Road Profile Viewer mit MVC refaktorieren:
Model (models/profile.py):
from pydantic import BaseModel
class RoadProfile(BaseModel):
"""Domain-Modell - weiß nichts über UI oder Speicherung."""
id: str
name: str
x_coordinates: list[float]
y_coordinates: list[float]
def get_elevation_at(self, x: float) -> float:
"""Geschäftslogik lebt im Model."""
# Interpoliere Höhe an gegebener x-Koordinate
...
def max_slope(self) -> float:
"""Berechne maximale Steigung - reine Domain-Logik."""
...
View (presentation/charts.py):
import plotly.graph_objects as go
from models.profile import RoadProfile
def create_profile_figure(profile: RoadProfile) -> go.Figure:
"""Reine Visualisierung - keine Geschäftslogik."""
return go.Figure(
data=go.Scatter(
x=profile.x_coordinates,
y=profile.y_coordinates,
mode='lines',
name=profile.name
),
layout=go.Layout(
title=f"Road Profile: {profile.name}",
xaxis_title="Distance (m)",
yaxis_title="Elevation (m)"
)
)
Controller (presentation/callbacks.py):
from dash import callback, Input, Output
from repositories.profile_repository import ProfileRepository
from presentation.charts import create_profile_figure
repository = ProfileRepository()
@callback(
Output('profile-chart', 'figure'),
Input('profile-dropdown', 'value')
)
def update_chart(profile_id: str):
"""Controller: koordiniert Model und View."""
if not profile_id:
return go.Figure() # Leere Figur
# Hole Daten vom Model
profile = repository.get(profile_id)
# Erstelle View-Repräsentation
return create_profile_figure(profile)
Erreichte Vorteile:
RoadProfilekann ohne Dash getestet werden- Diagramme können in anderen Kontexten wiederverwendet werden (Export nach PDF, etc.)
- Callbacks sind schlank — nur Koordination
3.3 Kategorie 2: Organisation der Systemstruktur
3.3.1 Schichtenarchitektur
Schichtenarchitektur organisiert Code in horizontale Schichten, wobei jede Schicht nur von der darunterliegenden Schicht abhängt.
Schlüsselregeln:
- Nur Abhängigkeiten nach unten: Präsentation hängt von Geschäftslogik ab; Geschäftslogik hängt von Daten ab
- Keine Schichten überspringen: Präsentation sollte Daten nicht direkt aufrufen
- Schichten sind kohäsiv: Aller UI-Code in Präsentation, alle Domain-Logik in Geschäftslogik
| Schicht | Verantwortlichkeit | Typische Komponenten |
|---|---|---|
| Präsentation | Benutzerinteraktion verarbeiten, Daten anzeigen | Webseiten, API-Endpunkte, CLI-Befehle |
| Geschäftslogik | Domain-Logik, Validierung, Berechnungen | Services, Domain-Modelle, Validatoren |
| Datenzugriff | Daten persistieren und abrufen | Repositories, ORM-Modelle, Cache-Clients |
3.3.2 Netflix: Ein Beispiel für Schichtenarchitektur
Die Architektur von Netflix demonstriert Schichtenprinzipien im großen Maßstab:
Weiterführende Lektüre: Der Netflix Technology Blog bietet detaillierte Einblicke in ihre Architektur. Besonders relevant:
- Optimizing the Netflix API — wie sich ihr API Gateway (Zuul) entwickelt hat
- Netflix Conductor: A Microservices Orchestrator — Koordination von Services in der Geschäftslogikschicht
- Evolution of the Netflix Data Pipeline — wie ihre Datenschicht Petabytes an Daten verarbeitet
Netflix veröffentlicht auch viele ihrer Architektur-Tools als Open Source unter netflix.github.io, einschließlich Zuul (API Gateway), Eureka (Service Discovery) und Hystrix (Fehlertoleranz).
Jede Schicht kann unabhängig skaliert werden:
- Mehr Empfehlungskapazität benötigt? Skalieren Sie nur diesen Service
- Datenbank langsam? Fügen Sie Read-Replicas hinzu, ohne die Geschäftslogik zu ändern
- Neue TV-Plattform? Fügen Sie Präsentations-Client hinzu, ohne das Backend zu berühren
3.3.3 Road Profile Viewer: Schichten anwenden
Aktuelle Struktur (vermischte Verantwortlichkeiten):
# Alles in einer Datei - schwer zu warten
app = Dash(__name__)
@app.callback(...)
def update_chart(profile_id):
# UI-Logik
if not profile_id:
return {}
# Datenzugriff
conn = sqlite3.connect('profiles.db')
cursor = conn.execute('SELECT * FROM profiles WHERE id = ?', (profile_id,))
row = cursor.fetchone()
# Geschäftslogik
x_coords = json.loads(row[1])
y_coords = json.loads(row[2])
max_slope = calculate_max_slope(x_coords, y_coords)
# Zurück zur UI
return create_figure(x_coords, y_coords, max_slope)
Refaktoriert mit Schichten:
# data_access/repositories.py (Datenschicht)
class ProfileRepository:
def __init__(self, db_path: str = 'profiles.db'):
self.db_path = db_path
def get(self, profile_id: str) -> Profile | None:
conn = sqlite3.connect(self.db_path)
cursor = conn.execute(
'SELECT * FROM profiles WHERE id = ?',
(profile_id,)
)
row = cursor.fetchone()
if row:
return Profile.from_row(row)
return None
# business/services.py (Geschäftslogikschicht)
class ProfileService:
def __init__(self, repository: ProfileRepository):
self.repository = repository
def get_profile_with_analysis(self, profile_id: str) -> dict:
profile = self.repository.get(profile_id)
if not profile:
raise ProfileNotFoundError(profile_id)
return {
'profile': profile,
'max_slope': self._calculate_max_slope(profile),
'total_distance': self._calculate_distance(profile),
}
def _calculate_max_slope(self, profile: Profile) -> float:
# Geschäftslogik hier
...
# presentation/callbacks.py (Präsentationsschicht)
service = ProfileService(ProfileRepository())
@app.callback(...)
def update_chart(profile_id):
if not profile_id:
return empty_figure()
try:
data = service.get_profile_with_analysis(profile_id)
return create_figure(data)
except ProfileNotFoundError:
return error_figure("Profile not found")
Vorteile:
- Testbar: Mock
ProfileRepository, umProfileServicezu testen - Austauschbar: SQLite durch PostgreSQL ersetzen, indem nur
ProfileRepositorygeändert wird - Verständlich: Jede Datei hat einen klaren Zweck
3.4 Kategorie 3: Verteilte Systeme
3.4.1 Client-Server-Architektur
Das grundlegendste verteilte Muster: Clients fordern Dienste von Servern an.
graph LR
C1[Client 1<br>Web Browser] --> S[Server<br>Web Application]
C2[Client 2<br>Mobile App] --> S
C3[Client 3<br>Desktop App] --> S
S --> DB[(Database)]
Eigenschaften:
- Clients: Initiieren Anfragen, zeigen Ergebnisse an, verarbeiten Benutzereingaben
- Server: Verarbeiten Anfragen, verwalten Daten, setzen Geschäftsregeln durch
- Protokoll: Normalerweise HTTP/HTTPS für Webanwendungen
Road Profile Viewer ist bereits Client-Server:
- Client: Webbrowser, der JavaScript/Dash-Frontend ausführt
- Server: Python Dash/Flask-Prozess, der Callbacks verarbeitet
- Protokoll: HTTP-Anfragen für Seitenladevorgänge, WebSockets für Live-Updates
3.4.2 Microservices-Architektur
Microservices zerlegen ein System in kleine, unabhängig deploybare Dienste.
graph TB
API[API Gateway]
API --> US[User Service]
API --> PS[Profile Service]
API --> AS[Analysis Service]
US --> UDB[(User DB)]
PS --> PDB[(Profile DB)]
AS --> ADB[(Analysis Cache)]
vs. Monolith:
| Aspekt | Monolith | Microservices |
|---|---|---|
| Deployment | Alles oder nichts; gesamte App wird redeployed | Unabhängig; ein Service nach dem anderen deployen |
| Skalierung | Alles zusammen skalieren | Einzelne Services basierend auf Bedarf skalieren |
| Technologie | Eine Sprache/Framework für alles | Jeder Service kann das beste Werkzeug für den Job verwenden |
| Ausfall | Ein Bug kann das gesamte System zum Absturz bringen | Ausfälle sind auf einen Service isoliert |
| Komplexität | Einfaches Deployment, komplexe Codebasis | Komplexes Deployment, einfachere Codebasen |
Amazons Transformation:
Im Jahr 2002 verordnete Jeff Bezos, dass alle Teams ihre Funktionalität über Service-Schnittstellen bereitstellen müssen. Diese “Zwei-Pizza-Team”-Regel (Teams, die klein genug sind, um mit zwei Pizzen gefüttert zu werden) führte zu Amazon Web Services — ursprünglich interne Infrastruktur, jetzt ein über 80 Milliarden Dollar schweres Geschäft.
Wann Microservices verwenden:
- Große Teams (können sich nicht effektiv auf einer Codebasis koordinieren)
- Unterschiedliche Skalierungsanforderungen (manche Features haben 100x mehr Traffic)
- Technologische Vielfalt (manche Probleme brauchen andere Sprachen)
- Organisatorische Unabhängigkeit (Teams brauchen Autonomie)
Wann KEINE Microservices verwenden:
- Kleine Teams (Koordinationsaufwand überwiegt die Vorteile)
- Frühe Produktphase (schnelles Iterieren nötig)
- Einfache Domänen (zusätzliche Komplexität lohnt sich nicht)
Road Profile Viewer: Ein Monolith ist die richtige Wahl. Sie haben 3 Entwickler und eine einfache Domäne. Microservices würden erhebliche Komplexität ohne Nutzen hinzufügen.
3.4.3 AWS und Cloud-Architektur
Cloud-Plattformen wie AWS bieten Bausteine für verteilte Architekturen:
Weiterführende Lektüre: AWS bietet umfangreiche Dokumentation und Architekturanleitungen:
- AWS Well-Architected Framework — Best Practices für den Aufbau sicherer, leistungsfähiger und kosteneffizienter Systeme
- AWS Architecture Center — Referenzarchitekturen und Diagramme für gängige Anwendungsfälle
- What is Amazon CloudFront? — CDN-Service-Dokumentation
- Elastic Load Balancing — Verteilung von Traffic auf mehrere Ziele
- Amazon ECS on AWS Fargate — Serverless-Container-Deployment
Für Studierende: AWS bietet AWS Educate mit kostenlosen Credits und Lernressourcen.
Für Road Profile Viewer Deployment (Zukunft):
Ein einfaches Cloud-Deployment könnte verwenden:
- Render.com oder Railway.app (einfache PaaS, kostenlose Stufe verfügbar)
- Supabase (verwaltetes PostgreSQL mit kostenloser Stufe)
Sie brauchen keine AWS-Komplexität für ein Studentenprojekt!
4. Architektur auf den Road Profile Viewer anwenden
4.1 Aktueller Zustand: Vom Monolithen zu Modulen
In Vorlesung 4: Refactoring haben wir eine monolithische main.py in Module aufgeteilt:
road-profile-viewer/
├── src/
│ ├── geometry.py # Ray-Intersection-Berechnungen
│ ├── road.py # Profil-Generierung
│ ├── visualization.py # Diagramm-Erstellung
│ └── main.py # Anwendungs-Einstiegspunkt
└── tests/
Das war ein guter Anfang! Aber wir können mit einer ordentlichen Schichtenarchitektur weitergehen.
4.2 Vorgeschlagene weiterentwickelte Architektur
road-profile-viewer/
├── src/
│ ├── __init__.py
│ │
│ ├── domain/ # Kern-Geschäftskonzepte
│ │ ├── __init__.py
│ │ ├── models.py # RoadProfile, Measurement
│ │ └── services.py # GeometryCalculator, ProfileAnalyzer
│ │
│ ├── infrastructure/ # Externe Systeme
│ │ ├── __init__.py
│ │ ├── database.py # Datenbankverbindung
│ │ └── repositories.py # ProfileRepository
│ │
│ ├── presentation/ # Benutzeroberfläche
│ │ ├── __init__.py
│ │ ├── app.py # Dash-Anwendungs-Setup
│ │ ├── layouts.py # Seitenlayouts
│ │ ├── callbacks.py # Dash-Callbacks (Controller)
│ │ └── charts.py # Diagramm-Builder (Views)
│ │
│ ├── api/ # Optional: REST API
│ │ ├── __init__.py
│ │ ├── routes.py # FastAPI-Endpunkte
│ │ └── schemas.py # API-Request/Response-Modelle
│ │
│ └── main.py # Einstiegspunkt
│
├── tests/
│ ├── domain/
│ │ ├── test_models.py
│ │ └── test_services.py
│ ├── infrastructure/
│ │ └── test_repositories.py
│ └── presentation/
│ └── test_callbacks.py
│
├── pyproject.toml
└── README.md
4.3 Hinzufügen einer REST-API-Schicht
Warum eine API hinzufügen, wenn Dash bereits funktioniert?
- Mehrere Frontends: Dasselbe Backend für Web, Mobile, CLI
- Testen: APIs sind einfacher zu testen als UI-Callbacks
- Integration: Andere Systeme können auf Ihre Daten zugreifen
Einfache FastAPI-Ergänzung (api/routes.py):
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from domain.models import RoadProfile
from infrastructure.repositories import ProfileRepository
app = FastAPI(title="Road Profile Viewer API")
repository = ProfileRepository()
class ProfileResponse(BaseModel):
id: str
name: str
x_coordinates: list[float]
y_coordinates: list[float]
max_slope: float | None = None
@app.get("/api/profiles")
def list_profiles() -> list[ProfileResponse]:
"""Liste alle verfügbaren Profile auf."""
profiles = repository.list_all()
return [ProfileResponse(**p.dict()) for p in profiles]
@app.get("/api/profiles/{profile_id}")
def get_profile(profile_id: str) -> ProfileResponse:
"""Hole ein spezifisches Profil nach ID."""
profile = repository.get(profile_id)
if not profile:
raise HTTPException(status_code=404, detail="Profile not found")
return ProfileResponse(**profile.dict())
@app.post("/api/profiles")
def create_profile(profile: RoadProfile) -> ProfileResponse:
"""Erstelle ein neues Profil."""
saved = repository.save(profile)
return ProfileResponse(**saved.dict())
Jetzt können sowohl Dash als auch die API dieselben ProfileRepository- und RoadProfile-Modelle verwenden.
4.4 Zukünftige Skalierbarkeit berücksichtigen
Falls Sie den Road Profile Viewer jemals skalieren müssen:
- Datenbank: SQLite durch PostgreSQL ersetzen (nur
infrastructure/database.pyändern) - Caching: Redis-Cache in
infrastructure/cache.pyhinzufügen (keine Domain-Änderungen) - Deployment: Mit Docker containerisieren, in die Cloud deployen
- Lastverteilung: Mehrere Instanzen hinter einem Load Balancer hinzufügen
Die Schichtenarchitektur macht jede dieser Änderungen lokalisiert. Sie müssen nicht die gesamte Anwendung neu schreiben.
Aber denken Sie daran: Vorzeitige Optimierung ist die Wurzel allen Übels. Bauen Sie zuerst die einfache Version. Skalieren Sie, wenn Sie Beweise haben, dass Sie es brauchen.
5. Architektur und Agile: Zusammenarbeit
5.1 Der scheinbare Konflikt
Einige Entwickler glauben, Agile und Architektur stünden im Konflikt:
“Agile bedeutet kein Vorab-Design — einfach coden und refaktorieren!”
“Architektur erfordert Monate der Planung, bevor man Code schreibt!”
Beide Extreme sind falsch.
5.2 Emergente Architektur in Scrum
Der agile Ansatz zur Architektur ist emergentes Design mit beabsichtigter Struktur:
- Genug Entscheidungen zum Start treffen: Sprache, Framework, Grundstruktur wählen
- Funktionierende Software implementieren: Echte Features bauen, nicht hypothetische Infrastruktur
- Refaktorieren, wenn Muster entstehen: Bei Duplikation oder Komplexität umstrukturieren
- Entscheidungen regelmäßig überprüfen: Sprint-Retrospektiven können architektonische Schulden ansprechen
In Scrum:
- Sprint 0: Initiale architektonische Entscheidungen (optional, aber üblich)
- Backlog Refinement: Stories mit architektonischen Implikationen identifizieren
- Technische Spikes: Zeitlich begrenzte Recherche für architektonische Fragen
- Definition of Done: Architektonische Qualität einschließen (z.B. “Code folgt Schichten-Konventionen”)
5.3 Technische Spikes für Architekturentscheidungen
Ein technischer Spike ist eine zeitlich begrenzte Untersuchung, um Risiko oder Unsicherheit zu reduzieren.
Beispiel Spike-Story:
Als Entwickler möchte ich PostgreSQL vs SQLite für unsere Datenbank untersuchen damit wir eine fundierte Entscheidung über die Datenspeicherung treffen können
Akzeptanzkriterien:
- Vor- und Nachteile jeder Option dokumentieren
- Proof-of-Concept mit PostgreSQL erstellen
- Performance mit realistischem Datenvolumen messen
- Empfehlung dem Team präsentieren
Zeitrahmen: Maximal 2 Tage
Nach dem Spike kann das Team eine fundierte architektonische Entscheidung treffen, keine Vermutung.
6. Zusammenfassung
| Konzept | Kernpunkt | Road Profile Viewer Anwendung |
|---|---|---|
| Architekturmuster | Bewährte Lösungen für wiederkehrende Designprobleme | Muster wie MVC für vorhersagbare Struktur verwenden |
| MVC-Muster | Model (Daten), View (Anzeige), Controller (Koordination) trennen | Pydantic-Modelle, Diagramm-Builder, Dash-Callbacks |
| Schichtenarchitektur | Präsentation → Geschäftslogik → Datenzugriff | presentation/, domain/, infrastructure/ Pakete |
| Verteilte Muster | Client-Server, Microservices für Skalierung und Unabhängigkeit | Bereits Client-Server; vorerst monolithisch bleiben |
| Agile + Architektur | Emergentes Design mit beabsichtigter Struktur | Technische Spikes für unsichere Entscheidungen verwenden |
| Weiterentwickelte Struktur | domain/, infrastructure/, presentation/ Pakete | Klare Trennung ermöglicht zukünftiges Wachstum |
6.1 Wichtige Erkenntnisse
- Muster sind bewährte Lösungen — MVC, Schichtenarchitektur und Client-Server lösen wiederkehrende Probleme mit bekannten Kompromissen
- MVC trennt Verantwortlichkeiten — Model kennt Daten, View kennt Anzeige, Controller koordiniert
- Schichten schaffen Grenzen — Präsentation hängt von Geschäftslogik ab, die von Daten abhängt, niemals umgekehrt
- Microservices sind nicht immer die Antwort — Kleine Teams profitieren mehr von gut strukturierten Monolithen
- Architektur entwickelt sich mit Agile — Einfach anfangen, refaktorieren wenn Muster entstehen, Spikes für große Entscheidungen verwenden
- Struktur ermöglicht Veränderung — Gute Architektur macht zukünftige Änderungen lokalisiert und vorhersagbar
7. Reflexionsfragen
-
Musteridentifikation: Betrachten Sie Ihren aktuellen Road Profile Viewer-Code, können Sie identifizieren, welchem Muster (falls überhaupt) er derzeit folgt? Was müsste sich ändern, damit er klar MVC folgt?
-
Schichtenzuordnung: Listen Sie jede Python-Datei in Ihrem Road Profile Viewer-Projekt auf. Zu welcher Schicht (Präsentation, Geschäftslogik, Datenzugriff) gehört jede Datei? Gibt es Dateien, die mehrere Schichten vermischen?
-
Abhängigkeitsrichtung: Zeichnen Sie ein Diagramm, das zeigt, wie Ihre Python-Module sich gegenseitig importieren. Zeigen alle Abhängigkeiten “nach unten” (Präsentation → Geschäftslogik → Daten)? Falls nicht, welche Imports verletzen die Schichtung?
-
Framework-Passung: Sowohl Dash als auch Django implementieren MVC-ähnliche Muster. Wie passt Dashs Callback-System zur Controller-Rolle? Was ist mit Djangos View-Funktionen?
-
Skalierungsauslöser: Welches spezifische Ereignis oder welche Metrik würde Ihnen sagen, dass es Zeit ist, den Road Profile Viewer von SQLite auf PostgreSQL umzustellen? Schreiben Sie einen konkreten Schwellenwert (z.B. “Wenn täglich aktive Benutzer X überschreiten” oder “Wenn die Datenbankdateigröße Y MB überschreitet”).
-
Technischer Spike: Schreiben Sie eine 2-Tage-Spike-Story zur Untersuchung einer architektonischen Frage zum Road Profile Viewer (z.B. “Sollten wir eine REST API hinzufügen?” oder “Sollten wir async Callbacks verwenden?”).
8. Weiterführende Literatur
8.1 Bücher
- Ian Sommerville: Software Engineering (10. Auflage), Kapitel 6 — Quellmaterial für diese Vorlesung
- Robert C. Martin: Clean Architecture — Praktische Muster für wartbare Software
- Martin Fowler: Patterns of Enterprise Application Architecture — Umfassender Musterkatalog
- Sam Newman: Building Microservices — Wann und wie man Microservices verwendet
8.2 Artikel und Ressourcen
- Martin Fowler on MVC — Evolution von UI-Architekturmustern
- Martin Fowler on Microservices — Definitive Einführung in Microservices
- The Twelve-Factor App — Best Practices für Cloud-native Anwendungen
- Netflix Tech Blog — Praxisnahe Architektur-Fallstudien
- FastAPI Documentation — Modernes Python API-Framework
8.3 Werkzeuge
- Draw.io / diagrams.net — Kostenloses Diagramm-Tool für Architekturdiagramme
- Mermaid — Textbasierte Diagramme (in diesem Kurs verwendet)
- PlantUML — UML-Diagramme aus Text
- C4 Model — Hierarchische Architekturdiagramme (Context, Container, Component, Code)
9. Was kommt als Nächstes
Mit beiden Teilen dieser Vorlesung haben Sie jetzt ein vollständiges Verständnis von Softwarearchitektur:
Teil 1 behandelte:
- Warum Architektur wichtig ist und was architektonische Entscheidungen beeinflusst
- Das 4+1-Sichtenmodell zum Verständnis von Systemen aus mehreren Perspektiven
- Wie nicht-funktionale Anforderungen architektonische Entscheidungen prägen
Teil 2 behandelte:
- Architekturmuster (MVC, Schichten, Client-Server, Microservices)
- Anwendung von Mustern auf den Road Profile Viewer
- Wie Architektur und agile Entwicklung zusammenarbeiten
Auf den Road Profile Viewer anwenden:
- Identifizieren Sie Ihr aktuelles Muster: Welchem Muster folgt Ihr Code derzeit (falls überhaupt)?
- Ordnen Sie Ihre Schichten zu: Welcher Code ist Präsentation? Geschäftslogik? Datenzugriff?
- Refaktorieren Sie eine Schicht: Wählen Sie den unordentlichsten Bereich und wenden Sie ordentliche Trennung an
- Dokumentieren Sie Ihre Architektur: Erstellen Sie eine einfache
ARCHITECTURE.mdmit den vier Sichten - Erwägen Sie eine API: Würde eine REST API Ihrem Projekt nutzen?
Das Ziel ist nicht perfekte Architektur — es ist bewusste Architektur. Treffen Sie bewusste Entscheidungen über die Struktur, dokumentieren Sie sie und entwickeln Sie sie weiter, während Sie lernen.