- Hinzufügen der DependencyInjection-Konfiguration für das Billing-Modul. - Erstellen der Invoice-Entity mit API-Ressourcen und Berechtigungen. - Konfigurieren der Services in services.yaml für das Billing-Modul. - Implementieren von CLI-Commands zur Verwaltung von Modul-Lizenzen und zur Auflistung installierter Module. - Erstellen eines API-Controllers zur Verwaltung von Modulen und Lizenzen. - Hinzufügen eines EventListeners für das Booten von Modulen. - Definieren von Interfaces für Lizenzvalidierung und Modul-Plugins. - Implementieren der ModuleRegistry zur Verwaltung und Booten von Modulen. - Erstellen eines LicenseValidator-Services zur Validierung und Registrierung von Lizenzen.
12 KiB
Plugin-System: Zusammenfassung der Implementierung
Executive Summary
Das implementierte Plugin-System ermöglicht die Installation und Verwaltung von optionalen, lizenzierbaren Modulen in myCRM. Module sind vollständig unabhängige Composer-Packages, die ohne Änderungen am Core nachträglich installiert werden können und nur mit gültiger Lizenz funktionieren.
Kernmerkmale
✅ Anforderung: Leicht nachträglich installierbar
Gelöst durch:
- Composer-basierte Installation (
composer require mycrm/modul-name) - Symfony Flex Auto-Configuration für Bundle-Registrierung
- Automatische Service-Discovery über Tagged Iterators
- Keine manuelle Code-Änderung im Core erforderlich
✅ Anforderung: Keine Core-Abhängigkeiten
Gelöst durch:
- Interface-basierte Architektur (
ModulePluginInterface,LicenseValidatorInterface) - Core kennt nur Interfaces, keine konkreten Module
- Module registrieren sich selbst via Dependency Injection
- Loose Coupling durch Event-System und Service-Tags
✅ Anforderung: Lizenzvalidierung erforderlich
Gelöst durch:
- REST-API basierte Lizenzprüfung gegen externen Server
- JWT-Token Format für Lizenzschlüssel
- Caching mit 24h TTL + 7 Tage Grace Period
- Offline-Betrieb möglich (gecachte Validierung)
- Module werden nur mit gültiger Lizenz gebootet
Architektur-Komponenten
1. Core-System (Hauptprojekt)
Interfaces:
/src/Plugin/
├── ModulePluginInterface.php # Plugin-Contract
└── LicenseValidatorInterface.php # Lizenz-Contract
Implementierungen:
/src/
├── Plugin/ModuleRegistry.php # Plugin-Verwaltung
├── Service/LicenseValidator.php # Lizenzprüfung
├── EventListener/ModuleBootListener.php # Auto-Boot
├── Command/
│ ├── ModuleListCommand.php # CLI: Liste
│ └── ModuleLicenseCommand.php # CLI: Lizenz-Verwaltung
└── Controller/Api/ModuleManagementController.php # REST-API
Frontend:
/assets/js/views/
└── ModuleManagement.vue # Admin-UI
2. Modul-Package (externes Repository)
mycrm-modulname/
├── composer.json # Symfony Bundle Type
├── src/
│ ├── ModulnameModulePlugin.php # Implements ModulePluginInterface
│ ├── ModulnameBundle.php # Symfony Bundle
│ ├── DependencyInjection/ # Container Extension
│ ├── Entity/ # Doctrine Entities
│ ├── Controller/ # API Controllers
│ ├── Security/Voter/ # Permissions
│ └── Service/ # Business Logic
├── config/
│ ├── services.yaml # Service-Tag: app.module_plugin
│ └── routes.yaml
└── migrations/ # Doctrine Migrations
Workflow
Installation eines Moduls
graph TD
A[composer require mycrm/modul] --> B[Composer installiert Package]
B --> C[Symfony Flex registriert Bundle]
C --> D[DI Container lädt Services]
D --> E[ModuleRegistry erkennt Plugin via Tag]
E --> F[Admin registriert Lizenz]
F --> G[LicenseValidator prüft gegen Server]
G --> H{Lizenz gültig?}
H -->|Ja| I[ModuleBootListener bootet Modul]
H -->|Nein| J[Modul bleibt inaktiv]
I --> K[Modul verfügbar]
Lizenzvalidierung
sequenceDiagram
participant App as myCRM
participant Cache as Cache
participant Validator as LicenseValidator
participant Server as Lizenzserver
App->>Validator: validate("billing")
Validator->>Cache: getCached("billing")
alt Cache Hit & Grace Period aktiv
Cache-->>Validator: Gecachte Lizenz
Validator-->>App: Valid (cached)
else Cache Miss oder abgelaufen
Validator->>Server: POST /api/validate
alt Server erreichbar
Server-->>Validator: {valid: true, ...}
Validator->>Cache: speichern (24h TTL)
Validator-->>App: Valid
else Server nicht erreichbar
alt Gecachte Lizenz in Grace Period
Validator-->>App: Valid (offline)
else Keine Grace Period
Validator-->>App: Invalid
end
end
end
API-Übersicht
REST-API Endpunkte
GET /api/modules # Alle Module auflisten
GET /api/modules/{id} # Modul-Details
POST /api/modules/{id}/license # Lizenz registrieren
DELETE /api/modules/{id}/license # Lizenz widerrufen
POST /api/modules/{id}/validate # Lizenz validieren
GET /api/modules/licenses # Alle Lizenzen
CLI-Commands
app:module:list # Module auflisten
app:module:license <module> [key] # Lizenz verwalten
--revoke # Lizenz widerrufen
--validate # Lizenz prüfen
Integration mit bestehendem System
Permission-System
Module können eigene Permission-Module registrieren:
public function getPermissionModules(): array
{
return ['billing', 'invoices', 'payments'];
}
Diese werden automatisch im Role-Permission-System verfügbar und können über die bestehenden Voter geprüft werden:
$this->denyAccessUnlessGranted('VIEW', 'billing');
API Platform Integration
Modul-Entities implementieren ModuleAwareInterface:
class Invoice implements ModuleAwareInterface
{
public function getModuleName(): string
{
return 'billing';
}
}
#[ApiResource(
security: "is_granted('VIEW', 'billing')"
)]
Vue.js Frontend
Module können eigene Vue-Komponenten bereitstellen:
// Modul-Package: assets/js/index.js
export default {
components: { InvoiceManagement },
routes: [/* ... */]
}
// Haupt-App: Bedingte Integration
if (window.myCRM.modules.billing?.active) {
app.use(BillingModule)
}
Sicherheits-Features
1. Lizenzschlüssel-Speicherung
- Environment-Variablen (
.env.local) - Nicht im Git-Repository (
.env.localin.gitignore) - Optional: Verschlüsselte DB-Speicherung
2. API-Zugriff
- Alle Management-Endpunkte:
#[IsGranted('ROLE_ADMIN')] - Lizenzschlüssel werden nie im Frontend exponiert
- CSRF-Protection via Symfony
3. Offline-Schutz
- Grace Period verhindert Dauerbetrieb ohne Validierung
- Cache-TTL: 24 Stunden
- Grace Period: 7 Tage nach letzter Validierung
4. Modul-Isolation
- Module können Core nicht modifizieren
- Eigene Doctrine Entity-Manager möglich
- Eigene Namespaces, keine Konflikte
Performance-Optimierung
Caching-Strategie
Lizenzvalidierung:
├── Online-Check: Nur alle 24h
├── Cache-Hit: < 1ms
└── Offline-Mode: Grace Period 7 Tage
Lazy-Loading
- Module booten nur beim ersten Request
- Frontend-Komponenten lazy-loaded
- Doctrine-Mappings on-demand
Service-Tagging
- Automatische Plugin-Discovery
- Kein manuelles Service-Scanning
- Symfony DI Container optimiert
Testing-Strategie
Unit-Tests
class MyModulePluginTest extends TestCase
{
public function testIsLicensedWithValidLicense(): void
{
$validator = $this->createMock(LicenseValidatorInterface::class);
$validator->method('validate')->willReturn(['valid' => true]);
$plugin = new MyModulePlugin($validator);
$this->assertTrue($plugin->isLicensed());
}
}
Integration-Tests
class LicenseValidatorTest extends KernelTestCase
{
public function testValidateFetchesFromServer(): void
{
$client = $this->createMock(HttpClientInterface::class);
// Mock HTTP-Response
$validator = new LicenseValidator($client, ...);
$result = $validator->validate('billing');
$this->assertTrue($result['valid']);
}
}
Functional-Tests
class ModuleManagementControllerTest extends WebTestCase
{
public function testListModulesRequiresAdmin(): void
{
$client = static::createClient();
$client->request('GET', '/api/modules');
$this->assertResponseStatusCodeSame(401);
}
}
Deployment-Checklist
Erstinstallation
- Core-System:
services_plugin.yamlimportieren - Environment:
LICENSE_SERVER_URLsetzen - Environment:
INSTANCE_IDgenerieren - Cache-Clear:
php bin/console cache:clear
Modul-Installation
- Composer:
composer require mycrm/modul-name - Bundle: Automatisch registriert oder manuell in
bundles.php - Migrations:
php bin/console doctrine:migrations:migrate - Lizenz:
php bin/console app:module:license modul-name - Cache:
php bin/console cache:clear - Test:
php bin/console app:module:list
Produktion
.env.localmit Lizenzen auf Server deployen- Lizenzserver-Firewall: Zugriff erlauben
- Monitoring: Lizenz-Ablaufdaten überwachen
- Backup:
.env.localin Backup einschließen
Erweiterungsmöglichkeiten
Bereits implementiert
✅ Interface-basierte Architektur ✅ Lizenzvalidierung mit Online/Offline-Modus ✅ CLI-Commands für Verwaltung ✅ REST-API für Admin-UI ✅ Vue.js Admin-Interface ✅ Integration mit Permission-System ✅ Caching mit Grace Period ✅ Composer-basierte Installation
Zukünftige Features (optional)
- 🔮 Multi-Tenancy-Support (mehrere Instanzen, eine Lizenz)
- 🔮 Offline-Lizenzierung (Air-Gapped Systeme)
- 🔮 Automatische Update-Benachrichtigungen
- 🔮 Modul-Marketplace Integration
- 🔮 Feature-Flags innerhalb von Modulen
- 🔮 Modul-Dependencies (Modul A benötigt Modul B)
- 🔮 Rollback-Mechanismus bei Installation
Technologie-Stack
Backend
- PHP 8.3+
- Symfony 7.1 LTS
- Doctrine ORM
- API Platform
- PSR-6 Cache (Symfony Cache)
- PSR-3 Logger (Monolog)
Frontend
- Vue.js 3 (Composition API)
- PrimeVue (UI-Komponenten)
- Tailwind CSS
DevOps
- Composer (Package Management)
- Symfony Flex (Auto-Configuration)
- Doctrine Migrations
Code-Statistik
Core-System (neu):
├── Interfaces: 2 Dateien
├── Services: 2 Dateien
├── Commands: 2 Dateien
├── Controller: 1 Datei
├── EventListener: 1 Datei
└── Vue-Komponenten: 1 Datei
Beispiel-Modul:
├── Plugin-Klasse: 1 Datei
├── Bundle: 1 Datei
├── DependencyInjection: 2 Dateien
├── Entity: 1 Datei (Beispiel)
└── Konfiguration: 1 Datei
Dokumentation:
├── PLUGIN_SYSTEM.md: Vollständige Anleitung
├── PLUGIN_SYSTEM_SUMMARY.md: Diese Datei
├── EXAMPLE_MODULE_STRUCTURE.md: Modul-Template
└── example-module/: Code-Beispiele
Kosten-Nutzen-Analyse
Vorteile
✅ Modularität: Kunden zahlen nur für benötigte Features ✅ Wartbarkeit: Module unabhängig updatebar ✅ Skalierbarkeit: Beliebig viele Module möglich ✅ Lizenzierung: Automatische Kontrolle über Funktionen ✅ Entwicklung: Parallele Modul-Entwicklung möglich ✅ Testing: Module isoliert testbar ✅ Deployment: Schrittweise Einführung möglich
Aufwand
⚠️ Initial: ~2-3 Tage für Core-System (bereits implementiert) ⚠️ Pro Modul: ~1-2 Tage zusätzlich für Plugin-Setup ⚠️ Lizenzserver: Separater Service erforderlich ⚠️ Dokumentation: Pro Modul individuell
ROI
Mit diesem System können Sie:
- Module einzeln lizenzieren und verkaufen
- Upselling durch Feature-Module
- Trial-Lizenzen mit Ablaufdatum
- Pay-per-Feature-Modelle
- White-Label-Lösungen mit unterschiedlichen Modulen
Kontakt und Support
Diese Implementierung wurde mit Claude Code entwickelt und basiert auf Symfony Best Practices.
Nächste Schritte:
- Lizenzserver implementieren (externe Komponente)
- Erstes Produktiv-Modul entwickeln (z.B. Billing)
- Testing und Monitoring in Produktion
- Dokumentation für Modul-Entwickler erweitern