Active Record Pattern in Magento 2: Models und AbstractModel
Magento 2 · Design Patterns

Active Record Pattern
in Magento 2

Das Active Record Pattern kombiniert Datenbankzugriff und Geschäftslogik in einem Objekt. Magento 2 baut historisch darauf auf — aber das moderne Magento bevorzugt das Repository Pattern. Wie beide zusammenspielen und was man heute verwenden soll.

⏱ 11 Min. Lesezeit PHP 8.4 Magento 2.4.8

1. Das Active Record Pattern

Das Active Record Pattern (Martin Fowler) kombiniert die Datenbankzeile und die Geschäftslogik in einem Objekt. Das Objekt weiß, wie es sich selbst speichert, lädt und löscht:

2. Active Record in Magento 2: Model und ResourceModel

Magento 2 trennt das Active Record Pattern in zwei Klassen: Model (Daten + Geschäftslogik) und ResourceModel (Datenbankzugriff). Das Model delegiert DB-Operationen an das ResourceModel:

3. Probleme des Active Record Patterns

Das Active Record Pattern in Magento 2 hat bekannte Schwächen:

  • Verletzt SRP — das Model enthält Daten, Geschäftslogik UND Datenbankwissen
  • Schwer testbar$model->load() und $model->save() brauchen Datenbankzugriff
  • Magic methodsgetData()/setData() sind nicht typsicher, keine IDE-Unterstützung
  • AbstractModel::load() deprecated — ab Magento 2.4 als deprecated markiert
  • Keine Interface-Contracts — nicht über Service Contracts erreichbar

4. Repository Pattern als Alternative

Das Repository Pattern trennt Datenbankzugriff vollständig von der Geschäftslogik. Das Model ist eine reine Datenstruktur (Value Object), das Repository kümmert sich um Persistenz:

5. Vollständige Repository-Implementierung

Mironsoft

Magento 2 Repository Pattern & Service Contracts

Legacy Active Record durch Repository ablösen?

Wir migrieren Magento 2 Module von Model::load()-basiertem Active Record zu sauberem Repository Pattern mit Service Contracts, SearchCriteria und vollständiger Testabdeckung.

Service Contracts

Repository Interfaces und Data Interfaces als stabile API-Schicht

Migration

load()-Aufrufe durch Repository::getById() ersetzen, sicher und schrittweise

Unit Tests

Repository-Klassen mocken und isoliert testen ohne Datenbankzugriff

6. Zusammenfassung

Magento 2 basiert historisch auf Active Record (Model + ResourceModel), bewegt sich aber zunehmend zum Repository Pattern. AbstractModel::load() ist deprecated. Neuer Code verwendet immer Repository-Interfaces als Service Contracts — testbar, typsicher und ohne direkte DB-Abhängigkeit im Geschäftscode.

Active Record & Repository — Das Wichtigste auf einen Blick

load() deprecated

AbstractModel::load() ist seit Magento 2.4 deprecated. Stattdessen: Repository::getById() oder Repository::get().

Model + ResourceModel

Model = Daten + Geschäftslogik. ResourceModel = Datenbankzugriff. Diese Trennung ist Magento 2's Antwort auf reines Active Record.

Repository Interface

PostRepositoryInterface mit getById(), save(), delete(), getList(). In di.xml verknüpft mit konkreter Implementierung.

Data Interface

PostInterface mit typisierten Gettern/Settern. Implementiert vom Model. API-Clients kennen nur das Interface, nicht das konkrete Model.

7. FAQ: Active Record Pattern in Magento 2

1 Was ist das Active Record Pattern?
Kombination von Datenbankzeile und Geschäftslogik in einem Objekt — kann sich selbst laden, speichern und löschen. In Magento 2 durch Model + ResourceModel implementiert: Model delegiert DB-Operationen an ResourceModel.
2 Warum ist AbstractModel::load() deprecated?
Kein typsicheres Interface, vermischt Concerns, nicht über Service Contracts erreichbar, erschwert Tests. Seit 2.4 deprecated. Ersatz: Repository::getById().
3 Model vs. ResourceModel — der Unterschied?
Model: Daten, Geschäftslogik, Events. ResourceModel: Datenbankverbindung, SQL, Tabellen-Mapping. Model delegiert load/save/delete an ResourceModel — Magento's Antwort auf klassisches Active Record.
4 Wie erstellt man ein Repository?
Interface mit getById(), save(), delete(), getList(). Implementierung mit PostFactory + PostResource + CollectionFactory. In di.xml als Preference verknüpfen.
5 Wie ladet man ein Produkt korrekt?
$productRepository->getById(42) oder $productRepository->get('SKU-001'). Nicht mehr $product->load(42) — deprecated.
6 Wie testet man ein Repository?
Repository Interface mocken: createMock(PostRepositoryInterface::class). Klassen die das Repository nutzen bekommen den Mock per Constructor Injection. Kein Datenbankzugriff im Unit Test.
7 Was ist ein Data Interface?
Definiert typisierte Getter/Setter für eine Entity. Model implementiert es. API-Clients kennen nur das Interface. Ermöglicht Implementierungsaustausch ohne API-Breaking-Changes.
8 Kann man load() noch in 2.4.8 verwenden?
Technisch ja, aber deprecated. In neuen Modulen immer Repository. In Legacy-Code schrittweise migrieren. PHPStan markiert es als deprecated warning.
9 Was passiert intern bei Repository::save()?
postResource->save($post) → prüft INSERT/UPDATE → dispatcht _beforeSave/_afterSave Events → führt SQL aus → setzt Entity-ID nach INSERT. Bei Fehler: CouldNotSaveException.
10 Wie implementiert man delete() im Repository?
postResource->delete($post) aufrufen. CouldNotDeleteException bei Fehler werfen. Für deleteById(int $id): erst getById(), dann delete().