Basic

Best Practice #

Request Lifecycle #

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

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.

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
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.

Example: Update data

// Define query string.
_, err := q.DB.FluentUpdate(func(query fluentsql.UpdateBuilder) fluentsql.UpdateBuilder {
    return query.
        Set("email", u.Email).
        Set("password_hash", u.PasswordHash).
        Set("fullname", u.Fullname).
        Set("phone", u.Phone).
        Set("token", u.Token).
        Set("user_status", u.UserStatus).
        Set("updated_at", u.UpdatedAt).
        Where(fluentsql.Eq{"id": u.ID})
}, "users")

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.

View use pongo2

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.

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