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 inputRequest
,Form Request
objects ofController
directly intoService
. This can cause problems if you need to callService
inConsole
in feature.
NOTE
Service
is where the logic is performed. What data should be requested from itself. And thatService
can be used in many different places as a caller (Controller
orConsole
). The caller has to createDTO
data thatService
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.