Skip to content
IRC-Coding IRC-Coding
3-Schichten-Modell 3-Tier DTO Service Layer Repository

3-Schichten-Modell (3-Tier) einfach erklärt: UI, Business, Datenzugriff & Prüfungsfragen

3-Schichten/3-Tier Architektur: Präsentation, Geschäftslogik, Datenzugriff. Kernkomponenten, Vorteile/Nachteile, Praxisbeispiel, Lernstrategie und Prüfungsfragen.

S

schutzgeist

1 min read
3-Schichten-Modell (3-Tier) einfach erklärt: UI, Business, Datenzugriff & Prüfungsfragen

type: article

keywords:

  • 3-Schichten-Modell
  • 3-Tier Architektur
  • Präsentation Business Daten
  • DTO

canonicalURL: https://www.irc-coding.de/3-schichten-modell-3-tier-architektur

Softwarearchitektur: 3-Schichten-Modell / 3-Tier

Dieser Beitrag ist eine Begriffserklärung zum 3-Schichten-Modell (3-Tier) – inklusive Prüfungsfragen, Merkpunkte und Tags.

In a Nutshell

Das 3-Schichten-Modell trennt Software logisch in Präsentation, Geschäftslogik und Datenzugriff – für klare Zuständigkeiten, Wartbarkeit und Testbarkeit.

Kompakte Fachbeschreibung

Die drei Schichten:

  1. Präsentation (UI)
  2. Business Logic (Services/Use Cases)
  3. Persistence (Repository/ORM/DB)

Kommunikation erfolgt typischerweise von oben nach unten. Jede Schicht kennt nur die direkt darunterliegende. Das macht Systeme lose gekoppelt.

Prüfungsrelevante Stichpunkte

  • Trennung von Darstellung, Logik, Datenzugriff
  • Klare Verantwortlichkeiten
  • Bessere Testbarkeit/Wartbarkeit
  • Standard in Java/.NET/Webprojekten (IHK-relevant)
  • Komponenten austauschbar (UI-Wechsel)
  • Schichten sind Sicherheitsbarrieren
  • Modularisierung reduziert Folgekosten
  • Architektur muss dokumentiert werden

Kernkomponenten

  1. Präsentationsschicht
  2. Logikschicht
  3. Datenzugriffsschicht
  4. Schnittstellen zwischen Schichten
  5. Logging/Fehlerbehandlung
  6. Unit Tests in Business-Schicht
  7. DTOs
  8. Security Layer
  9. Persistenz (SQL/NoSQL)

Praxisbeispiel: Task-Verwaltung mit Python

Was zeigt dieses Beispiel? Ein einfaches Task-Management-System in Python, das strikt nach dem 3-Schichten-Modell aufgebaut ist. Daten fließen von der Präsentation (CLI) durch die Business-Schicht (Validierung, Regeln) bis zur Persistence-Schicht (SQLite-Repository).

Warum ist das ein gutes Lernbeispiel? Jede Schicht ist einzeln testbar: Die Business-Schicht braucht keine Datenbank, die Präsentation braucht kein SQLite, sie kommunizieren nur über saubere Methodenaufrufe.

import sqlite3
from dataclasses import dataclass
from typing import List, Optional

# === Schicht 3: Persistence (Datenzugriff) ===
@dataclass
class TaskDTO:
    id: int; title: str; description: str; status: str

class TaskRepository:
    def __init__(self, db_path="tasks.db"):
        self.db_path = db_path
        with sqlite3.connect(db_path) as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS tasks (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    title TEXT NOT NULL, description TEXT,
                    status TEXT DEFAULT 'open')""")

    def create(self, task: TaskDTO) -> TaskDTO:
        with sqlite3.connect(self.db_path) as conn:
            c = conn.execute("INSERT INTO tasks (title,description,status) VALUES (?,?,?)",
                (task.title, task.description, task.status))
            task.id = c.lastrowid
            return task

    def get_all(self) -> List[TaskDTO]:
        with sqlite3.connect(self.db_path) as conn:
            rows = conn.execute("SELECT * FROM tasks").fetchall()
            return [TaskDTO(*r) for r in rows]

    def get_by_id(self, tid: int) -> Optional[TaskDTO]:
        with sqlite3.connect(self.db_path) as conn:
            r = conn.execute("SELECT * FROM tasks WHERE id=?", (tid,)).fetchone()
            return TaskDTO(*r) if r else None

    def update(self, task: TaskDTO) -> TaskDTO:
        with sqlite3.connect(self.db_path) as conn:
            conn.execute("UPDATE tasks SET title=?,description=?,status=? WHERE id=?",
                (task.title, task.description, task.status, task.id))
            return task

    def delete(self, tid: int) -> bool:
        with sqlite3.connect(self.db_path) as conn:
            return conn.execute("DELETE FROM tasks WHERE id=?", (tid,)).rowcount > 0


# === Schicht 2: Business Logic (Anwendungslogik) ===
class TaskService:
    def __init__(self, repo: TaskRepository):
        self._repo = repo

    def create_task(self, title: str, description="") -> TaskDTO:
        if not title or len(title.strip()) == 0:
            raise ValueError("Titel darf nicht leer sein")
        if len(title) > 100:
            raise ValueError("Titel max. 100 Zeichen")
        return self._repo.create(TaskDTO(0, title.strip(), description, "open"))

    def complete_task(self, task_id: int) -> TaskDTO:
        task = self._repo.get_by_id(task_id)
        if not task:
            raise ValueError(f"Task {task_id} nicht gefunden")
        task.status = "done"
        return self._repo.update(task)

    def list_open_tasks(self) -> List[TaskDTO]:
        return [t for t in self._repo.get_all() if t.status != "done"]


# === Schicht 1: Präsentation (CLI) ===
class TaskCLI:
    def __init__(self, service: TaskService):
        self._service = service

    def run(self):
        while True:
            print("\n1=Neuer Task  2=Offene Tasks  3=Abschließen  4=Beenden")
            choice = input("Wahl: ")
            if choice == "1":
                title = input("Titel: ")
                desc = input("Beschreibung: ")
                try:
                    task = self._service.create_task(title, desc)
                    print(f"Task {task.id} erstellt")
                except ValueError as e:
                    print(f"Fehler: {e}")
            elif choice == "2":
                for t in self._service.list_open_tasks():
                    print(f"[{t.id}] {t.title} ({t.status})")
            elif choice == "3":
                tid = int(input("Task-ID: "))
                try:
                    self._service.complete_task(tid)
                    print("Abgeschlossen")
                except ValueError as e:
                    print(f"Fehler: {e}")
            elif choice == "4":
                break


if __name__ == "__main__":
    repo = TaskRepository()
    service = TaskService(repo)
    cli = TaskCLI(service)
    cli.run()

Vorteile und Nachteile

Vorteile

  • Strukturierte, wartbare Anwendung
  • Leichter Austausch von Komponenten
  • Gute Testbarkeit

Nachteile

  • Initial mehr Aufwand
  • Overhead bei sehr kleinen Projekten

Typische Prüfungsfragen (mit Kurzantwort)

  1. Was beschreibt das 3-Schichten-Modell? Präsentation, Logik, Datenzugriff.
  2. Welche Schicht validiert Eingaben? Business-Logik.
  3. Was gehört zur Datenzugriffsschicht? DB-Zugriff, SQL/ORM, Repositories.

FAQ — Häufige Fragen zum 3-Schichten-Modell

F: Was ist der Unterschied zwischen 3-Schichten-Modell und 3-Tier-Architektur? A: Das 3-Schichten-Modell beschreibt eine logische Trennung (Präsentation, Business, Persistence) —> alle Schichten können auf einem Server laufen. Die 3-Tier-Architektur beschreibt eine physische Trennung auf drei Maschinen: Client, Application-Server und Datenbank-Server. In der Prüfung werden beide Begriffe oft synonym verwendet.

F: Warum sollte man nicht direkt aus der UI auf die Datenbank zugreifen? A: Durch direkte DB-Zugriffe aus der UI entsteht eine starke Kopplung. Datenbankschema-Änderungen erfordern Anpassungen an allen UI-Stellen. Business-Regeln (Validierungen) können nicht zentral gesteuert werden. SQL-Injection-Angriffe sind schwerer abzuwehren. Die Business-Schicht fungiert als zentrale Kontroll- und Schutzinstanz.

F: Wo genau liegen die Grenzen zwischen den Schichten? A: Die Präsentation kennt nur die Business-Schicht und übergibt Rohdaten. Die Business-Schicht validiert und übergibt DTOs an die Persistence-Schicht. Die Persistence kennt nur SQL/ORM. Wenn in einem Repository Methodennamen wie “validiere” oder “prüfe” auftauchen, ist die Grenze verletzt.

F: Was ist ein DTO und warum braucht man es? A: Ein DTO (Data Transfer Object) trägt nur Daten, enthält keine Logik. Im Python-Beispiel ist TaskDTO ein DTO. DTOs entkoppeln die Schichten, da jede Schicht nur das DTO-Format kennen muss — nicht die internen Strukturen der anderen Schichten.

F: Wann lohnt sich das 3-Schichten-Modell nicht? A: Bei sehr kleinen Skripten (wenige hundert Zeilen) ist der Overhead unnötig. Sobald Unit-Tests nötig sind oder die Datenbank austauschbar sein soll, lohnt sich die Trennung. Die Faustregel: Ein-Personen-Skript = flach, Team-Projekt oder wartbare Anwendung = 3 Schichten.

Freie Antwort

Für IHK-Projekte ist das Modell ideal, weil es leicht zu zeichnen und zu begründen ist. Wichtig: keine SQLs im Controller und keine DB-Zugriffe aus der UI.

Lernstrategie

  1. Modell für ein System zeichnen (Shop/Blog).
  2. CRUD-App strikt nach Schichten implementieren.
  3. Schichten in Projektdoku erklären.
  4. Trennung technisch durch Pakete/Namespaces umsetzen.

Weiterführende Infos

  1. https://c4model.com/
Zurück zum Blog
Share:

Ähnliche Beiträge