Dies ist der erste Beitrag einer Reihe, die ich erstellt habe, um zu erklären, wie ich meine Symfony-Anwendungen organisiere und wie ich versuche, Code so domänenorientiert wie möglich zu schreiben.
Unten finden Sie das Flussdiagramm, das ich in allen Teilen der Serie verwenden werde. In jedem Beitrag werde ich mich auf einen konkreten Teil des Diagramms konzentrieren und versuchen, die beteiligten Prozesse zu analysieren und herauszufinden, welche Teile zu unserer Domäne gehören würden und wie man sich mithilfe externer Ebenen von den anderen Teilen entkoppeln kann.
In diesem ersten Teil konzentrieren wir uns auf die Datenextraktions- und Validierungsprozesse. Für den Datenextraktionsprozess gehen wir davon aus, dass die Anforderungsdaten im JSON-Format vorliegen.
Aufgrund der Tatsache, dass wir wissen, dass die Anforderungsdaten in der JSON-Anforderungsnutzlast enthalten sind, wäre das Extrahieren der Anforderungsnutzlast (in diesem Fall bedeutet Extrahieren das Erhalten eines Arrays aus der JSON-Nutzlast) genauso einfach wie die Verwendung der PHP-Funktion json_decode.
class ApiController extends AbstractController { #[Route('/api/entity', name: 'api_v1_post_entity', methods: ['POST'])] public function saveAction(Request $request,): JsonResponse { $requestData = json_decode($request->getContent(), true); // validate data } }
Um die Daten zu validieren, benötigen wir drei Elemente:
Für das erste erstellen wir ein DTO (Data Transfer Object), das die eingehenden Daten darstellt, und verwenden die Symfony-Validierungseinschränkungsattribute, um anzugeben, wie diese Daten validiert werden müssen.
readonly class UserInputDTO { public function __construct( #[NotBlank(message: 'Email cannot be empty')] #[Email(message: 'Email must be a valid email')] public string $email, #[NotBlank(message: 'First name cannot be empty')] public string $firstname, #[NotBlank(message: 'Last name cannot be empty')] public string $lastname, #[NotBlank(message: 'Date of birth name cannot be empty')] #[Date(message: 'Date of birth must be a valid date')] public string $dob ){} }
Wie Sie sehen können, haben wir unsere Eingabedatenvalidierungsregeln innerhalb des kürzlich erstellten DTO definiert. Diese Regeln sind die folgenden:
Für die zweite Variante verwenden wir die Symfony-Normalisierungskomponente, mit der wir die eingehenden Anforderungsdaten unserem DTO zuordnen können.
class ApiController extends AbstractController { #[Route('/api/entity', name: 'api_v1_post_entity', methods: ['POST'])] public function saveAction(Request $request, SerializerInterface $serializer): JsonResponse { $requestData = json_decode($request->getContent(), true); $userInputDTO = $serializer->denormalize($requestData, UserInputDTO::class); } }
Wie oben gezeigt, erledigt die Methode denormalize das Zeug und mit einer einzigen Codezeile füllen wir unser DTO mit den eingehenden Daten.
Zur Validierung der Daten verlassen wir uns schließlich auf den Symfony-Validierungsdienst, der eine Instanz unseres kürzlich denormalisierten DTO empfängt (das die eingehenden Daten überträgt) und die Daten gemäß den DTO-Regeln validiert.
class ApiController extends AbstractController { #[Route('/api/entity', name: 'api_v1_post_entity', methods: ['POST'])] public function saveAction(Request $request,): JsonResponse { $requestData = json_decode($request->getContent(), true); // validate data } }
Bisher haben wir den Prozess des Extrahierens und Validierens von Daten in vier Teile unterteilt:
Die Frage ist nun: Welche dieser Teile gehören zu unserer Domäne?
Um die Frage zu beantworten, analysieren wir die beteiligten Prozesse:
1.- Daten extrahieren: Dieser Teil verwendet nur die Funktion „json_decode“, um die eingehenden Daten von JSON in ein Array umzuwandeln. Es fügt keine Geschäftslogik hinzu, sodass diese nicht zur Domäne gehören würde.
2.- Das DTO: Das DTO enthält die mit den Eingabedaten verbundenen Eigenschaften und wie sie validiert werden. Das bedeutet, dass das DTO Geschäftsregeln (die Validierungsregeln) enthält, sodass es zur Domäne gehören würde.
3.- Daten denormalisieren: Dieser Teil verwendet einfach einen Infrastrukturdienst (eine Framework-Komponente), um die Daten in ein Objekt zu denormalisieren. Dies enthält keine Geschäftsregeln, daher würde es nicht zu unserer Domain gehören.
4.- Daten validieren: Genauso wie der Prozess Daten denormalisieren nutzt auch der Datenvalidierungsprozess einen Infrastrukturdienst (eine Framework-Komponente), um die eingehenden Daten zu validieren . Dies enthält keine Geschäftsregeln, da diese im DTO definiert sind und daher auch nicht Teil unserer Domäne wären.
Nach der Analyse der letzten Punkte können wir schlussfolgern, dass nur das DTO Teil unserer Domäne sein wird. Was machen wir dann mit dem Rest des Codes?
Ich persönlich mag es, diese Art von Prozessen (Extrahieren, Denormalisieren und Validieren von Daten) in die Anwendungsschicht oder Serviceschicht einzubeziehen. Warum? Lassen Sie uns die Anwendungsschicht vorstellen.
Kurz gesagt ist die Anwendungsschicht für die Orchestrierung und Koordination verantwortlich und überlässt die Geschäftslogik der Domänenschicht. Darüber hinaus fungiert es als Vermittler zwischen der Domänenschicht und externen Schichten wie der Präsentationsschicht (UI) und der Infrastrukturschicht.
Ausgehend von der obigen Definition könnten wir die Prozesse Extrahieren, Denormalisieren und Validieren in einen Dienst in der Anwendungsschicht einbeziehen, da:
Perfekt, wir werden einen Anwendungsdienst erstellen, um diese Prozesse zu verwalten. Wie machen wir das? Wie werden wir mit den Verantwortlichkeiten umgehen?
Das Single-Responsibility-Prinzip (SRP) besagt, dass jede Klasse nur für einen Teil des Verhaltens der Anwendung verantwortlich sein sollte. Wenn eine Klasse mehrere Verantwortlichkeiten hat, wird es schwieriger, sie zu verstehen, zu warten und zu ändern.
Welche Auswirkungen hat das auf uns? Lass es uns analysieren.
Bisher wissen wir, dass unser Anwendungsdienst die eingehenden Daten extrahieren, denormalisieren und validieren muss. Wenn man das weiß, ist es leicht, die folgenden Verantwortlichkeiten abzuleiten:
Sollten wir diese Verantwortlichkeiten auf drei verschiedene Dienste aufteilen? Das glaube ich nicht. Lass es mich erklären.
Wie wir gesehen haben, wird jede Verantwortung von einer Infrastrukturfunktion oder -komponente verwaltet:
Da der Anwendungsdienst diese Verantwortlichkeiten an die Infrastrukturdienste delegieren kann, können wir eine abstraktere Verantwortung (Prozessdaten) erstellen und diese dem Anwendungsdienst zuweisen.
class ApiController extends AbstractController { #[Route('/api/entity', name: 'api_v1_post_entity', methods: ['POST'])] public function saveAction(Request $request,): JsonResponse { $requestData = json_decode($request->getContent(), true); // validate data } }
Wie oben gezeigt, verwendet der Anwendungsdienst DataProcessor die Funktion json_decode und den Normalisierungs- und Validierungsdienst von Symfony, um die Eingabeanforderung zu verarbeiten und ein neues und validiertes DTO zurückzugeben. Wir können also sagen, dass der DataProcessor-Dienst:
Wie Sie vielleicht bemerkt haben, löst der DataProcessor-Dienst eine Symfony ValidationException aus, wenn der Validierungsprozess einen Fehler findet. Im nächsten Beitrag dieser Serie erfahren wir, wie wir unsere Geschäftsregeln anwenden, um die Fehler zu strukturieren und sie schließlich dem Kunden zu präsentieren.
Ich weiß, dass wir den DataProcessor-Dienst entfernen und MapRequestPayload als Dienstanwendungsschicht zum Extrahieren, Denormalisieren und Validieren der Daten verwenden könnten, aber angesichts des Kontexts dieses Artikels hielt ich es für praktischer schreibe es so.
In diesem ersten Artikel haben wir uns auf das Extrahieren und Validieren von Datenprozessen aus dem Flussdiagramm konzentriert. Wir haben die mit diesem Prozess verbundenen Aufgaben aufgelistet und gelernt, wie wir erkennen können, welche Teile zur Domäne gehören.
Da wir wissen, welche Teile zur Domäne gehören, haben wir einen Dienst auf Anwendungsebene geschrieben, der die Infrastrukturdienste verbindet, die die Domäne regiert, und den Prozess der Datenextraktion und -validierung koordiniert.
Im nächsten Artikel werden wir uns mit der Definition unserer Geschäftsregeln zum Verwalten von Ausnahmen befassen und außerdem einen Domänendienst erstellen, der für die Umwandlung des Eingabe-DTO in eine dauerhafte Einheit verantwortlich ist.
Das obige ist der detaillierte Inhalt vonErstellen fokussierter Domänenanwendungen. Ein Symfony-Ansatz (Teil 1). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!