Leistungen Referenzen Notfallservice Kontakt
    KONTAKT.

    Konsolutions | Full-Service-Agentur
    Volker Königshofen
    Mühlenbachstr. 40
    41462 Neuss

    Fon: +49 172 2485226 (auch Whatsapp)
    Fax: +49 2131 5394167
    Mail: info@koenigshofen.com/blog

    USt-IdNr.: DE255840543

    Kontaktformular
    Unser Team

    Haendlerbund

    Ein typisches PHP-Projekt startet klein: ein paar SQL-Queries im Controller, etwas Logik im Model, fertig. Spätestens wenn Features wachsen, ändern sich Anforderungen – und plötzlich muss eine Abfrage an fünf Stellen angepasst werden. Oder es soll statt MySQL eine API genutzt werden. Genau hier hilft ein Muster, das bewusst langweilig wirkt: Es ordnet den Zugriff auf Daten, ohne das Projekt zu verkomplizieren.

    Das Repository Pattern sorgt dafür, dass Anwendungslogik (z. B. „User registrieren“) nicht mehr direkt weiß, wie Daten gespeichert oder geladen werden. Stattdessen fragt sie über eine klar definierte Schnittstelle: „Gib mir den User mit ID 5“ oder „Speichere diesen User“. Dadurch wird Code leichter testbar, besser lesbar und robuster gegen Änderungen am Speicherort.

    Repository Pattern: Was damit gemeint ist

    Ein Repository ist eine Klasse (oder ein Satz von Klassen), die als „Sammlung“ für Entities (fachliche Objekte wie User, Artikel, Bestellung) dient. Es versteckt die Details der Datenquelle: SQL, PDO, ORM, HTTP-API oder sogar eine Datei. Im Alltag bedeutet das: Controller und Services sprechen nicht mehr direkt mit der Datenbank, sondern mit dem Repository.

    Warum „Datenzugriff trennen“ so viel ausmacht

    In vielen Codebasen entsteht ein Mix aus SQL, Business-Regeln und Formatierung in derselben Methode. Das hat drei Folgen:

    • Änderungen am Schema erzwingen Änderungen in vielen Dateien.
    • Tests werden schwierig, weil für fast alles eine echte Datenbank nötig ist.
    • Neue Datenquellen (z. B. zusätzliches Caching oder eine API) lassen sich kaum einbauen, ohne Logik zu duplizieren.

    Mit einem Repository wandert SQL in genau einen Bereich. Der Rest des Systems nutzt Methoden mit sprechenden Namen.

    Repository vs. „einfach ein Model“

    In klassischen Active-Record-Ansätzen „kann“ das Model selbst speichern und laden. Das ist für kleine Projekte ok, vermischt aber oft Domain-Logik und Persistence (Speicherung). Ein Repository verfolgt die entgegengesetzte Idee: Die Entity bleibt möglichst „dumm“ und kennt weder SQL noch PDO. Das Repository kennt die Datenquelle – und stellt Methoden bereit, um Entities zu holen oder zu speichern.

    Wann lohnt sich ein Repository wirklich – und wann nicht

    Das Muster ist kein Muss. Es lohnt sich besonders dann, wenn Datenzugriff an mehreren Stellen gebraucht wird oder sich die Art der Speicherung ändern kann. Bei einer sehr kleinen Seite mit zwei Abfragen pro Request kann es zu viel Struktur sein.

    Entscheidungsweg für die Praxis

    • Wenn die App mehr als „ein CRUD-Screen“ ist:
      • Wenn mehrere Features dieselben Abfragen benötigen → Repository hilft, Duplikate zu vermeiden.
      • Wenn Logik unabhängig von SQL getestet werden soll → Repository hilft, Test-Doubles (Mocks/Stubs) einzusetzen.
    • Wenn sich Datenquellen ändern können:
      • Heute MySQL, morgen zusätzlich Redis-Cache oder externe API → Repository reduziert Umbauten.
    • Wenn es wirklich nur eine Handvoll Abfragen gibt:
      • Lieber sauber strukturierte Query-Klassen oder Funktionen nutzen und nicht „auf Teufel komm raus“ abstrahieren.

    Eine sinnvolle Struktur: Entity, Interface, Repository

    In PHP hat sich eine einfache Aufteilung bewährt:

    • Entity: fachliche Datenstruktur, z. B. User mit id, email, passwordHash.
    • Repository Interface: beschreibt, welche Operationen möglich sind (z. B. findById).
    • Repository Implementierung: konkrete Umsetzung, z. B. mit PDO und SQL.

    Wichtig ist weniger die Ordnerstruktur, sondern die Richtung der Abhängigkeiten: Die Anwendung hängt am Interface, nicht an der Datenbanktechnik. Das passt gut zu sauberer Architektur, wie sie auch im Artikel zum Dependency Inversion Principle beschrieben wird.

    Beispiel: Repository-Interface

    Ein Interface hält Methoden knapp und fachlich. Statt getUserRowByEmail lieber findByEmail. Das beschreibt das Ziel, nicht den technischen Weg.

    • findById(int $id): ?User
    • findByEmail(string $email): ?User
    • save(User $user): void

    Ob save intern ein INSERT oder UPDATE ist, darf die Anwendung nicht interessieren.

    Implementierung mit PDO: Schritt für Schritt

    Viele Projekte nutzen PDO. Das passt gut, weil es bewusst nah an SQL bleibt, aber trotzdem vorbereitete Statements (Prepared Statements) ermöglicht.

    1) Entity bewusst schlicht halten

    Eine Entity sollte nicht wissen, wie sie gespeichert wird. Sinnvoll sind Konstruktor, Getter und ggf. kleine Validierung (z. B. „E-Mail darf nicht leer sein“). Komplexe Eingabeprüfung gehört eher vor das Speichern – siehe auch Input-Validierung im Backend.

    2) Repository baut Entities aus Datenbankzeilen

    Der zentrale Job eines Repositories ist das Übersetzen: DB-Zeile → Entity und Entity → DB-Operation. Damit dieser Teil nicht ausufert, hilft ein klarer, wiederverwendbarer Mapper-Ansatz. Das kann eine private Methode sein wie mapRowToUser.

    3) Save-Strategie definieren (Insert vs. Update)

    Damit save klar bleibt, braucht es eine Regel. Häufig: Wenn id vorhanden ist, dann UPDATE, sonst INSERT. Alternativ kann man zwei Methoden anbieten (insert/update) – das ist manchmal transparenter, wenn IDs extern vergeben werden.

    4) Fehler sichtbar machen, aber sinnvoll

    PDO wirft bei Fehlern Exceptions (wenn korrekt konfiguriert). Diese sollten nicht verschluckt werden. In größeren Anwendungen lohnt sich eine kleine Übersetzung in fachliche Fehler, z. B. „E-Mail existiert schon“. Dafür ist es hilfreich, die Datenbank über Constraints abzusichern; dazu passt SQL Constraints verstehen.

    Praktischer Ablauf: so bleibt der Code im Alltag sauber

    • Pro Entity ein Repository, aber nur wenn wirklich Abfragen gebraucht werden.
    • Methoden nach Fachsprache benennen: findActiveByEmail ist besser als selectWhereStatus.
    • SQL nur im Repository (oder in klaren Query-Hilfsklassen), nicht im Controller.
    • Prepared Statements verwenden, um SQL-Injection zu verhindern; vertiefend: Prepared Statements in PHP.
    • Repository-Methoden klein halten: lieber zwei gezielte Methoden als eine „Super-Suche“ mit zehn optionalen Parametern.

    Typische Stolperfallen und wie sie vermieden werden

    In der Praxis scheitert das Muster selten an der Idee, sondern an der Umsetzung. Diese Punkte sind die häufigsten Ursachen für Frust:

    Zu generische Repositories

    Ein „BaseRepository“ mit findAll, findBy, deleteWhere wirkt zunächst praktisch, wird aber schnell zur Dumping-Zone. Abfragen werden dann als Arrays beschrieben und verteilen sich wieder im Code. Besser: gezielte Methoden, die eine fachliche Absicht ausdrücken.

    Repository gibt Arrays statt Entities zurück

    Arrays sind flexibel, aber sie verschieben Verantwortung: Jede aufrufende Stelle muss wissen, wie Felder heißen, welche optional sind und welche Typen gelten. Entities bündeln diese Regeln. Wenn ein Projekt bewusst „array-first“ ist (z. B. reine API-Response-Schicht), kann das ok sein – dann sollte aber konsequent ein DTO (Data Transfer Object) genutzt werden.

    Business-Logik im Repository

    Ein Repository sollte keine fachlichen Entscheidungen treffen wie „User darf nicht gelöscht werden, wenn …“. Das gehört in einen Service (Anwendungslogik). Das Repository soll Daten laden/speichern und ggf. technische Details kapseln (z. B. Transaktionen starten, wenn es wirklich nur Persistence betrifft).

    Vergleich: Repository Pattern vs. direkte PDO-Queries

    Aspekt Repository Pattern Direkte Queries im Code
    Änderungen am Schema Meist nur im Repository anzupassen Oft verteilt über mehrere Dateien
    Tests ohne Datenbank Gut möglich über Interfaces und Test-Doubles Schwer, weil SQL überall steckt
    Lesbarkeit Methoden drücken Fachabsicht aus SQL muss jedes Mal „mitgelesen“ werden
    Startaufwand Mehr Struktur am Anfang Schneller Einstieg

    Tests: Repository austauschen, ohne die App umzubauen

    Der große Gewinn entsteht, wenn die Anwendung nicht mehr an PDO hängt. Stattdessen hängt sie am Interface. In Tests kann ein InMemory-Repository eingesetzt werden: eine einfache Implementierung, die Entities in einem Array hält. So lassen sich Services testen, ohne Datenbank und ohne SQL-Fixtures.

    Ein InMemory-Repository als Test-Double

    Für Unit-Tests reicht oft:

    • ein Array als Speicher
    • eine ID-Vergabe (z. B. hochzählend)
    • Methoden wie findById/save mit minimaler Logik

    Wichtig: Das InMemory-Repository sollte das Interface erfüllen, aber nicht versuchen, die Datenbank 1:1 zu simulieren. Es soll Tests beschleunigen, nicht neue Komplexität schaffen.

    Saubere Erweiterungen: Caching und Transaktionen ohne Chaos

    Wenn das Repository die Grenze zur Datenquelle ist, lassen sich Erweiterungen gezielt einbauen:

    • Datenzugriff mit Cache: Ein Decorator-Repository kann zuerst im Cache nachsehen und bei Miss aus der DB laden.
    • Transaktionen: Wenn mehrere Repositories in einem Use Case zusammenarbeiten, ist oft ein Service der bessere Ort, um eine Transaktion zu steuern. Hintergrund dazu bietet Transactions in SQL.

    Der Grundgedanke bleibt: Die Anwendung beschreibt Absichten, die Repository-Schicht kümmert sich um Technik.

    Quellen

    • Keine externen Quellen angegeben.
    Share.
    Avatar-Foto

    Königshofen Digital - Websites, E-Commerce, SEO/SEA, Google Ads, Branding und Automation mit KI. Liefert effiziente, automatisierte und messbare Lösungen aus einer Hand.