
Deshalb bauen wir Jardis
Komplexe Software läuft seit Jahrzehnten auf CRUD und scheitert. Domain-Driven Design war die Antwort, nur zu aufwendig in der Umsetzung. Darum baue ich Jardis.
Es gibt ein Muster in der Softwareentwicklung, das seit Jahrzehnten stabil ist, und im PHP-Ecosystem besonders deutlich. Sobald die Anforderungen über einfaches CRUD hinausgehen, steht ein Team vor einer Wahl, die eigentlich keine ist: entweder das Fachliche sauber nach Domain-Driven Design modellieren und in eine hexagonale Architektur gießen, mit einem Umsetzungsaufwand, der am Eintritt höher liegt als bei CRUD, oder CRUD-Denken in eine komplexe Domäne pressen und hoffen, dass es hält. Die meisten wählen CRUD. Nicht aus Unwissenheit, sondern weil Zeit und Liefertermin nichts anderes zulassen.
Das Ergebnis ist vorhersehbar. Überdrehte Service-Schichten, die mit jeder neuen Anforderung fragiler werden. Anwendungen, die über die Jahre krank werden, weil das Wissen im Team nicht mit der Komplexität der Domäne Schritt hält, weil immer neue Techniken eingezogen werden in der Hoffnung, dass es danach besser wird, und weil der zugrunde liegende Ansatz für diese Art von Anforderung schlicht nicht gemacht ist. Das ist kein Vorwurf an Entwickler. Es ist ein strukturelles Problem: CRUD gibt einem Team kein Werkzeug, um mit fachlicher Komplexität umzugehen, und lässt es mit jeder Entscheidung allein.
Die richtige Antwort auf komplexe Domänen ist DDD, als Disziplin, das Fachliche sauber zu modellieren. Das ist seit Eric Evans' Standardwerk von 2003 klar. Aber die richtige Modellierung nutzt nichts, wenn ihre Umsetzung zu aufwendig ist, um sie Projekt für Projekt zu wiederholen. Genau hier setzt Jardis an. Nicht das Modellieren wird automatisiert, das bleibt menschliche Arbeit. Automatisiert wird die wiederkehrende Übersetzung des Modells in sauberen, hexagonal aufgesetzten Code. Damit ist DDD von Anfang an schneller, günstiger und zukunftssicherer als jede CRUD-Implementierung.
Das größere Muster
Legt man CRUD-Projekte in komplexen Domänen schematisch nebeneinander, zeigt sich immer derselbe Verlauf. Am Anfang läuft alles. Die Struktur ist übersichtlich, die Entscheidungen sind frisch, die Liefergeschwindigkeit ist hoch. Nach einigen Monaten passt das Bild nicht mehr zur Realität. Services rufen Services, die Services rufen. Validierungen verteilen sich über drei Schichten. Geschäftsregeln stecken in Controllern, in Event-Listenern, in Datenbank-Triggern, überall und nirgends sauber.
Das Symptom heißt schleichende Architektur-Erosion, und es ist nicht das Versagen eines konkreten Teams. Es ist die unvermeidliche Konsequenz, wenn CRUD-Denken auf Anforderungen trifft, die CRUD nicht abbilden kann. Das habe ich in fast jedem PHP-Projekt gesehen, das ich in den letzten Jahrzehnten begleitet habe, sobald die Komplexität über reines CRUD hinausging.
Die Konsequenz ist ökonomisch und inzwischen branchenweit sichtbar: Unternehmen zahlen nicht mehr für neue Features, sie zahlen für die Lebenserhaltung der Software, die sie schon haben. Refaktorisierungen, die nicht stattfinden. Migrationen, die sich verschieben. Senior-Entwickler, die zur Hälfte ihrer Zeit historische Eigenheiten erklären. Und jede neue fachliche Einheit wirft dasselbe Problem erneut auf, nicht einmal, sondern so oft, wie die Fachlichkeit es verlangt. Ein erheblicher Teil der Entwicklungskapazität fließt nicht in Fachlichkeit, sondern in den immer gleichen Kampf gegen eine Struktur, die für diese Art von Komplexität nie gedacht war. Was nicht sofort als Infrastruktur-Aufwand sichtbar wird, kommt später als technische Schuld zurück, weil das einmal Gebaute am Leben gehalten werden muss.
Die naheliegende Antwort lautet: bessere Disziplin. Mehr Code-Reviews, klarere Konventionen, strengere Architektur-Boards. Das habe ich lange geglaubt. Es ist nicht falsch, aber es reicht nicht. Die Drift kommt zurück, in jedem Projekt, in jeder Konstellation, in jeder Teamgröße. Irgendwann musste ich mir die Frage stellen, ob hier die falsche Variable optimiert wird, oder der falsche Ansatz.
DDD war nie das Problem, seine Umsetzung war es
Domain-Driven Design nach Eric Evans war nie unbekannt. Das Buch erschien 2003, und ich habe es vor über fünfzehn Jahren zum ersten Mal ernsthaft studiert. Trotzdem blieb es ein theoretischer Werkzeugkasten. In der Praxis wurde es regelmäßig abgetan: zu aufwendig, zu kompliziert, und der Nutzen zeigt sich zu spät. Was die Umsetzung angeht, war das nicht falsch. DDD klassisch zu implementieren kostet Wochen strategischer und taktischer Modellierung, verlangt Vokabular-Disziplin, und der Mehrwert wird erst sichtbar, wenn ein System Jahre läuft. Was diese Einschätzung übersah: aufwendig war die wiederkehrende Umsetzung, nicht das Modellieren, in dem der Wert entsteht. Genau das Modellieren ist über den Lebenszyklus der tragfähigere Weg, und ein höherer Eintritts-Aufwand, der sich über die Zeit auszahlt, ist die Definition eines Investments.
Was sich über die Jahre aber nicht abstellen ließ: die immer wiederkehrenden Situationen, in denen genau das schiefging, was Evans beschrieben hatte. Aggregate, deren Grenzen unklar waren. Bounded Contexts, die niemand sauber getrennt hatte. Eine Ubiquitous Language, die in der dritten Sprint-Planung schon nicht mehr stimmte. DDD hatte für jedes dieser Probleme eine Antwort. Aber zwischen Modell und lauffähigem Code lagen jedes Mal Wochen mechanischer Umsetzung, und die ließ sich nicht in jedem Projekt aufs Neue stemmen.
Also fiel die Wahl immer wieder auf das, was sich rechnen ließ: überdrehte CRUD-Anwendungen mit Service-Schichten, pragmatisch genug für den Liefertermin, fragil genug für die nächste Wartungsrunde.
Der Bruch: 2023
2023 habe ich entschieden, dem Thema einen eigenen Projektierungsschritt zu widmen, unabhängig von Kundenbudgets, als Investment. Die Frage war nicht mehr: Wie erkläre ich einem Kunden, dass DDD sich lohnt? Sondern: Lässt sich der Implementierungs-Aufwand so weit reduzieren, dass DDD von Anfang an die pragmatischere Wahl wird als CRUD?
Der Schlüssel lag in einer veränderten Perspektive auf DDD. Nicht als Werkzeug, eine Sammlung nützlicher Begriffe und Pattern, sondern als Brille. Ein Ordnungssystem, das zeigt, wo sich Strukturen wiederholen, wo Begriffe sich klären und wo aus wenigen Mustern ein vollständiger Unterbau entsteht.
Mit dieser Brille wurde sichtbar, dass der hohe Implementierungsaufwand kein einmaliger Akt am Projektanfang ist. Er wiederholt sich bei jedem Bounded Context und jedem Aggregat. Jedes Aggregat, also die zusammengehörigen Geschäftsobjekte, die gemeinsam konsistent bleiben müssen, braucht Repositories, Command- und Query-Handler, Hydratoren, Persistence, Domain Events. Die Form ist jedes Mal dieselbe. Was sich unterscheidet, sind die fachlichen Inhalte: welche Aggregate, mit welchen Feldern, mit welchen Regeln. DDD ist nicht eine aufwendige Sache, die man einmal erledigt. Es ist dasselbe aufwendige Muster, das bei jeder fachlichen Einheit erneut auftritt, mit unterschiedlichen Ausprägungen, aber identischer Struktur.
In dem Moment kippt etwas. Wenn die Form der Schicht in jedem Projekt gleich ist, dann ist diese Form nicht etwas, was geschrieben werden muss. Sie ist etwas, was erzeugt werden kann. Die fachliche Entscheidung, welche Aggregate es gibt, wo ihre Grenzen verlaufen, welche Invarianten sie schützen, bleibt menschliche Modellierungsarbeit, das ist taktisches DDD. Was daraus folgt, die immer gleiche hexagonale Hülle um dieses Modell, erzeugt Jardis aus dem modellierten Aggregat, jedes Mal nach derselben Regel, jedes Mal gleich.
Jardis vs. Hand-Coded DDD
Manuelles DDD erfordert tiefe Expertise, Monate an Setup und liefert Ergebnisse, die stark vom jeweiligen Entwickler abhängen. Jardis erzeugt den gesamten Infrastruktur-Layer konsistent, damit dein Team sich auf Domain-Logik konzentriert.
Nicht optimieren, eliminieren
An dieser Stelle löst sich das alte Dreieck aus schneller liefern, höhere Qualität, geringere Kosten nicht durch Balance auf. Ich habe es jahrelang als Naturgesetz behandelt, wie alle: eine Achse zieht, die andere gibt nach. Aber im Dreieck steckt eine unausgesprochene Annahme, nämlich dass der wiederkehrende Umsetzungs-Aufwand gegeben ist. Es löst sich auf, sobald der aufwendigste, weil immer wiederkehrende Arbeitsblock aus dem System fällt. Nicht beschleunigen. Eliminieren.
Was bleibt, ist die Arbeit, die tatsächlich Entscheidungen verlangt: Wo verlaufen die Grenzen der Bounded Contexts? Welche Regeln schützt ein Aggregat? Welche Domain Events lösen welche Folgeprozesse aus? Das ist Fachlichkeit. Die geht nicht weg. Sie wird sichtbarer, weil die wiederkehrende Routinearbeit sie nicht mehr zudeckt.
Warum zwei Perspektiven nötig waren
Diese Einsicht entsteht nicht aus einer einzelnen Perspektive. Ich habe lange als Entwickler gearbeitet, später als IT-Leiter, als CTO, als Geschäftsführer, am Ende als Gründer. Diese Stationen haben mich gezwungen, jede technische Entscheidung durch zwei Brillen zugleich anzusehen: die der ingenieurmäßigen Eleganz und die der kaufmännischen Belastbarkeit.
Wer nur Entwickler ist, sieht die handwerkliche Eleganz von DDD und verteidigt sie gegen den Vorwurf, sie sei zu aufwendig, hat aber kein Argument, warum sich dieser Aufwand für einen mittelständischen Auftraggeber auszahlen sollte. Wer nur Kaufmann ist, sieht die Foundation-Wochen als bedauerlichen Posten in der Kalkulation, hat aber keinen Hebel, um an der Ursache zu rütteln. Er verhandelt am Preis, nicht an der Arbeit selbst.
Erst die Kombination zwingt zur richtigen Frage. Nicht: Wie liefere ich schneller? Nicht: Wie senke ich die Kosten? Sondern: Welche Arbeit muss überhaupt noch von Menschen erledigt werden, und welche nicht? Die kaufmännische Perspektive verbietet, sich an den wiederkehrenden Aufwand zu gewöhnen. Die Entwickler-Perspektive sieht, dass diese Arbeit in jedem Projekt derselben Regel folgt, also automatisierbar ist. Eine ohne die andere bringt nichts. Zusammen zeigen sie den Hebel.
Das ist der Grund, warum Jardis nicht den nächsten Library-Vorschlag macht, sondern eine eigene Schicht im Stack einzieht. Library-Vorschläge bewegen sich innerhalb des Dreiecks. Eine Schicht, die den wiederkehrenden Aufwand erzeugt statt ihn zu schreiben, verlässt das Dreieck, weil sie eine andere Frage beantwortet.
Was zwei Jahre Bauen gelehrt haben
Ich baue Jardis seit zwei Jahren. In dieser Zeit hat sich die ursprüngliche These bestätigt, an einigen Stellen aber auch verschoben.
Bestätigt: Der Platform-Code sieht in jedem Projekt gleich aus. Aus Schema und Aggregat-Definition entsteht jedes Mal dieselbe hexagonale Architektur. Pro Aggregat sind das, je nach Komplexität, dutzende bis weit über hundert Dateien, die ein Team sonst von Hand schreibt. Jardis liefert sie in derselben Form, jederzeit, ohne Drift zwischen Projekten und ohne Übersetzungsverluste zwischen Entwurf und Code.
Verschoben: wie viel die zweite Schicht trägt. Der Platform-Code pro Aggregat ist das eine, das Deterministische. Darauf sitzt die fachliche Logik, und die beginnt nicht auf der grünen Wiese. In Jardis modelliere ich die Geschäftsabläufe als Flows: einen Ablauf pro Anwendungsfall, als Graph gezeichnet, angelehnt an die Lese-Konvention von BPMN. Jeder Knoten im Flow wird zu genau einer Klasse, und Jardis erzeugt diese Klassen vollständig als Gerüst. Pro Knoten hinterlege ich ein Requirement, eine fachliche Anforderung in normaler Sprache, und Jardis flicht sie direkt in den erzeugten Code ein, nicht in ein Dokument, das später keiner liest.
Erst diese Flow-Schicht macht den Punkt mit der KI tragfähig. Eine KI ergänzt die fachliche Logik nicht im offenen Raum, sondern in einem engen Korridor: das Was steht im Requirement am Code-Ort, das Womit im Input, das Wie in den dokumentierten Regeln der Plattform. Die Architektur schrumpft den Spielraum, bevor die KI loslegt, statt hinterher im Review die Scherben zu suchen. Das wurde mir beim Bauen klarer als beim Entwerfen.
Verschoben hat sich auch, wie wichtig eine saubere Sprache an dieser Stelle ist. Jardis entlastet, es ersetzt nicht. Jardis erzeugt keine Geschäftslogik. Jardis erzeugt die Schicht, auf der Geschäftslogik sitzt. Wer das verwischt, baut entweder ein No-Code-Versprechen, das nicht haltbar ist, oder einen Scaffold-Generator, der nichts Neues löst. Beides wäre zu wenig.
Sprint 1 war sauber. Sprint 10 ist Chaos.
Ihr habt die Architektur bewusst entworfen. Dann kam Zeitdruck, dann kamen Ausnahmen, dann kamen neue Leute ohne Kontext. Jardis macht Architektur-Grenzen unveränderlich, statt sie dem täglichen Pragmatismus zu überlassen.
Was Jardis ist, und was nicht
Jardis ist eine Plattform für Domain-Driven Design im PHP-Ecosystem. Aus Schema-Definitionen erzeugt sie zwei Schichten. Die erste ist der vollständige Platform-Code pro Aggregat: Repositories, Command- und Query-Handler, Hydratoren, Persistence, Domain Events, hexagonal aufgesetzt und jederzeit wiederholbar. Die zweite ist das Gerüst für die Fachlichkeit: die Geschäftsabläufe, als Flows modelliert, aus denen Jardis pro Schritt eine Klasse als Stub erzeugt, die Anforderung direkt im Code. Die Geschäftslogik selbst schreiben dann KI und Mensch in diese Stubs. Custom-Code überlebt jeden Rebuild. Die Foundation eines ernsthaften Projekts steht in Tagen, nicht in Monaten.
Was Jardis nicht ist: kein Ersatz für Symfony oder Laravel. Jardis beginnt ab dem Controller, die Wahl des Web-Frameworks bleibt frei. Kein No-Code-Werkzeug. Die fachliche Logik bleibt klassische Entwicklerarbeit. Kein Versprechen, Entwickler zu ersetzen. Jardis entlastet sie von der Arbeit, die in jedem Projekt dieselbe Form hat, damit mehr Zeit für die Arbeit bleibt, die tatsächlich Entscheidungen verlangt.
Ausblick
In den nächsten zehn Jahren Softwareentwicklung wird die Implementierung eines neuen Bounded Context oder Aggregats nicht mehr dasselbe wiederkehrende Bild zeigen, das ich seit dreißig Jahren sehe. Teams werden nicht mehr zwischen sauberer Architektur und Lieferfähigkeit wählen müssen. Entwicklungskapazität wird nicht mehr in immer gleiche Infrastruktur fließen, sondern in Fachlichkeit. Build the foundation of complex software in days, not months. Das ist kein Marketing-Satz, das ist die Arbeit, die ich in die PHP-Welt trage.
Es bleibt genug zu tun. Aber das, was in jedem Projekt gleich aussieht, muss nicht mehr von Hand gemacht werden.