Highlights
Was ein Klick auf Build erzeugt -- und was alles out of the box mitkommt.
Build-Artefakte
| Artefakt | Beschreibung |
|---|---|
Domain-Fassade {Domain} | Einstiegspunkt -- hält den Koffer, öffnet die Bounded Contexts |
{Domain}Context | Generierte Basisklasse der Context-Familie -- Kernel-Naht und Versionierung |
| BC-Fassade | Die Außentür: {agg}() (Read-Fassade), process() und rule-guarded Commands |
| Entities | Typisierte Attribute, Snapshots, Change-Tracking, Schema-Validierung |
| Aggregate (READ/WRITE getrennt) | READ-Fassade für Queries und Listen; WRITE-Fassade (Commands, event()) familienintern -- der hermetische Aggregate-Tree |
| Repository Pipeline | Query, Transform, Validate, Persist -- vier Stufen, eine Wahrheitsquelle |
| Command DTOs & Handler | Typisierte Eingabeobjekte + Orchestratoren mit Steps (Initialize, Validate, Hydrate, Apply, Persist) |
| Query DTOs & Handler | Einzel-Aggregate und paginierte Listen mit SQL-JOINs |
| Processes | BC-Level-Orchestrierung als Graph -- DTO, Orchestrator und Knoten-Stubs |
| Rules-Layer | Guard vor dem Command; Verletzung endet als DomainResponse mit Status 422 |
| Domain Events | Lifecycle (Created/Removed) und Operation Events -- modellgetrieben verkündet |
| FieldMap | Bidirektionales Mapping DTO <-> Datenbank |
| API Specifications | OpenAPI 3.1, AsyncAPI 3.0, Protocol Buffers 3 |
Für ein Aggregat mit fünf Entities entstehen so mehrere Dutzend Dateien -- konsistent, an einem Ort.
DDD von Grund auf
Domains, Bounded Contexts, Aggregate, Entities, Commands, Queries, Events -- die gesamte DDD-Architektur steht nach dem Build. Kein manuelles Verdrahten, kein Zusammensuchen von Patterns aus verschiedenen Libraries.
- Domain = Sammlung von Bounded Contexts. Jeder BC ist eine geschlossene Einheit mit eigener API.
- Aggregate = Konsistenzgrenze. Der hermetische Aggregate-Tree ist die einzige Tür zum Entity-Graph -- Schreiben ausschließlich über Commands.
- CQRS = Lesen und Schreiben getrennt. Lesen über die Read-Fassade des Aggregats, Schreiben über einen Prozess -- durch Konstruktion, nicht durch Konvention.
- Events = entkoppelte Kommunikation. Domain Events entstehen modellgetrieben und werden aus einem Prozess-Knoten verkündet.
$ecommerce = new Ecommerce($kernel);
// Schreiben über einen Prozess
$response = $ecommerce->sales()->process()->placeOrder($dto);
// Lesen über die Read-Fassade des Aggregats
$order = $ecommerce->sales()->order()->getOrderById($orderId);Hexagonale Architektur als Standard
Der erzeugte Code folgt der Hexagonalen Architektur -- Dependency-Pfeile zeigen nach innen, Infrastructure bleibt aussen.
Domain Layer (Core) -- Entities, Aggregates, Events
Application Layer (Support) -- Commands, Queries, Repositories, Validation
Infrastructure (Adapter) -- Datenbank, Cache, HTTP, Messaging- Core importiert niemals Adapter-Code. Durch Konstruktion, nicht durch Code-Reviews.
- Alle Infrastructure hinter Interfaces. Datenbank, Cache, Logger, Events -- austauschbar ohne eine Zeile Domain-Code zu ändern.
- PSR-kompatibel. PSR-3 (Logger), PSR-6/16 (Cache), PSR-7/18 (HTTP), PSR-11 (Container), PSR-14 (Events), PSR-15 (Middleware).
Versionierung next Level
Erzeugten Code weiterentwickeln, ohne ihn zu forken. Das ClassVersion-System löst das strukturell:
src/Ecommerce/Sales/Command/Handler/Step/
+-- HydrateCreateOrder.php <-- erzeugt (v1)
+-- v2/
+-- HydrateCreateOrder.php <-- eigene Anpassung (v2)- Automatische Auflösung. Existiert
v2/-- wird sie verwendet. Sonst greift die Basis. - Parallele Versionen. Mehrere Verhaltens-Versionen einer Klasse liegen im selben Build; die aktive wird pro Aufruf über Namespace-Injektion aufgelöst.
- Fallback-Ketten. v3 fällt auf v2 zurück, v2 auf Basis -- Sprint für Sprint evolven.
- Was kann versioniert werden? Command Steps, Repository Pipeline, Aggregate Steps, FieldMap, Entity-Validatoren, Registries.
Kein Breaking Change. Kein Feature-Flag. Kein Big-Bang-Release.
Response System API fähig
Jede Domain-Operation liefert eine DomainResponse -- immutable, aggregiert, context-keyed.
$response->isSuccess(); // true/false
$response->getStatus(); // 200, 201, 400, 404, 422, 500
$response->getData(); // ['OrderContext' => [...], 'InventoryContext' => [...]]
$response->getEvents(); // ['OrderContext' => [OrderCreated(...)]]
$response->getErrors(); // ['OrderContext' => ['...']]
$response->getMetadata(); // Duration, Timestamp, Version, Contexts- Context-Keyed. Bei verschachtelten BC-Aufrufen bleiben alle Daten nach Kontext getrennt.
- Immutable & aggregiert. Der generierte
{Domain}Contextbaut die Response auf und gibt sie final und unveränderlich zurück. - HTTP-nahe Codes. Status als Enum; 422 signalisiert eine verletzte Business Rule. App übersetzt die Response in den
{status, data, errors, meta}-Envelope.
3 API Formate immer da
Aus denselben Definitionen erzeugt Jardis drei API-Spezifikationen -- immer synchron, kein Drift:
| Format | Zweck |
|---|---|
| OpenAPI 3.1 | REST-API mit DomainResponse-Envelope |
| AsyncAPI 3.0 | Event-Channels und Payload-Schemas für alle Domain Events |
| Protocol Buffers 3 | gRPC-Services mit typisierten Messages |
Die interne und die externe Schnittstelle sind identisch -- ob per Methodenaufruf oder über HTTP/gRPC.
Pipeline-Konsistenz wie es sein soll
Jede Lese- und Schreiboperation fließt durch eine konsistente Pipeline:
Query -> Transform -> Validate -> Persist- Query: Root-Entity per PK laden, Child-Entities in FK-Reihenfolge nachladen.
- Transform: Flache Daten in verschachtelte Aggregate-Struktur umwandeln.
- Validate: Nur geänderte Entities prüfen -- nicht alles, nur das Relevante.
- Persist: Transaktionssicher, FK-sichere Reihenfolge, kaskadierende Deletes.
Query-Output = Transform-Input = Persist-Erwartung. Durch Konstruktion, nicht durch Konvention.