Magento Modul programmieren (Architektur): Setup, DI, Observer, Composer & Marketplace-ready

1) Zielbild: Architektur für ein wartbares Magento-2-Modul
Ein solides Magento-Modul trennt Verantwortlichkeiten: API/Service Layer (Business-Logik), Infrastructure (Persistence, Magento-Adapter), Presentation (Controller/Blocks/UI) und Integration (Events/Observer, Plugins). So bleibt das Modul updatefähig, testbar und leichter für den Magento Marketplace vorbereitbar (Qualität, Composer-Metadaten, saubere Dependencies).
Interessiert an diesem Thema?
Kontaktieren Sie uns für eine kostenlose Beratung →Empfohlene Minimal-Struktur
app/code/Vendor/Module/(oder als Composer-Package untervendor/)etc/module.xml,registration.phpetc/di.xml(Dependency Injection Konfiguration)etc/events.xml(Observer Pattern)Setup/Patch/Schemaund/oderSetup/Patch/DataModel/,Api/,Api/Data,Service/(je nach Komplexität)
2) Modul-Bootstrap: Registrierung & Moduldeklaration
Magento lädt Module über registration.php und etc/module.xml. In einer Marketplace- bzw. Composer-Verteilung liegt das Modul meist als Package vor; in Projekten startet man oft unter app/code.
3) Dependency Injection (DI): Kernprinzip für saubere Abhängigkeiten
Dependency Injection bedeutet: Klassen erstellen ihre Abhängigkeiten nicht selbst (kein new für Services), sondern bekommen diese vom Magento Object Manager (DI-Container) bereitgestellt. Vorteile:
- Testbarkeit (Mocks/Stubs für Abhängigkeiten)
- Austauschbarkeit über
preference,virtualTypeoder Argumente - Klare Architektur: Services konsumieren Interfaces statt konkreter Implementierungen
DI-Architektur-Empfehlung
- Definiere Interfaces in
Api/ - Implementierungen in
Model/oderService/ - Verknüpfung per
etc/di.xml(Preference) oder Constructor Type-Hints
4) Observer Pattern: Events nutzen statt Core zu verändern
Magento setzt stark auf das Observer Pattern: Ein Event wird „ausgelöst“ (dispatch), und ein oder mehrere Observer reagieren darauf. Das ist ideal, um Verhalten zu erweitern, ohne Core-Dateien anzupassen. Typische Events: Checkout, Customer Login, Order Place, Product Save.
Wann Observer statt Plugin?
- Observer: Reaktion auf ein Event, oft „fire-and-forget“, mehrere Listener möglich.
- Plugin (Interceptor): Methodeneingriff (before/after/around) gezielt auf eine PHP-Methode.
5) Datenbank & Setup: Patches statt Install/Upgrade Scripts
Für Magento 2 sind Schema/Data Patches (unter Setup/Patch) der moderne Weg. Sie sind versionierbar, wiederholbar kontrolliert und besser wartbar als klassische InstallSchema/UpgradeSchema-Skripte.
PHP Script for Module Setup (Schema Patch + Beispiel-Tabelle)
<?php
declare(strict_types=1);
namespace Vendor\Module\Setup\Patch\Schema;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\PatchVersionInterface;
use Magento\Framework\Setup\Patch\SchemaPatchInterface;
class CreateExampleEntityTable implements SchemaPatchInterface, PatchVersionInterface
{
private ModuleDataSetupInterface $moduleDataSetup;
public function __construct(ModuleDataSetupInterface $moduleDataSetup)
{
$this->moduleDataSetup = $moduleDataSetup;
}
public function apply(): self
{
$connection = $this->moduleDataSetup->getConnection();
$this->moduleDataSetup->startSetup();
$tableName = $this->moduleDataSetup->getTable('vendor_module_entity');
if (!$connection->isTableExists($tableName)) {
$table = $connection->newTable($tableName)
->addColumn(
'entity_id',
Table::TYPE_INTEGER,
null,
[
'identity' => true,
'unsigned' => true,
'nullable' => false,
'primary' => true
],
'Entity ID'
)
->addColumn(
'external_reference',
Table::TYPE_TEXT,
64,
['nullable' => false],
'External Reference'
)
->addColumn(
'payload',
Table::TYPE_TEXT,
'2M',
['nullable' => true],
'JSON Payload'
)
->addColumn(
'created_at',
Table::TYPE_TIMESTAMP,
null,
['nullable' => false, 'default' => Table::TIMESTAMP_INIT],
'Created At'
)
->addIndex(
$this->moduleDataSetup->getIdxName(
$tableName,
['external_reference'],
AdapterInterface::INDEX_TYPE_UNIQUE
),
['external_reference'],
['type' => AdapterInterface::INDEX_TYPE_UNIQUE]
)
->setComment('Vendor Module - Example Entity');
$connection->createTable($table);
}
$this->moduleDataSetup->endSetup();
return $this;
}
public static function getDependencies(): array
{
return [];
}
public function getAliases(): array
{
return [];
}
public static function getVersion(): string
{
return '1.0.0';
}
}
6) Composer & Distribution: Modul paketieren (Marketplace-/CI-ready)
Für professionelle Auslieferung (Kundenprojekte, CI/CD, oder Magento Marketplace) solltest du das Modul als Composer-Package pflegen. Composer übernimmt Autoloading, Versionschema, Abhängigkeiten und erleichtert Updates.
JSON Config for Composer (composer.json für ein Magento-2-Modul)
{
"name": "vendor/module",
"description": "Vendor Module - Example entity + observer + DI",
"type": "magento2-module",
"version": "1.0.0",
"license": [
"proprietary"
],
"require": {
"php": ">=8.1",
"magento/framework": "*"
},
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"Vendor\\Module\\": ""
}
},
"minimum-stability": "stable",
"prefer-stable": true
}
7) DI + Observer zusammenbringen: Ereignisgetriebene Architektur
Ein sauberes Muster ist: Observer ist nur ein „Adapter“, der das Event annimmt und an einen Service weitergibt. Der Service kapselt Business-Logik in PHP und lässt sich testen. DI injiziert dem Observer den Service.
Mermaid: Request/Event → Observer → Service → Persistence
flowchart LR
A[Magento Event Dispatch] --> B[Observer]
B -->|DI inject| C[Application Service]
C --> D[Repository Interface]
D --> E[Repository Implementation]
E --> F[(DB Table vendor_module_entity)]
8) Architektur-Checkliste (Table)
| Feature | Details |
|---|---|
| Modul-Schnittstellen | Interfaces in Api/ definieren, Implementierungen trennen (Service/Model) |
| Dependency Injection | Constructor Injection, keine direkten ObjectManager-Calls, Preferences sparsam einsetzen |
| Observer Pattern | Observer schlank halten, Logik in Services, Events gezielt wählen |
| Setup/Patches | SchemaPatch/DataPatch statt Install/Upgrade; idempotent und versioniert |
| Composer Packaging | type=magento2-module, PSR-4 Autoload, versioning, require php/magento deps |
| Marketplace Readiness | Saubere Dependencies, keine Core-Hacks, klare Konfiguration, Tests/Static Analysis empfohlen |
9) Typische Stolperfallen (und wie du sie architektonisch vermeidest)
- Zu viel Logik im Observer: macht Events schwer testbar → Observer delegiert an Service.
- ObjectManager direkt nutzen: erschwert Tests → DI verwenden.
- Fette Preferences: riskant bei Updates/Kompatibilität → lieber Plugins oder eigene Services.
- Setup-Skripte nicht idempotent: wiederholte Deployments brechen →
isTableExists/columnExistsprüfen. - Fehlende Composer-Metadaten: Updates schwierig → composer.json sauber pflegen.
10) Umsetzung im Projekt: Wann lohnt sich Unterstützung durch eine Magento Agentur?
Wenn du ein Modul für mehrere Shops ausrollen willst, Marketplace-Compliance anstrebst oder eine komplexe Architektur (Multi-Source Inventory, B2B, Headless, Integrationen) planst, lohnt sich die Unterstützung durch eine Magento Agentur—insbesondere für Code-Reviews, Quality Gates, Performance und Release-Prozesse.


