I have an app with "clean architecture" classic layers:
Ports (such as HTTP, GRPC, GRAPHQL resolvers)
Services (CRUD methods and others)
Adapters
Domain
My app has of course multiple "models" such as:
Player
- id: String (mandatory)
- firstname: String (mandatory)
- lastname: String (mandatory)
- age: Number (NOT mandatory)
- motto: String (NOT mandatory)
- team_id: String (mandatory)
Team
- id: String (mandatory)
- name: String (mandatory)
Coach
- id: String (mandatory)
- firstname: String (mandatory)
- lastname: String (mandatory)
The typical flow is:
DTO structs (Data Transfer Objects) such as
PlayerCreateInput
andTeamUpdateInput
are created inPorts
layer's resolvers (from user requests);Service
layer methods are called (such asCreate
,Update
,Delete
and so on) with that DTO as input;Domain
logic is called (if needed);Adapters
methods for persistence or other needs are called;Ports
methods return the result.
Everything is working good and I'm pretty happy!
Today the need has arisen to be able to save each model as a draft: not complete with all fields even if mandatory.
The first idea I had was to add a "status" field to each model like:
- state: enum (can be "DRAFT" or "CONFIRMED" for example)
As you can imagine I have a frontend in my app with generated types for each model. These types obviously respect the mandatory conditions of the fields so as to respect the principle "make invalid state unrepresentable".
In fact for each model I have a function called "new(args)
" which checks all args
before returning.
If I were to allow saving a model without mandatory fields I could no longer use this logic and would have to create other types such as "PartialDomainPlayer
" which has all fields optional.
Then based on application logic I should convert the PartialDomainPlayer
into DomainPlayer
where needed handling the opportune error.
But the same also applies to databases: today the mandatory fields have a NOT NULL which in this case should be removed because the row in the DB can be saved with NULL values.
Another way to save drafts of each model would be to save those drafts in another database table, such as "drafts" :).
But I can't understand how and if it is possible to correctly paginate them, for example in the "players list" page, on the first page, with a limit of 5 players, ordered by "created_at DESC
", if there are others players in the "drafts" table I would have to merge two queries with the same criteria on the backend, but in any case I would also need to change the type on the frontend from Player
to PartialPlayer
with a consequent increase of the code like: if (player.firstname) { use firstname here }
. Now instead I can trust that Player
's firstname
is not null.
I'm wondering what the best practices are for this type of IT problem.
What do you think?