Home Backend Development PHP Tutorial Creating focused domain applications. A Symfony approach (Part 1)

Creating focused domain applications. A Symfony approach (Part 1)

Nov 03, 2024 am 10:52 AM

Introduction

This is the first post of a series that i've decided to create in order to explain how I organize my symfony applications and how I try to write code as domain-oriented as possible.
Bellow, you can find the flow diagram that I will use during all the series parts. In each post, I will focus in a concrete part of the diagram and I will try to analyze the processes involved and detect which parts would belong to our domain and how to decouple from the other parts using external layers.

Creating focused domain applications. A Symfony approach (Part 1)

Breaking down the data processing

In this first part, we will focus on the data extraction and validation processes. For the data extraction process, we will assume that the request data comes formatted as JSON.

Extracting the data

Based on the fact that we know that the request data comes within the JSON request payload, extracting the request payload (in this case extracting means obtaining an array from the JSON payload) would be as easy as using the php json_decode function.

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
    }
}
Copy after login
Copy after login
Copy after login

Validating the data

To validate the data, we need three elements:

  • One for defining the validation rules.
  • One for transforming the requested data into a validable object.
  • One for validating the extracted data based on those rules.

Defining the validation rules

For the first one, we are going to create a DTO (Data Transfer Object) which will represent the incoming data and we will use the Symfony validation constraints attributes to specify how this data must be validated.

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
    ){}
}
Copy after login

As you can see, we have defined our input data validation rules within the recently created DTO. These rules are the following:

  • email: Cannot be empty and must be a valid email
  • firstname: Cannot be empty
  • lastname: Cannot be empty
  • dob (Date of birth): Cannot be empty and must be a valid date.

Denormalizing the request data into the DTO

For the second one, we will use the Symfony normalizer component by which we will be able to map the request incoming data into our DTO.

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);
    }
}
Copy after login

As shown above, the denormalize method do the stuff and with a single line of code, we get our DTO filled with the incoming data.

Validating the DTO

Finally, to validate the data we will rely on the Symfony validator service which will receive an instance of our recently denormalized DTO (which will carry the incoming data) and will validate the data according to the DTO rules.

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
    }
}
Copy after login
Copy after login
Copy after login

Identifying the domain

So far, we have broken down the process of Extracting and Validating Data into four parts:

  • Extract an array from the JSON request payload.
  • Create a DTO to represent the incoming data and their validation rules.
  • Denormalize the array into a DTO which contains the validation rules.
  • Validate the DTO.

The question now is: Which of these parts do belong to our domain?
To answer the question, Let's analyze the processes involved:

1.- Extracting data: This part only uses the "json_decode" function to transform the incoming data from json to array. It does not add business logic so this would not belong to the domain.

2.- The DTO: The DTO contains the properties associated with the input data and how they will be validated. This means that the DTO contains business rules (the validation rules) so it would belong to the domain.

3.- Denormalize data: This part simply uses an infrastructure service (a framework component) to denormalize the data into an object. This does not contains business rules so this would not belong to our domain.

4.- Validating data: In the same way as the Denormalize data process, the validating data process also uses an infraestructure service (a framework component) to validate the incoming data. This does not contains business rules since they are defined in the DTO so it would not be part of our domain either.

After analyzing the last points, we can conclude that only the DTO will be part of our domain. Then, what do we do with the rest of the code?

Creating an application service

Personally, I like including this kind of processes (extracting, denormalizing and validating data) into the application layer or service layer. Why ?, let's introduce the application layer.

The Application layer

In short, the application layer is responsible for orchestration and coordination, leaving the business logic to the domain layer. Moreover, it acts as an intermediary between the domain layer and external layers such as the presentation layer (UI) and the infrastructure layer.
Starting from the above definition, we could include the Extracting, Denormalizing and Validating processes into a service in the application layer since:

  • It coordinates the incoming data processes.
  • It uses the infrastructure services (PHP JSON function, Symfony normalize component and Symfony validation component) to process the incoming data.
  • It applies the domain rules by passing the DTO to the validation infrastructure service.

Perfect, we are going to create an application service to manage this processes. How are we going to do it ? How are we going to manage the responsibilities?

Analyzing the responsibilities

The Single Responsibility Principle (SRP) states that each class should be responsible for only one part of the application's behavior. If a class has multiple responsibilities, it becomes more difficult to understand, maintain, and modify.

How does this affect to us ? Let's analyze it.
So far, we know that our application service must extract, denormalize and validate the incoming data. Knowing this, it is easy to extract the following responsibilities:

  • Extract data
  • Denormalize data
  • Validate data

Should we split these responsibilities into 3 different services? I do not think so. Let me explain.

As we have seen, each responsibility is managed by an infrastructure function or component:

  • Extracting responsibility: PHP json_decode function
  • Denormalize data responsibility: Symfony normalizer component
  • Validate data responsibility: Symfony validation component

As the application service can delegate these responsibilities to the infrastructure services, we can create a more abstract responsibility (Process data) and assign it to the application service.

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
    }
}
Copy after login
Copy after login
Copy after login

As shown above, the DataProcessor application service uses the json_decode function and the Symfony normalizer and validator service to process the input request and return a fresh and validated DTO. So we can say that the DataProcessor service:

  • Coordinates all tasks related to the processing of input data.
  • Communicates the outside world (infrastructure services) with the domain business rules (DTO).

As you may have noticed, the DataProcessor service throws a Symfony ValidationException when the validation process finds an error. In the next post of this series, we will learn how to apply our business rules to structure the errors and finally present them to the client.

I know that we could remove the DataProcessor service and use the MapRequestPayload as the service application layer to extract, denormalize and validate the data but, given the context of this article, I thought it more convenient to write it this way.

Conclusion

In this first article, we have focused on the Extracting and Validating data processes from the flow diagram. We have listed the tasks involved within this process and we have learned how to detect which parts belong to the domain.
Knowing which parts belong to the domain, we have written an application layer service that connects the infrastructure services which the domain rules and coordinates the extracting and validating data process.
In the next article, we will explore hot to define our business rules to manage exceptions and we will also create a domain service which will be responsible of transforming the Input DTO into a persistible entity.

The above is the detailed content of Creating focused domain applications. A Symfony approach (Part 1). For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: How To Unlock Everything In MyRise
1 months ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Apr 05, 2025 am 12:04 AM

JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

Explain the concept of late static binding in PHP. Explain the concept of late static binding in PHP. Mar 21, 2025 pm 01:33 PM

Article discusses late static binding (LSB) in PHP, introduced in PHP 5.3, allowing runtime resolution of static method calls for more flexible inheritance.Main issue: LSB vs. traditional polymorphism; LSB's practical applications and potential perfo

Framework Security Features: Protecting against vulnerabilities. Framework Security Features: Protecting against vulnerabilities. Mar 28, 2025 pm 05:11 PM

Article discusses essential security features in frameworks to protect against vulnerabilities, including input validation, authentication, and regular updates.

Customizing/Extending Frameworks: How to add custom functionality. Customizing/Extending Frameworks: How to add custom functionality. Mar 28, 2025 pm 05:12 PM

The article discusses adding custom functionality to frameworks, focusing on understanding architecture, identifying extension points, and best practices for integration and debugging.

How to send a POST request containing JSON data using PHP's cURL library? How to send a POST request containing JSON data using PHP's cURL library? Apr 01, 2025 pm 03:12 PM

Sending JSON data using PHP's cURL library In PHP development, it is often necessary to interact with external APIs. One of the common ways is to use cURL library to send POST�...

What exactly is the non-blocking feature of ReactPHP? How to handle its blocking I/O operations? What exactly is the non-blocking feature of ReactPHP? How to handle its blocking I/O operations? Apr 01, 2025 pm 03:09 PM

An official introduction to the non-blocking feature of ReactPHP in-depth interpretation of ReactPHP's non-blocking feature has aroused many developers' questions: "ReactPHPisnon-blockingbydefault...

Describe the SOLID principles and how they apply to PHP development. Describe the SOLID principles and how they apply to PHP development. Apr 03, 2025 am 12:04 AM

The application of SOLID principle in PHP development includes: 1. Single responsibility principle (SRP): Each class is responsible for only one function. 2. Open and close principle (OCP): Changes are achieved through extension rather than modification. 3. Lisch's Substitution Principle (LSP): Subclasses can replace base classes without affecting program accuracy. 4. Interface isolation principle (ISP): Use fine-grained interfaces to avoid dependencies and unused methods. 5. Dependency inversion principle (DIP): High and low-level modules rely on abstraction and are implemented through dependency injection.

See all articles