Skip to content

Highlights

Was ein Klick auf Build erzeugt -- und was alles out of the box mitkommt.

Build-Artefakte

ArtefaktBeschreibung
Domain-Fassade {Domain}Einstiegspunkt -- hält den Koffer, öffnet die Bounded Contexts
{Domain}ContextGenerierte Basisklasse der Context-Familie -- Kernel-Naht und Versionierung
BC-FassadeDie Außentür: {agg}() (Read-Fassade), process() und rule-guarded Commands
EntitiesTypisierte 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 PipelineQuery, Transform, Validate, Persist -- vier Stufen, eine Wahrheitsquelle
Command DTOs & HandlerTypisierte Eingabeobjekte + Orchestratoren mit Steps (Initialize, Validate, Hydrate, Apply, Persist)
Query DTOs & HandlerEinzel-Aggregate und paginierte Listen mit SQL-JOINs
ProcessesBC-Level-Orchestrierung als Graph -- DTO, Orchestrator und Knoten-Stubs
Rules-LayerGuard vor dem Command; Verletzung endet als DomainResponse mit Status 422
Domain EventsLifecycle (Created/Removed) und Operation Events -- modellgetrieben verkündet
FieldMapBidirektionales Mapping DTO <-> Datenbank
API SpecificationsOpenAPI 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.
php
$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.

php
$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}Context baut 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:

FormatZweck
OpenAPI 3.1REST-API mit DomainResponse-Envelope
AsyncAPI 3.0Event-Channels und Payload-Schemas für alle Domain Events
Protocol Buffers 3gRPC-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.