Skip to main content
gFly v1.15.1
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Basic

Request Lifecycle Flow

sequenceDiagram
Router->>Middleware: Send user Request
Router->>Controller: Send user Request
Controller->>Controller: Validation & Handle
Controller->>Controller: Transform to DTOs
Controller->>Service: Perform business-logic
Service->>Service: Communicate each others
Service->>Service: Call external services
Service->>Repository: CRUD actions
Repository->>Model: Select/Update data
Repository-->>Service: Model data
Service-->>Controller: Transform response
Controller->>Controller: Transformer process
Controller->>View: Response data via view rendering

The request lifecycle represents the complete journey of a user request through your application, from the moment it reaches your server until a response is sent back to the user. Understanding this flow is crucial for developers to follow the execution path, troubleshoot issues, and implement new features effectively.

1. Request Entry Point: Router

Function: The router is the entry point for all incoming HTTP requests. Responsibilities:

  • Parse the incoming URL and HTTP method
  • Match the request against defined routes
  • Forward the request to the appropriate middleware and controller
  • Handle 404 responses for unmatched routes

2. Request Processing: Middleware

Function: Middleware acts as a filter chain that processes requests before they reach the controller. Responsibilities:

  • Authentication verification
  • CSRF protection
  • Request logging
  • Session management
  • Rate limiting
  • Header manipulation
  • IP filtering
  • Response compression

Middleware can be global (applied to all routes) or route-specific (applied only to certain routes).

3. Request Handling: Controller

Function: Controllers receive the filtered request and orchestrate the application logic. Responsibilities:

  • Validation & Handling: Validate input data against defined rules
  • Transform to DTOs: Convert raw request data into Data Transfer Objects (DTOs)
  • Service Coordination: Delegate business logic to appropriate service(s)
  • Response Transformation: Process service results into appropriate response format
  • View Selection: Determine which view should render the response

Controllers should remain thin, focusing on request/response handling rather than implementing business logic.

4. Business Logic: Service Layer

Function: Services contain the core business logic of the application. Responsibilities:

  • Perform Business Logic: Execute the actual business rules and processes
  • Service Communication: Coordinate with other services when needed
  • External Services Integration: Interact with third-party services or APIs
  • Transaction Management: Ensure data consistency across operations

Services should be independent of the HTTP layer, making them reusable across different entry points (web, CLI, etc.).

5. Data Access: Repository Layer

Function: Repositories abstract the data access logic from the business logic. Responsibilities:

  • CRUD Operations: Provide methods for Create, Read, Update, and Delete operations
  • Query Building: Construct database queries
  • Data Filtering: Apply filtering, sorting, and pagination
  • Transaction Support: Participate in transaction management

Repositories shield the rest of the application from the specifics of the data storage mechanism.

6. Data Layer: Models

Function: Models represent the data structure and relationships. Responsibilities:

  • Define data structure and validation rules
  • Handle relationships between different data entities
  • Provide accessor and mutator methods for data properties
  • Encapsulate data-specific behavior

7. Response Journey: Back Through the Layers

After data operations are completed, the response follows a reverse path:

  1. Repository → Service: Return model data to the service layer
  2. Service → Controller: Return processed results to the controller
  3. Controller Transformation: Convert data to the appropriate response format
  4. View Rendering: Combine data with templates to generate HTML (for web views)
  5. Response Delivery: Send the completed response back to the client

Types of Responses

Depending on the application type, responses may take different forms:

  1. HTML Responses: Rendered views for traditional web applications
  2. JSON/XML Responses: Structured data for API consumers
  3. File Downloads: Binary data for file operations
  4. Redirects: HTTP redirects to other URLs
  5. Error Responses: Structured error information

Benefits of the Layer-Based Architecture

This structured request lifecycle offers several advantages:

  1. Separation of Concerns: Each component has a single, well-defined responsibility
  2. Testability: Individual components can be tested in isolation
  3. Maintainability: Changes to one layer minimally impact other layers
  4. Scalability: Components can be scaled independently as needed
  5. Code Reuse: Services and repositories can be used across different controllers or entry points

Performance Considerations

The request lifecycle can impact application performance:

  1. Middleware Efficiency: Excessive middleware can add latency
  2. N+1 Query Problems: Inefficient data access patterns can multiply database queries
  3. Eager vs. Lazy Loading: Choosing the right data loading strategy affects performance
  4. Response Size: Large responses increase network transfer time
  5. Caching Strategies: Strategic caching can bypass parts of the lifecycle for improved performance

Error Handling Throughout the Lifecycle

Each layer has specific error handling responsibilities:

  1. Router: Handle 404 (not found) and 405 (method not allowed) errors
  2. Middleware: Handle authentication and authorization errors (401, 403)
  3. Controller: Handle validation errors (422) and coordinate error responses
  4. Service: Handle business logic errors and application exceptions
  5. Repository: Handle database errors and data access exceptions

Debugging the Request Lifecycle

When troubleshooting issues, consider these approaches:

  1. Request Logging: Log the request at each major step
  2. Performance Profiling: Identify bottlenecks in the request processing
  3. Exception Tracking: Monitor where and when exceptions occur
  4. Transaction Monitoring: Track database transactions through the layers
  5. Request Tracing: Implement distributed tracing for complex applications

Best Practices

For an optimal request lifecycle implementation:

  1. Keep Controllers Thin: Move business logic to services
  2. Use DTOs: Create explicit data transfer objects for each layer boundary
  3. Implement Consistent Error Handling: Use a uniform approach to error processing
  4. Apply Dependency Injection: Avoid tight coupling between layers
  5. Document the Flow: Maintain clear documentation on how requests flow through your system
  6. Consider Asynchronous Processing: Offload long-running tasks from the main request thread
  7. Validate Early: Catch invalid requests as early as possible in the lifecycle

Conclusion

The request lifecycle provides a structured approach to handling user interactions with your application. By understanding each step in this journey, developers can build more maintainable, testable, and performant applications while ensuring a clear separation of concerns across the different architectural layers.

So it is clear that any Request will be received from Router. Immediately after that, Router will coordinate with Middleware (if any). If there are no problems, the data will be sent to Controller for processing.

At Controller, two main processes will be performed: checking input data. This is a way to ensure that the data that needs to be processed is valid and complete.

The next important step is to process that request. With submitted data, it can be a combination of many things. Therefore, it is necessary to classify data into DTOs (Data Transfer Object) that need to be processed before putting them into Services which contain business-logic.

Note
IMPORTANT!
Do not input Request, Form Request objects of Controller directly into Service. This can cause problems if you need to call Service in Console in feature.
Note
NOTE
Service is where the logic is performed. What data should be requested from itself. And that Service can be used in many different places as a caller (Controller or Console). The caller has to create DTO data that Service requires.

Service is the right place to use the persistence layer Repository and Model. The Service can use multiple Repositories at the same time to create modification requests and query data appropriate for a business request. The Service can be called each other. In addition, Service is also the place to generate communication processes with external services.

Repository will create editing operations or retrieve data from the database through Model. To facilitate data manipulation, gFly provides Fluent Query Builder, a tool that makes data querying more efficient.

Make sure all the data manipulation steps are complete. Then returning results to the user will require a View layer. The data received from Service returned to Controller is base data. Depending on different display needs, we can change that data before sending it to View for display. gFly proposes an additional Transformer handler (optional) to increase data adaptability to View needs.

Why we need DTOs?

To avoid spam registrations. The system only requires users to send basic information such as their email address, first name, last name for registration. And the below processing will be required:

  • Save user information: Only responsible for storing data in related tables.
  • Activate an account: Send emails to users asking them to activate their accounts and create passwords. In addition, the system also has a reminder mechanism by sending this email reminder every 1 month and repeating it 3 times.
  • Notify new user: Send notifications to administrators about newly registered user information.
flowchart TD
O["fa:fa-twitter Register User Request"]
O-->A[fa:fa-ban RegisterUserDTO]
O-->B(fa:fa-ban ActivateAccountDTO)
O-->C(fa:fa-camera-retro NotifyNewUserDTO)

Suppose you create a service and receive an HTTP Request object. Retrieving and processing data is inside the service. If one day there is a requirement to write a function to import users into the system using a CLI console to read data from CSV or Excel files, you will definitely encounter difficulties and have to rewrite those services. If you create the DTOs needed to request a service to run, then you can now definitely use it without having to write anything else. In addition, if you combine the 3 services above, you may encounter some difficulties requesting "Re-send email to alert user to activate account" in the future.

Note
TIP
Don’t be afraid to create `DTOs’ and use them for components and layers in your code.