Darkwood Blog Blog
  • Artikel
  • Beobachtung
  • Releases
  • Creators
de
  • en
  • fr
Anmeldung
  • Blog
  • Artikel
  • Beobachtung
  • Releases
  • Creators

đŸ—ïž Wo sollte nativer Code liegen? Phalcon, Symfony und TrueAsync auf verschiedenen Ebenen des Stacks.

vom 27. Juni 2026

Anmelden um auf diesen Beitrag zu reagieren

🚀 1

Seit Jahren ist Phalcon eine rationale Antwort fĂŒr stark frequentierte PHP-Plattformen: ein Framework, das als C-Erweiterung bereitgestellt wird, mit geringem Ressourcenverbrauch und einer vertrauten MVC-Architektur.

KĂŒrzlich stieß ich im Rahmen eines Proof-of-Concept-Architekturprojekts fĂŒr einen Kunden auf eine Situation, die typisch fĂŒr ausgereifte PHP-Plattformen ist: eine leistungsstarke, bestehende Infrastruktur, WartungsbeschrĂ€nkungen, Workflows, die komplexer geworden sind als der einfache Anfrage-Antwort-Zyklus, und die Versuchung, schnell ein Framework auszuwĂ€hlen. Die naheliegende Frage war: Sollten wir beim Phalcon-Modell bleiben oder auf Symfony aufbauen? Die eigentliche Frage ging jedoch tiefer: Wo sollten native Performance, GeschĂ€ftslogik und Workflow-Orchestrierung angesiedelt sein?

Phalcon, Symfony und TrueAsync stehen nicht in direkter Konkurrenz zueinander: Sie optimieren unterschiedliche Anforderungen auf verschiedenen Ebenen des Stacks. Dieser Artikel geht von dieser praktischen Beobachtung aus, um eine umfassendere Perspektive zu bieten – ohne dabei anzunehmen, dass Migration immer die richtige Lösung ist.

Einleitung – Die Frage lautet nicht mehr: „Welches Framework ist schneller?“

Dieser Machbarkeitsnachweis war kein Einzelfall. Auf leseintensiven Plattformen reduziert sich das Dilemma oft auf zwei Optionen: Phalcon auf der einen Seite – eine C-Erweiterung mit geringem Overhead und bewĂ€hrtem MVC-Architekturmuster – und Symfony auf der anderen Seite – ein Ökosystem, eine etablierte Struktur und einfachere Rekrutierung – mit TrueAsync im Hintergrund als Versprechen asynchroner Laufzeitumgebung. Jede dieser Optionen hatte ihre VorzĂŒge. Keine beantwortete jedoch die sich allmĂ€hlich herauskristallisierende Frage: Auf welcher Ebene sollte die Performance optimiert und auf welcher die Orchestrierung erfolgen?

Die Diskussion ging schnell ĂŒber die Wahl des Frameworks hinaus. Sollten wir bei einer Architektur Ă€hnlich wie Phalcon bleiben, bei der der native Code auf der MVC-Ebene liegt? Oder zu Symfony migrieren, um Wartbarkeit und InteroperabilitĂ€t zu verbessern? TrueAsync als Laufzeit-Option einfĂŒhren, unabhĂ€ngig von der Webentwicklungsentscheidung? Oder die Framework-Migration mit der Neugestaltung von ArbeitsablĂ€ufen verwechseln – der hĂ€ufigste Fehler, den wir auf dieser Art von Plattform beobachten?

Die Ausgangsfrage lautete: „Sollten wir von Phalcon zu Symfony migrieren?“ Die grundlegende Frage lautete: Wo sollte der native Code liegen – und wo sollten die Schnittstellen und GeschĂ€ftsprozesse angesiedelt sein?

FĂŒr den Muttersprachler existieren drei Reaktionsmöglichkeiten nebeneinander, jede auf einem anderen Niveau:

  • Framework-Schicht - Phalcon optimiert Router, Dispatcher, ORM und Cache ĂŒber eine C-Erweiterung
  • Anwendungsschicht - Symfony strukturiert die Anwendung in reinem PHP: Dependency Injection, Sicherheit, Bundles, Organisationskonventionen
  • Laufzeitschicht - TrueAsync zielt auf gleichzeitige AusfĂŒhrung und nicht-blockierende E/A ĂŒber ext-async ab.

Diese drei AnsĂ€tze schließen sich nicht gegenseitig aus. Der Proof of Concept (POC) offenbarte jedoch vor allem einen vierten Bereich, der in Debatten um „Framework A vs. Framework B“ oft ĂŒbersehen wird: Wo Workflows angesiedelt sind und wie sich GeschĂ€ftslogik vom zugehörigen AusfĂŒhrungsmodell trennen lĂ€sst. Abgrenzungen, Orchestrierung und Laufzeit sind drei unabhĂ€ngige Entscheidungen. Werden diese – wie die ursprĂŒngliche Projektformulierung nahelegte – vermischt, fĂŒhrt dies zu den teuersten und ineffektivsten Migrationen.

Bei Darkwood versuchen wir, folgende Perspektive einzunehmen: Die Branche optimiert Frameworks seit Langem; komplexe Systeme offenbaren nun einen weiteren Engpass – die Definition von Grenzen und die explizite Orchestrierung. Die native Laufzeitumgebung bleibt ein Implementierungsdetail, nicht die Architektur. Der folgende Artikel verallgemeinert die Erkenntnisse dieses Proof of Concept und formuliert eine Perspektive, die ĂŒber einen spezifischen Anwendungsfall hinaus gĂŒltig ist.

Warum Phalcon sinnvoll war – und es immer noch ist

Phalcon ist ein Full-Stack-PHP-Framework mit kompiliertem Kern. Es ist in Zephir (https://phalcon.io) geschrieben und wird ĂŒber das cphalcon-Projekt (https://github.com/phalcon/cphalcon) nach C transpiliert. Die Verwendung entspricht der anderer PHP-Frameworks: Controller, Services und Module. Der Unterschied liegt im Stack: Eine HTTP-Anfrage erreicht PHP-FPM, die Erweiterung ist bereits geladen, und die kritischen Primitiven – Router, Dispatcher, ORM und Cache – befinden sich in der nativen Schicht. Die GeschĂ€ftslogik verbleibt in PHP; das Framework-GerĂŒst wird vorgelagert optimiert.

FĂŒr Redaktions-, Medien- oder E-Commerce-Plattformen bleiben die Vorteile mit dem richtigen Profil deutlich spĂŒrbar. Geringer Overhead durch den MVC-Ansatz. Konsistentes Modell: Module, Dependency Injection, ORM, PHQL, Volt, Cache. Leseintensive APIs mit vorab berechneten Dokumenten. Monolithische Architektur mit mehreren Schnittstellen ohne zusĂ€tzliche Schichten.

Phalcon war nicht „moderner“ als Symfony. Es war genau dort besser geeignet – auf Framework-Ebene – fĂŒr ein bestimmtes Workload-Profil. Und fĂŒr Teams, die diesen Stack beherrschen, ihre SLOs erfĂŒllen und deren Produkt sich kaum weiterentwickelt, ging dieser Vorteil nicht ĂŒber Nacht verloren.

Phalcon im Jahr 2026: Wartung, Management, GlaubwĂŒrdigkeit

Eine unvoreingenommene LektĂŒre von Phalcon im Jahr 2026 beginnt mit den Fakten – nicht mit der Annahme eines sich verschlechternden Rahmens.

Der 5.x-Zweig wird aktiv gepflegt. Version 5.16.0 wurde im Juni 2026 nach einem beschleunigten Release-Zyklus im zweiten Quartal veröffentlicht. Commits im Zweig 5.0.x erfolgen hĂ€ufig; das Changelog dokumentiert wesentliche Änderungen: die Schicht Phalcon\Contracts, einen neuen DI-Container, eine Authentifizierungskomponente, eine Queue-Schicht (Beanstalk, Redis, Memory, Stream), PIE-UnterstĂŒtzung fĂŒr die Installation, Migration zu Zephir 1.0 und QualitĂ€tssicherungstools (Infection, Sonar). Dies ist weit mehr als die minimale Wartung eines statischen Projekts.

Das Ökosystem verfĂŒgt ĂŒber eine nachweislich solide und glaubwĂŒrdige Grundlage. Die Datei BACKERS.md im cphalcon-Repository listet die UnterstĂŒtzer aus der Branche auf – darunter Cloudflare (auch auf der Cloudflare-Sponsoringseite erwĂ€hnt), Algolia und DigitalOcean (eine dokumentierte Open-Source-Partnerschaft fĂŒr Benchmarks und Infrastruktur). Open Collective und GitHub-Sponsoren leisten einen bescheidenen, aber realen Beitrag zur Community-Finanzierung. Die Steuerung basiert auf einem fokussierten Kernteam unter der Leitung von Nikolaos Dimopoulos und Anton Vasiliev mit strukturierten Beitragsprozessen.

Diese Faktoren beweisen nicht, dass Phalcon „besser“ als Symfony ist. Sie beweisen aber, dass es sich nicht um ein verlassenes Ökosystem handelt. Ein Sponsor aus der Industrie ist keine technische Garantie; er ist ein Zeichen dafĂŒr, dass das Projekt weiterhin sichtbar ist und von Stakeholdern unterstĂŒtzt wird, die an seine langfristige TragfĂ€higkeit glauben.

Architektonisch gesehen bleibt cphalcon im Kern Zephir/C: ĂŒber tausend .zep-Dateien, die als Erweiterung ĂŒber PIE oder PECL verteilt werden. Die nĂ€chste Hauptversion ist in Vorbereitung und gehört zum Zweig 7.0.x (PHP 8.3+), nicht zu einem v6-Zweig in diesem Repository. Öffentliche Diskussionen und die Arbeit an einem zugehörigen PHP-Repository („phalcon/phalcon“) deuten auf eine Weiterentwicklung hin zu einem zweigleisigen Modell: eine Referenzimplementierung in PHP mit optionalen Erweiterungen fĂŒr die ressourcenintensivsten Bereiche – ermittelt durch Messungen. Dies ist eine architektonische Weiterentwicklung, keine VerdrĂ€ngung der nativen Implementierung.

Abmessungen Phalcon (v5, 2026) Symfony
Vertrieb Erweiterung C (PIE / PECL) Composer-Pakete, reines PHP
Haupthebel Minimales Overhead-Framework Ökosystem, Struktur, InteroperabilitĂ€t
Personalbeschaffung Seltenere Profile Großer Markt
PHP-Versionsaktualisierung Erweiterung neu erstellen / neu anheften Semver Composer
Aktuelle Wartung HĂ€ufige Releases, neue Funktionen HĂ€ufige Releases, LTS
Optimale Passform Expertenteam, SLOs erfĂŒllt, kritische MVC-Performance Wachsendes Team, zahlreiche Integrationen, Produktfluktuation

Die Tabelle ermittelt keinen Gewinner. Sie stellt Bedingungen dar.

Phalcon ist nicht veraltet – das Optimierungsziel hat sich geĂ€ndert.

Die falsche Frage lautet: „Ist Phalcon veraltet?“ Die richtige Frage lautet: „Welche Optimierung ist jetzt am wichtigsten?“

Lange Zeit waren die PrioritÀten klar:

  • Reduzierung der Kosten des MVC-Dispatch- und Bootstrap-Frameworks
  • Minimierung des Speicher- und CPU-Overheads auf dem Anfrage-Antwort-Pfad
  • Bereitstellung leseintensiver APIs von einem hochperformanten Monolithen ohne Vervielfachung der Laufzeiten

Phalcon ging diese PrioritÀten konsequent an. Das native Framework war der richtige Hebel.

Die PrioritĂ€ten haben sich verĂ€ndert – nicht weil Phalcon etwas â€žĂŒbersehen“ hĂ€tte, sondern weil die Systeme gewachsen sind und sich die EngpĂ€sse verlagert haben:

  • Verteilte KomplexitĂ€t - Message-Busse, Worker, abgeleitete Modelle, groß angelegte Cache-Invalidierung
  • E/A-EngpĂ€sse - HTTP-Aggregation, Dateien, Netzwerkwartezeiten in Workern, nicht nur Bootstrap-Zeit
  • Orchestrierung – Handler-Ketten, Cronjobs, CLI-Skripte: Workflow-Logik umgeht MVC
  • Onboarding und InteroperabilitĂ€t - Rekrutierung, Pakete, Sicherheitsstandards, Integrationen von Drittanbietern
  • Organisatorische Skalierbarkeit - abgegrenzte Kontexte, progressive Extraktion, Koexistenz von Stacks

Auf vielen ausgereiften Plattformen ist der native Router nicht mehr der entscheidende Flaschenhals. Vielmehr sind es blockierende E/A-VorgĂ€nge in Workern, die KomplexitĂ€t von ArbeitsablĂ€ufen oder der Wartungsaufwand. Diese Verschiebung – nicht eine Wertung – verĂ€ndert die Diskussion um die Architektur.

Phalcon bleibt relevant, solange der synchrone MVC-Ansatz die wichtigste EinschrĂ€nkung darstellt und das Team den Stack gut beherrscht. Symfony gewinnt an Relevanz, wenn die Codeorganisation und das Ökosystem ĂŒber einen Zeitraum von zehn Jahren im Vordergrund stehen. TrueAsync kommt zum Einsatz, wenn Laufzeit-E/A-Wartezeiten die wichtigste EinschrĂ€nkung darstellen – unabhĂ€ngig vom gewĂ€hlten Framework.

Wo sollte der native Code liegen?

Kehren wir zur zentralen Frage zurĂŒck. Drei Ebenen, drei Logiken:

flowchart TB
  subgraph layers [Couches de la pile PHP]
    FrameworkLayer["Couche framework\nPhalcon extension C"]
    AppLayer["Couche application\nSymfony structure"]
    RuntimeLayer["Couche runtime\nTrueAsync ext-async"]
  end
  Question["OĂč vit le code natif ?"]
  Question --> FrameworkLayer
  Question --> AppLayer
  Question --> RuntimeLayer
Phalcon Symfony TrueAsync
Schicht Framework Anwendung Laufzeit
Ziel Router, ORM, MVC, Cache Struktur, Dependency Injection, Sicherheit, Konventionen Koroutinen, E/A, Pools
Frage Wie kann das Framework beschleunigt werden? Wie kann die Anwendung langfristig strukturiert werden? Wie können mehr E/A-Operationen ohne Blockierung durchgefĂŒhrt werden?
Distribution Erweiterungsframework (PIE / PECL) Compose, reines PHP Fork PHP + ext-async
VollstÀndige Version (2026) Version 5 wird beibehalten, Version 7 ist in Vorbereitung Standardproduktion Experimentell

Phalcon und TrueAsync teilen eine intuitive Vorgehensweise – kritische Aufgaben nĂ€her an den nativen Code heranzufĂŒhren –, aber nicht das gleiche Problem. Phalcon optimiert den Einstieg in das Framework. TrueAsync optimiert das Warten in der GeschĂ€ftslogik. Symfony hingegen konzentriert sich nicht auf das native Framework, sondern auf Struktur und InteroperabilitĂ€t.

Die Platzierung von nativem Code ist eine Architekturentscheidung, keine Modefrage. Man kann nativen Code auf der Framework-Ebene haben, ohne nativen Code zur Laufzeit zu verwenden. Man kann nativen Laufzeitcode einfĂŒhren, ohne Phalcon aufzugeben. Man kann seine Architektur in Symfony auch ohne TrueAsync strukturieren. Die Kombinationen hĂ€ngen von wohlĂŒberlegten EinschrĂ€nkungen ab – nicht von einer „alt vs. neu“-ErzĂ€hlung.

Symfony – eine weitere Optimierung, kein universeller Ersatz

Symfony bietet eine solide strukturelle Grundlage: HttpKernel, Routing, Dependency Injection, Sicherheit, Konsole, Cache, Messenger, Doctrine-Integration und ausgereifte Tools. FĂŒr eine Plattform, die ihren Code ĂŒber Jahre hinweg dokumentieren, prĂŒfen und weiterentwickeln muss, ist es eine verlĂ€ssliche Organisationsgrundlage.

Das Argument ist nicht nur technischer Natur. Es ist auch organisatorischer Natur: einfachere Rekrutierung, umfangreiche Dokumentation, Standardintegrationen, eine klare Entwicklungsrichtung ĂŒber mehrere PHP-Versionen hinweg, ohne auf eine fĂŒr jedes Upgrade kompilierte Framework-Erweiterung angewiesen zu sein.

Symfony optimiert jedoch eine andere EinschrĂ€nkung als Phalcon. Es verspricht keinen Router in C. Es verspricht eine wartbare, testbare und integrierbare Anwendung – mit einem Ökosystem, das einen Großteil der ĂŒbergreifenden KomplexitĂ€t (Authentifizierung, Warteschlangen, Observability, Validierung) aufnimmt.

Symfony ist die richtige Antwort, wenn die dominierenden Kosten nicht mehr die Mikrosekunde des MVC-Bootstrap sind, sondern:

  • die Liefergeschwindigkeit fĂŒr ein Produkt in der Evolution
  • die FĂ€higkeit, Personal zu rekrutieren und auszubilden
  • Integration mit externen Diensten und Standards
  • die Klarheit der Anwendungsbereiche auf lange Sicht

Symfony ist nicht automatisch besser. Es handelt sich um eine weitere Optimierung – und die damit verbundenen Migrationskosten sollten nicht unterschĂ€tzt werden.

TrueAsync – native, experimentelle, nicht-automatische Laufzeitumgebung

TrueAsync spielt eine dritte Rolle, unabhĂ€ngig von der Wahl des Frameworks. Es handelt sich um eine experimentelle PHP-Erweiterung (ext-async), eine Engine-Integration, einen libuv-Scheduler, Koroutinen und nicht-blockierende Ein-/Ausgabe fĂŒr bekannte Funktionen (curl, Dateien, in einigen FĂ€llen PDO). Die API bietet spawn(), await() und delay() – ohne dabei jede Funktion im Code als asynchron zu kennzeichnen.

Wir sollten hinsichtlich der Reife von TrueAsync vorsichtig bleiben. TrueAsync ist experimentell: PHP 8.6+, individuelle Anpassung, sich entwickelnde API, RFC in Arbeit. Es ist weder ein offizieller PHP-Standard noch ein direkter Ersatz fĂŒr Swoole oder Amp. Es ist auch nicht „die Zukunft“ von PHP – es ist eine Richtung, die anhand konkreter AnwendungsfĂ€lle evaluiert werden muss.

TrueAsync lÀsst sich sowohl hinter einem Phalcon-Monolithen als auch hinter Symfony implementieren. Die native Laufzeitumgebung erfordert keine Framework-Migration. Synchrone CRUD-Operationen im Backend sind in der Regel nicht erforderlich. Bei E/A-intensiven Prozessen, parallelen HTTP-Aggregationen und Dateipipelines kann TrueAsync jedoch sinnvoll sein, wenn die Metriken dies rechtfertigen und eine benutzerdefinierte Version akzeptabel ist.

Bei Darkwood betrachten wir TrueAsync als AusfĂŒhrungsstrategie – je nach Kontext austauschbar – und nicht als grundlegende Architekturentscheidung. Architektur definiert den Workflow und seine Grenzen. Der asynchrone Treiber legt lediglich die Art und Weise der AusfĂŒhrung fest.

Wann man Phalcon nicht verlassen sollte

Migration ist nicht immer wĂŒnschenswert. Manche Teams sollten auf Phalcon bleiben – und das ist eine rationale Entscheidung, kein EingestĂ€ndnis des Scheiterns.

Übernachten Sie, wenn:

Das Team beherrscht Phalcon und die ProduktivitĂ€t ist hoch. Die Kosten fĂŒr das erneute Erlernen und Überarbeiten ĂŒbersteigen den erwarteten Nutzen.

  • Die Service-Level-Objectives (SLOs) – Latenz, Durchsatz, StabilitĂ€t – wurden erreicht, und die Lastprofile haben sich nicht grundlegend verĂ€ndert.
  • Das Produkt wird nur wenig weiterentwickelt – Fehlerbehebungen, Wartung, keine grundlegenden Überarbeitungen oder zahlreiche Ökosystemintegrationen.
  • Migrationskosten > GeschĂ€ftswert – Überarbeitungen, doppelte AusfĂŒhrungen, Regressionsrisiko, monatelanger Funktionsstopp.
  • Eine schrittweise Modernisierung ist ausreichend – Upgrade auf Phalcon 5, EinfĂŒhrung von PIE, Verwendung neuer Komponenten (Queue, Container, Contracts) ohne Änderung des Frameworks

In solchen FĂ€llen ist die Migration zu Symfony nur, weil „es alle anderen auch tun“, ein strategischer Fehler. Phalcon v5 im Jahr 2026 ist nicht dasselbe wie Phalcon 3, das auf PHP 7 feststeckt: Das Projekt entwickelt sich weiter, das Ökosystem wird unterstĂŒtzt, und das native Framework bleibt so lange relevant, wie es den Flaschenhals darstellt.

Vier mögliche Flugbahnen

FĂŒr eine groß angelegte PHP-Plattform sind vier Lösungen plausibel – keine davon ist standardmĂ€ĂŸig die „richtige“:

1. Bei Phalcon bleiben und modernisieren. Upgrade auf Version 5, neue Komponenten einfĂŒhren, interne Workflows refaktorisieren und die Beobachtbarkeit verbessern. Optional TrueAsync fĂŒr E/A-intensive Worker, falls der benutzerdefinierte Build akzeptabel ist.

2. Migration zu Symfony. Wenn ÖkosystembeschrĂ€nkungen, Personalbeschaffung oder Produktentwicklung im Vordergrund stehen. Schrittweise Migration (wie ein Strangulator), kein abrupter Wechsel. TrueAsync ist in einer spĂ€teren Phase optional.

3. Hybridarchitektur. Phalcon verarbeitet kritischen Datenverkehr oder APIs mit sehr geringer Latenz; Symfony kĂŒmmert sich um neue, abgegrenzte Kontexte oder sich entwickelnde Schnittstellen. Dies ist eine bewusste Koexistenz mit expliziten API-VertrĂ€gen zwischen den beiden Welten.

4. FĂŒhren Sie die native Laufzeitumgebung unabhĂ€ngig vom Framework ein. TrueAsync (oder eine asynchrone Alternative) auf Workern und I/O-Pipelines, ohne das Web-Framework zu modifizieren. Phalcon oder Symfony in der HTTP-Schicht; die asynchrone Laufzeitumgebung in der AusfĂŒhrungsschicht.

TrueAsync ist in keinem Pfad außer dem vierten automatisch Teil der Antwort – und selbst dann erst nach der Messung. Es ist keine Voraussetzung fĂŒr die Migration.

Grenzen vor Rahmenwerken

Hier ist die These, die wir bei Darkwood vertreten und die wir in fast allen Architekturprojekten wiederfinden: Viele „Framework-Migrationen“ sind eigentlich Probleme schlecht definierter Grenzen.

Ein veralteter Monolith leidet nicht immer unter einem langsamen Router. Vielmehr liegt das Problem darin, dass drei GeschÀftssprachen ohne klare Abgrenzung nebeneinander existieren: Empfang (Ereignisse empfangen und validieren), Veröffentlichung (Speichern und Benachrichtigen) und Lesen (optimierte Ansicht bereitstellen). Im Code nutzen diese drei Funktionen dieselben Handler, dieselben Dienste und dieselben Cron-Skripte. Ein Framework-Wechsel ohne Neudefinition dieser Grenzen verschiebt das Problem lediglich.

Mehrere Konzepte helfen uns, dieses Problem zu benennen – ohne den Artikel in einen DDD-Katalog zu verwandeln:

Abgegrenzte Kontexte. Jeder GeschĂ€ftsbereich hat sein eigenes Vokabular und seine eigenen Invarianten. Auf einer Redaktionsplattform hat „veröffentlichter Artikel“ auf der Verarbeitungsseite (dem zu verarbeitenden Ereignis) und auf der Leseseite (dem vorab berechneten Dokument, das ausgeliefert werden soll) unterschiedliche Bedeutungen. Die ZusammenfĂŒhrung beider in einem einzigen Modul fĂŒhrt zu den verschachtelten Handlern, die wir ĂŒberall sehen.

DomĂ€nendienste vs. Anwendungsdienste. GeschĂ€ftsregeln (z. B. Inhaltsvalidierung, Berechnung des Veröffentlichungsstatus) sollten nicht im selben Bereich wie die technische Koordination (z. B. Cache-Invalidierung, Busaufruf, Neuerstellung eines Lesemodells) implementiert werden. Wenn alles als „Dienst“ behandelt wird, lĂ€sst sich nichts isoliert testen.

Orchestrierungsschicht vs. DomĂ€nenschicht. Die Orchestrierungsschicht steuert die einzelnen Schritte; die DomĂ€ne entscheidet. „Speichern, dann ungĂŒltig machen, dann neu erstellen“ ist Orchestrierung. „Ist dieser Inhalt veröffentlichungsfĂ€hig?“ ist DomĂ€ne. Werden beide in einem Controller oder Message-Handler vermischt, ist eine unabhĂ€ngige Verwaltung unmöglich.

CQRS und Lesemodelle. Auf leseintensiven Plattformen unterliegen Schreibpfad (kanonischer Speicher) und Lesepfad (abgeleitete Modelle, Cache, aggregierte Dokumente) gegensĂ€tzlichen Anforderungen. Die Verwendung einer einzigen ORM-Schicht fĂŒr beide Pfade fĂŒhrt zu kostspieligen Kompromissen. Die explizite Trennung von Schreib- und Lesepfad ist kein Dogma, sondern eine natĂŒrliche Reaktion auf ein analysiertes Arbeitslastprofil.

Ereignisgesteuerte und ereignisgesteuerte Konsistenz. Nach der Persistenz können Cache-Invalidierung und Lesemodellrekonstruktion asynchron erfolgen. Dies ist akzeptabel, vorausgesetzt, die Grenze zwischen starker und ereignisgesteuerter Konsistenz ist klar definiert.

flowchart TB
  subgraph sync_boundary [Boundary synchrone]
    Validate[Validation]
    Persist[Persistence canonique]
    Validate --> Persist
  end
  subgraph async_boundary [Boundary eventual]
    Invalidate[Invalidation]
    Rebuild[Rebuild read model]
    Invalidate --> Rebuild
  end
  Persist -->|"événement"| Invalidate

Der synchrone Grenzwert – Validierung und Persistenz – muss den ACID-Prinzipien entsprechen: Entweder wird der Inhalt akzeptiert und gespeichert, oder der Vorgang schlĂ€gt sauber fehl. Der potenzielle Grenzwert – UngĂŒltigmachung und Rekonstruktion – kann eine Verzögerung tolerieren, sofern das lesende System dies akzeptiert (z. B. durch einen Mechanismus zur Verarbeitung veralteter Daten).

Bei der PrĂŒfung einer Plattform im Hinblick auf eine Migration lautet die erste Frage nicht: „Phalcon oder Symfony?“ Sondern: Wo verlaufen die Grenzen heute und wo sollten sie verlaufen? Das Framework ist lediglich ein Container. Ein sauberer, aber schlecht designter Container ist immer noch besser als ein gut designter, der dieselben geschĂ€ftlichen Probleme birgt.

PEFT-Archetyp – Leseintensive redaktionelle Plattform – Muster

Um die Argumentation zu verankern, ohne uns an eine reale Architektur zu halten, stellen wir uns die PEFT (High Traffic Editorial Platform) vor – einen Archetyp, keinen Kundenfall.

PEFT vereint Funktionen, die fĂŒr große Content-Plattformen typisch sind:

  • Dominanter Lesepfad - APIs und das öffentliche Web liefern vorab berechnete Ansichten, nicht die aktuelle relationale Datenbank.
  • Ereignisgesteuerte Datenerfassung – Ein vorgelagertes System sendet Veröffentlichungsereignisse; Worker verarbeiten, validieren und speichern diese.
  • Abgeleitete Modelle - nach Persistenz, UngĂŒltigmachung und Rekonstruktion optimierter Lesestrukturen (Cache, aggregierte Dokumente)
  • Multi-Surface-Monolith – Web, APIs und Stapelverarbeitung teilen sich eine Codebasis, die oft in Modulen organisiert ist.
  • Progressive Extraktion – einige abgegrenzte Kontexte beginnen sich zu lösen, aber der Kern trĂ€gt weiterhin einen erheblichen Teil des Datenverkehrs.
flowchart LR
  CMS[Source de contenu] --> Bus[Bus de messages]
  Bus --> Workers[Workers ingestion]
  Workers --> Store[(Stockage canonique)]
  Workers --> Cache[(ModÚles dérivés)]
  Cache --> APIs[APIs read-heavy]
  APIs --> Clients[Clients]

Betrachtet man PEFT aus der Perspektive von Grenzen, so offenbart es drei implizite abgegrenzte Kontexte:

  1. Erfassung – Empfangen, Validieren und Normalisieren eingehender Ereignisse
  2. Veröffentlichung – den kanonischen Zustand aufrechterhalten, Signale des Wandels aussenden
  3. Lesen – Bereitstellung optimierter Lesemodelle, unabhĂ€ngig vom Schreibschema

Das CQRS-Muster ist hier naheliegend: Kanonische Speicher (Schreibmodell) und abgeleitete Modelle (Lesemodelle) haben unterschiedliche Lebenszyklen. UngĂŒltigmachung und Rekonstruktion fĂŒhren zu einer angenommenen potenziellen Konsistenz zwischen Schreiben und Lesen – akzeptabel, solange die SLOs fĂŒr AktualitĂ€t explizit definiert sind.

Bei diesem Archetyp basieren architektonische Entscheidungen nicht auf der Wahl des Routers. Sie basieren auf Folgendem:

  • wo die Logik der Datenaufnahme und -invalidierung angesiedelt ist (verschrĂ€nkte Handler vs. explizite ArbeitsablĂ€ufe)
  • wo die Grenze zwischen DomĂ€ne und Orchestrierung verlĂ€uft
  • ob der Flaschenhals im synchronen MVC-Prozess oder in der E/A der Worker liegt.
  • wenn das Produkt eine umfassende Reorganisation oder eine lange Stabilisierungsphase erfordert.

PEFT kann auf Phalcon bleiben, solange die SLOs erfĂŒllt werden und das Team stabil ist. PEFT kann zu Symfony migrieren, wenn das Ökosystem und die Fluktuation dies erfordern. PEFT kann hybrid betrieben werden. TrueAsync kommt nur dann zum Einsatz, wenn die Worker-I/O zum messbaren Flaschenhals wird – unabhĂ€ngig vom Framework. In jedem Fall muss jedoch die Grenzen neu definiert werden, bevor die Technologiewahl getroffen wird.

Erkenntnisse - Drei Archetypen von People of Color

Der in der Einleitung erwĂ€hnte Proof of Concept – eine stark frequentierte PHP-Plattform, performanter Legacy-Code und Workflows, die ĂŒber das MVC-Modell hinausgehen – veranschaulicht einen ersten Archetyp. Er ist nicht der einzige. In unserer Beratungs- und ValidierungstĂ€tigkeit begegnen wir regelmĂ€ĂŸig drei Profilen, bei denen der richtige erste Schritt selten vom Framework abhĂ€ngt. Er hĂ€ngt vielmehr von der Art der dominanten Altlasten ab.

Archetyp 1 – Legacy-Monolith, komplexe GeschĂ€ftsregeln. Dies trifft auf den ersten Proof of Concept zu: eine ausgereifte Plattform, komplexe GeschĂ€ftslogik und wenig Spielraum fĂŒr Überarbeitungen. Der sinnvolle Ansatz bestand nicht darin, Phalcon und Symfony theoretisch zu vergleichen, sondern die impliziten Kontextgrenzen abzubilden, die Grenzen der synchronen Konsistenz zu identifizieren und eine schrittweise Extraktion (Strangler) fĂŒr jeden Kontext vorzuschlagen. Das Framework – Phalcon oder Symfony – trat in den Hintergrund: Die Herausforderung bestand darin, die GeschĂ€ftsregeln aus dem Labyrinth der Handler zu extrahieren.

Archetyp 2 – Greenfield Symfony. Neues Produkt, wachsendes Team, zahlreiche geplante Integrationen. Hier lag der Fokus auf DomĂ€nenmodellierung und klar definierten Modulen – nicht asynchron, nicht in der nativen Laufzeitumgebung. Wir haben die Grenzen frĂŒhzeitig festgelegt (DomĂ€nendienste, Anwendungsdienste, separate Leseschichten), um in Symfony nicht das Spaghetti-artige Geflecht zu erzeugen, vor dem wir anderswo geflohen waren. Messenger reichte fĂŒr die ersten Workflows aus; explizite Orchestrierung war erst mit zunehmender KomplexitĂ€t erforderlich.

Archetyp 3 – Automatisierung und E/A-intensive Workflows. Verarbeitungsketten (Inhaltsanreicherung, mehrere API-Aufrufe, Dateipipelines – Profile Ă€hnlich einigen KI-Ketten). Der Flaschenhals war nicht der Router oder das DomĂ€nenmodell, sondern die E/A-Wartezeit und das Fehlen eines expliziten AusfĂŒhrungsmodells. Wir haben den Workflow zunĂ€chst lesbar gemacht – Schritte benannt, Fehler isoliert, synchrone/asynchrone Grenzen definiert – und erst dann je nach Umgebung TrueAsync, Amp oder Fiber evaluiert. Die EinfĂŒhrung der asynchronen Laufzeitumgebung vor einem expliziten Workflow hĂ€tte das eigentliche Problem verschleiert.

Diese drei Archetypen fĂŒhren nicht zum gleichen Vorgehen. Sie teilen jedoch eine gemeinsame Vorgehensweise: Die dominante EinschrĂ€nkung analysieren, bevor die Technologie ausgewĂ€hlt wird. Genau das wenden wir bei Darkwood an – sowohl bei Phalcon als auch bei Symfony, mit oder ohne TrueAsync.

Orchestrierung als Hauptanliegen

Web-Frameworks wie Phalcon, Symfony und Laravel sind fĂŒr einen einzigen Zyklus optimiert: HTTP-Anfrage → Verarbeitung → Antwort. Das ist ihre StĂ€rke. Gleichzeitig ist es aber auch ihre SchwĂ€che.

Ausgereifte Systeme wachsen schnell ĂŒber diesen Zyklus hinaus. Ein Veröffentlichungsereignis löst eine Kette aus: Validierung, Persistenz, Cache-Invalidierung, Rekonstruktion des Lesemodells, Benachrichtigung und BestĂ€tigung an einen Bus. Diese Kette lĂ€sst sich nicht in einem Controller kapseln. Sie ist fragmentiert ĂŒber Message-Handler, Dienste, CLI-Skripte und Cronjobs verteilt – manchmal ĂŒber Jahre der Entwicklung, manchmal unter dem Druck von Produktionsbereitstellungen, bei denen Geschwindigkeit Vorrang vor Struktur hatte.

Wir bezeichnen dies als ein Orchestrierungsproblem: die explizite Koordination von GeschĂ€ftsprozessen, die ĂŒber den Anfrage-Antwort-Zyklus hinausgehen. Und wir sind der Ansicht, dass dieses Problem die gleiche architektonische Aufmerksamkeit verdient wie die Wahl des Frameworks.

Eine workflowzentrierte Architektur ersetzt nicht das MVC-Modell, sondern ergĂ€nzt es. Der Controller bleibt granular: Er empfĂ€ngt, delegiert und antwortet. Der Workflow trĂ€gt die mehrstufige Logik: Er benennt die Schritte, behandelt Fehler in jedem Schritt, definiert Konsistenzgrenzen und trennt – idealerweise – die Pipeline-Beschreibung vom AusfĂŒhrungsmodell.

Hier unterscheiden wir uns von den AnsĂ€tzen „Alles in Messenger“ oder „Alles in einem ĂŒbergeordneten Dienst“. Messenger ĂŒbermittelt Nachrichten; es orchestriert nicht nativ eine mehrstufige Pipeline mit austauschbaren AusfĂŒhrungsstrategien. Ein 800-zeiliger Dienst, der alles orchestriert, ist zwar möglich, aber undurchsichtig und nicht testbar.

Bei Darkwood betrachten wir Orchestrierung als ebenso wichtig wie Schnittstellen und Framework-Auswahl. Nicht etwa, weil ein einzelnes Tool alles lösen könnte, sondern weil das VernachlĂ€ssigen der Orchestrierung die hĂ€ufigste Ursache fĂŒr gescheiterte Migrationen ist. Man wechselt das Framework, die Handler bleiben verstrickt, und die Schulden bleiben bestehen.

Ablauf – Trennung des Workflows vom AusfĂŒhrungsmodell

Diese Beobachtung veranlasste uns zur Entwicklung von Flow – einer Open-Source-Komponente, nicht eines Ersatz-Frameworks.

Flow existiert, weil wir bei Migrationen in der Praxis immer wieder dasselbe Muster beobachtet haben: Workflow-Logik findet in Symfony oder Phalcon keinen natĂŒrlichen Platz. Sie landet in anonymen Handlern, Cron-Skripten oder Sammeldiensten. Flow gibt ihr einen architektonischen Platz: eine explizite Pipeline, bestehend aus Flow (dem Schritt), Job (dem Unit-Job) und Ip (dem Datentoken, das die Pipeline durchlĂ€uft).

Das wichtigste architektonische Unterscheidungsmerkmal ist fĂŒr uns jedoch DriverInterface.

Gleicher Arbeitsablauf. Austauschbares AusfĂŒhrungsmodell.

Eine Veröffentlichungspipeline – Validierung, Speicherung, UngĂŒltigmachung, Neuaufbau – kann rein synchron, mit PHP Fibers, Amp-Coroutinen, einer ReactPHP-Ereignisschleife, einem Swoole-Worker oder ĂŒber TrueAsync ausgefĂŒhrt werden. Die GeschĂ€ftslogik bleibt unverĂ€ndert. Nur das AusfĂŒhrungsmodell Ă€ndert sich. Dies ist eine Infrastrukturentscheidung, keine DomĂ€nenentscheidung.

Flow bietet derzeit mehrere Treiber an: FiberDriver (Standard, keine Erweiterung), AmpDriver, ReactDriver, SwooleDriver, SpatieDriver, ParallelDriver und TrueAsyncDriver (experimentell, ext-async). Jeder Treiber ist fĂŒr einen anderen Anwendungsfall geeignet – bestehende Integration, dedizierter Worker, experimentelle native Ein-/Ausgabe.

use Flow\Driver\FiberDriver;
use Flow\Driver\TrueAsyncDriver;
use Flow\Flow\Flow;
use Flow\Ip;

// Le workflow est stable ; seul le driver change selon l'environnement
$driver = TrueAsyncDriver::isSupported()
    ? new TrueAsyncDriver()
    : new FiberDriver();

$flow = (new Flow(job: new ValidateMessage(), driver: $driver))
    ->fn(new Persist())            // domaine : boundary synchrone
    ->fn(new InvalidateCache())    // orchestration
    ->fn(new RebuildReadModel());  // I/O potentiellement async

$flow(new Ip($message));
$flow->await();

Symfony empfĂ€ngt das Ereignis – ĂŒber Messenger oder einen internen Endpunkt – und leitet es an Flow weiter. Jede fn()-Funktion stellt einen benannten Schritt dar; Fehler können mit errorJob isoliert werden. TrueAsync wird nicht zuerst verwendet: FiberDriver ist der Standardwert, misst die Situation und wĂ€hlt dann diejenige Pipeline aus, die dies rechtfertigt.

Wichtige Punkte fĂŒr den Produktiveinsatz: FiberDriver und TrueAsyncDriver dĂŒrfen nicht im selben Prozess verwendet werden (TrueAsync blockiert Userland Fibers, solange es aktiv ist). Threads, KanĂ€le oder TaskGroups sollten nicht von Anfang an eingefĂŒhrt werden – dies erhöht die BetriebskomplexitĂ€t unnötig.

Flow ist nicht die einzige Lösung. Symfony Messenger, eine benutzerdefinierte Pipeline oder ereignisgesteuerte Orchestrierung können fĂŒr einfache Workflows ausreichen. Flow verfolgt einen ambitionierteren Ansatz: Orchestrierung wird zu einem grundlegenden Architekturelement, mit einer expliziten Trennung zwischen dem Was (der Pipeline) und dem Wie (dem Treiber).

Wir behaupten nicht, dass Flow die Zukunft ist. Wir sagen aber, dass explizite Orchestrierung – mit der Möglichkeit, die AusfĂŒhrungsstrategie zu Ă€ndern, ohne die GeschĂ€ftslogik neu zu schreiben – eine Richtung ist, die uns komplexe Systeme gelehrt haben, ernst zu nehmen.

Wenn Sie sich fĂŒr eine Migration entscheiden – Prinzipien, kein Handlungsplan

Wenn man sich fĂŒr den Weg „Migration zu Symfony“ entscheidet – und nur dann – sind einige wenige Prinzipien besser als ein starrer FĂŒnf-Phasen-Plan.

Erst messen, dann umziehen. Identifizieren Sie die tatsÀchlichen EngpÀsse: MVC, Workflows, I/O-Worker, Volt/PHQL-Kopplung, unscharfe Schnittstellen. Migrieren Sie kein leistungsstarkes Framework, um ein Orchestrierungs- oder Modellierungsproblem zu lösen.

Definieren Sie zuerst die Grenzen neu. Bevor Sie ein Modul neu schreiben, benennen Sie dessen abgegrenzte Kontexte, Lesemodelle und Konsistenzgrenzen. Der Strangler arbeitet kontextbezogen, nicht schichtweise.

Strangler statt Big Bang. Neue Symfony-Anwendung parallel; progressives Routing Endpunkt fĂŒr Endpunkt. API-VertrĂ€ge bleiben wĂ€hrend der Koexistenz erhalten.

Erst synchron, dann asynchron. Extrahieren Sie die Workflows synchron. Sorgen Sie fĂŒr eine lesbare und testbare Abfolge, bevor Sie TrueAsync oder eine andere asynchrone Laufzeitumgebung einfĂŒhren.

Asynchrone Verarbeitung aktivieren. TrueAsync nur bei E/A-intensiven Pipelines in einer dedizierten Umgebung, mit der Möglichkeit zum Fallback.

Legacy-Muster werden je nach ihrer Art unterschiedlich migriert: Kettenhandler werden in einen expliziten Workflow integriert; Anwendungsmodule in Bundles oder Feature/-Ordner; Ad-hoc-Cache-Invalidierung in eine benannte Pipeline; blockierende E/A in Workern in eine optionale asynchrone Laufzeitumgebung; ORM/PHQL zu Doctrine – oft die aufwĂ€ndigste Migration, die separat behandelt werden muss.

Ein CRUD-Endpunkt lĂ€sst sich als einfacher Symfony-Controller implementieren. Eine Kette aus Datenerfassung, Cache und abgeleitetem Modell erfordert jedoch eine Workflow-Überlegung – und wahrscheinlich eine Neudefinition der Grenzen – bevor ein Tool ausgewĂ€hlt wird.

Was man nicht tun sollte

Manche Fehler treten immer wieder auf – auch dann, wenn eine Migration nicht erforderlich ist.

StandardmĂ€ĂŸig migrieren, weil Phalcon "veraltet" ist – wĂ€hrend Version 5 weiterhin gepflegt wird, die SLOs gelten und das Team produktiv ist.

Ignorieren Sie das moderne Phalcon v5 - Queue, Container, Contracts, PIE - und behandeln Sie das Framework so, als ob es auf einer alten Version feststecken wĂŒrde.

Frameworks wechseln, ohne Grenzen neu zu ziehen – der Spaghetti-Monolith ĂŒberlebt die NamensraumĂ€nderung.

TrueAsync flÀchendeckend einzusetzen ignoriert dessen experimentelle Reife und erhöht den operativen Aufwand.

Die Verwechslung von I/O-ParallelitĂ€t und CPU-ParallelitĂ€t fĂŒhrt unabhĂ€ngig vom Framework zu Fehlentscheidungen.

Umwandlung einer Orchestrierungsbibliothek in ein Framework – eine systematische Pipeline fĂŒr jedes Feature ersetzt ein Anti-Pattern durch ein anderes.

Wir prĂ€sentieren TrueAsync als bereit fĂŒr den universellen Produktiveinsatz – im Jahr 2026 gilt es, diesen Weg zu gestalten und zu testen.

Die EinfĂŒhrung von Async vor einem expliziten Workflow verbirgt die Orchestrierungsschulden hinter einer AusfĂŒhrungsschicht.

Realistische Risiken

Risiko Risikominderung
UnterschÀtzte Migration Strangler; Umfang durch Endpunkt; Grenzen zuerst
Schlecht definierte Grenzen Kontextzuordnung vor dem Umschreiben; Explizites CQRS bei leseintensiven Daten
Instabile TrueAsync-API Optionaler Treiber; Fiber-Fallback; Festgelegte Version
Benutzerdefiniertes PHP erstellen BeschrÀnkung auf Worker-/dedizierte Umgebungen
Doktrin + Wettbewerb EM pro Arbeitseinheit; kurze Transaktionen
Phalcon/Symfony-Koexistenz Explizite API-VertrÀge; transparentes Routing; Observability
Überkomplexe Orchestrierung Pipelines nur fĂŒr reale ArbeitsablĂ€ufe

Fazit – ĂŒber den Rahmen hinaus: explizite Orchestrierung

Phalcon war nie ein Fehler. FĂŒr leseintensive Plattformen war die Optimierung des Frameworks in C eine schlĂŒssige Antwort – und ist weiterhin vertretbar, solange der MVC-Ansatz die dominierende EinschrĂ€nkung darstellt.

Symfony ist nicht automatisch besser. Es geht um eine andere Art der Optimierung: Struktur, Ökosystem, organisatorische Wartbarkeit. Das ist relevant, wenn diese EinschrĂ€nkungen die Kosten des nativen Frameworks ĂŒberwiegen.

TrueAsync ist nicht automatisch die Zukunft. Es handelt sich um eine experimentelle Laufzeitoptimierung – relevant fĂŒr bestimmte E/A-Profile, unabhĂ€ngig von der Framework-Wahl, keine Voraussetzung fĂŒr die Modernisierung.

Phalcon, Symfony und TrueAsync platzieren native – oder strukturierte oder asynchrone – Funktionen auf unterschiedlichen Ebenen des Stacks. Doch architektonische Kompetenz im Jahr 2026 endet hier nicht.

Die Branche hat jahrelang Frameworks optimiert. Die nĂ€chste Herausforderung fĂŒr komplexe Systeme liegt nicht nur in der Framework-Performance, sondern auch in der expliziten Orchestrierung, klar definierten DomĂ€nengrenzen und kontextsensitiven AusfĂŒhrungsstrategien. Die native Laufzeitumgebung dient als Implementierungshebel. Orchestrierung ist eine architektonische Entscheidung.

Bei Darkwood entwickeln wir Flow, weil uns ausgereifte Systeme eines gelehrt haben: Sobald Workflows ĂŒber den Anfrage-Antwort-Zyklus hinausgehen, benötigen sie einen architektonischen Rahmen – und die AusfĂŒhrungslogik muss von der koordinierten GeschĂ€ftslogik getrennt werden. Flow verfolgt einen Ansatz, bei dem Orchestrierung zu einem grundlegenden Element wird und nicht nur eine Ansammlung von Handlern und Cronjobs darstellt.

Die Wahl des Frameworks ist weiterhin wichtig. Sie ist jedoch nur ein Teil der Gleichung. Die eigentliche Frage lautet nicht nur: „Wo befindet sich der native Code?“, sondern auch: Wo befinden sich die Workflows?, Wo verlaufen die Schnittstellen? und Wer trĂ€gt die Verantwortung fĂŒr die Orchestrierung? – in einer Architektur, die zehn Jahre Bestand haben muss.

Quellen

Dieser Artikel stĂŒtzt sich auf verschiedene öffentliche Quellen, persönliche Experimente und Open-Source-Arbeiten zur modernen PHP-Architektur, asynchronen AusfĂŒhrung und Workflow-Orchestrierung.

Frameworks und Laufzeitumgebung

  • Phalcon Framework (offizielle Website): offizielle Dokumentation, Framework-Historie und Zephir/C-Architektur.
  • Symfony Framework: Referenz fĂŒr das PHP-Ökosystem mit Fokus auf Wartbarkeit und Anwendungsarchitektur.
  • TrueAsync-Dokumentation: Dokumentation der experimentellen asynchronen Laufzeitumgebung basierend auf ext-async.

Darkwood-Referenzen

Ein Teil der hier vorgestellten Überlegungen stammt aus Arbeiten, die bei Darkwood zur Trennung zwischen GeschĂ€ftsworkflow und AusfĂŒhrungsmodell durchgefĂŒhrt wurden.

  • Flow – Darkwood-Orchestrierungsbibliothek: Open-Source-Implementierung von GeschĂ€ftsprozess-Pipelines mit austauschbaren AusfĂŒhrungstreibern (Fiber, Amp, ReactPHP, Swoole, Parallel, TrueAsync). Experimentelle UnterstĂŒtzung fĂŒr TrueAsyncDriver ist als Proof of Concept zur Erforschung von Laufzeit-entkoppelter Orchestrierung verfĂŒgbar.
  • Slidewire-PrĂ€sentationsquelltext: Die Folien der zu diesem Artikel gehörenden PrĂ€sentation, die mit Slidewire erstellt wurde, sind in diesem Repository öffentlich verfĂŒgbar.

Anmelden um auf diesen Beitrag zu reagieren

🚀 1

Site

  • Sitemap
  • Kontakt
  • Impressum

Network

  • Hello
  • Blog
  • Apps
  • Photos

Social

Darkwood 2026, alle Rechte vorbehalten