|
|
# DTOs
|
|
|
Librador provides an interface for several different types of DTOs: `Create`, `Patch`, `View`, and `Id`.
|
|
|
## DTOs
|
|
|
|
|
|
## Create
|
|
|
`Create` is used to create new entities. A create class should have (at lease the required) fields of the class to be created and the class to be created itself. For example: if we want to create a `Dog` entity with a name and age, we can create the following `Create` class:
|
|
|
### What are DTOs
|
|
|
DTO stands for [Data Transfer Object](https://en.wikipedia.org/wiki/Data_transfer_object). DTOs can be with or without structure. Structureless DTOs can be seen in Javascript: one can create a JSON object at any point with any number of fields and with any types of fields. We use DTOs in a structured fashion, so every object sent over a network to and from LabraCORE is parsed to and serialized from some predetermined format.
|
|
|
|
|
|
In Java Spring, DTOs are simple [POJOs](https://en.wikipedia.org/wiki/Plain_old_Java_object). The fields in a DTO class represent the keys of data to be sent and the type of the field can be seen as a contract as to how to parse and serialize data in that DTO.
|
|
|
|
|
|
### The different types of DTOs
|
|
|
Within Librador, we distinguish between a couple different types of DTO, corresponding to a couple different actions that can be done within the application. These are: `Create`, `Patch`, `View`, and `Id`.
|
|
|
|
|
|
#### Create
|
|
|
The Create DTO is used in the body of a POST request that is supposed to create some new database entry. Create DTOs contain a function to convert the DTO into a valid database object called [apply](https://gitlab.ewi.tudelft.nl/eip/labrador/librador/-/blob/development/src/main/java/nl/tudelft/librador/dto/create/Create.java#L35). This DTO, like all following DTOs out of the Librador library, uses the [ModelMapper](http://modelmapper.org/)
|
|
|
library to map POJOs into other POJOs without too much hassle.
|
|
|
|
|
|
Create DTOs enable swift and easily customizable validation on create operations and standardized conversion into a database object that is to be saved.
|
|
|
|
|
|
A create class should have (at lease the required) fields of the class to be created and the class to be created itself. For example: if we want to create a `Dog` entity with a name and age, we can create the following `Create` class:
|
|
|
```
|
|
|
@Data
|
|
|
@Builder
|
... | ... | @@ -23,44 +35,8 @@ public class DogCreateDTO extends Create<Dog> { |
|
|
```
|
|
|
Then we can create a `Dog` from a `DogCreateDTO` by calling `apply()`.
|
|
|
|
|
|
## Patch
|
|
|
`Patch` is used to edit already existing entities. A patch needs the fields that need to be changed and an implementation of how to edit the entity. Helper methods for this are provided. An example of a patch class:
|
|
|
```
|
|
|
@Data
|
|
|
@Builder
|
|
|
@NoArgsConstructor
|
|
|
@AllArgsConstructor
|
|
|
@EqualsAndHashCode(callSuper = false)
|
|
|
public class DogPatchDTO extends Patch<Dog> {
|
|
|
private String name;
|
|
|
private Integer age;
|
|
|
|
|
|
@Override
|
|
|
public void applyOneToOne() {
|
|
|
updateNonNull(name, data::setName);
|
|
|
updateNonNull(age, data::setName);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
To change the entity we can again use the `apply()` method.
|
|
|
|
|
|
## View
|
|
|
`View` is used to convert an entity to some object that is used to give to the user. Information can be filtered out or added to View DTOs. An example of a View DTO:
|
|
|
```
|
|
|
@Data
|
|
|
@Builder
|
|
|
@NoArgsConstructor
|
|
|
@AllArgsContstructor
|
|
|
@EqualsAndHashCode(callSuper = false)
|
|
|
public class DogViewDTO extends View<Dog> {
|
|
|
private String name;
|
|
|
private Integer age;
|
|
|
}
|
|
|
```
|
|
|
To convert an entity to a view, we use `View.convert`. In this example `View.convert(dog, DogViewDTO.class)`.
|
|
|
|
|
|
## Id
|
|
|
`Id` is used in Create and Patch to refer to data that is already stored and we therefore do not want to store the entire data in the DTO. To use the same example, if a Dog has an Owner that we already have in the database, the Create would look like:
|
|
|
##### Create with ID
|
|
|
`Id` is used in Create (and Patch, see below) to refer to data that is already stored and we therefore do not want to store the entire data in the DTO. To use the same example, if a Dog has an Owner that we already have in the database, the Create would look like:
|
|
|
```
|
|
|
@Data
|
|
|
@Builder
|
... | ... | @@ -97,4 +73,56 @@ public class OwnerIdDTO extends IdDTO<Owner, Long> { |
|
|
builder.register(SCEditionIdDTO.class, SCEdition.class);
|
|
|
```
|
|
|
|
|
|
#### Patch
|
|
|
The Patch DTO is used in the body of a PATCH request that is supposed to change some value in an existing database entry. Patch DTOs contain a similar function to that found in Create DTOs. The difference is that [apply](https://gitlab.ewi.tudelft.nl/eip/labrador/librador/-/blob/development/src/main/java/nl/tudelft/librador/dto/patch/Patch.java#L27) takes, as an argument, the entity that the patch is supposed to be used on. The entity is then modified according using the modifications proposed in the patch DTO.
|
|
|
|
|
|
Patches make heavy use of helper methods to define when a change is actually applied. We use `null` values in Patches to denote that no change should be made to that field. `updateNonNull` calls can be used to then update the entity class appropriately.
|
|
|
|
|
|
A Patch should never contain the ID of the entity to be changed, this should always be provided within the request directly through a path variable or query parameter.
|
|
|
|
|
|
Patches are rather complicated and may be quite tricky to use. Take a good look at the section on how to create patch DTOs, examples and the methods within [GenericPatch](https://gitlab.ewi.tudelft.nl/eip/labrador/librador/-/blob/development/src/main/java/nl/tudelft/librador/dto/patch/GenericPatch.java) to understand what the methods in Patches can do.
|
|
|
|
|
|
A patch needs the fields that need to be changed and an implementation of how to edit the entity. Helper methods for this are provided. An example of a patch class:
|
|
|
```
|
|
|
@Data
|
|
|
@Builder
|
|
|
@NoArgsConstructor
|
|
|
@AllArgsConstructor
|
|
|
@EqualsAndHashCode(callSuper = false)
|
|
|
public class DogPatchDTO extends Patch<Dog> {
|
|
|
private String name;
|
|
|
private Integer age;
|
|
|
|
|
|
@Override
|
|
|
public void applyOneToOne() {
|
|
|
updateNonNull(name, data::setName);
|
|
|
updateNonNull(age, data::setName);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
To change the entity we can again use the `apply()` method.
|
|
|
|
|
|
##### Patch with IDs
|
|
|
PatchWithIds is a special type of Patch DTO that has mostly the same functionality as the original Patch DTO.
|
|
|
In addition to the Patch DTO, however, it provides conversion methods for those fields that use an ID DTO instead of a normal DTO or flat data.
|
|
|
|
|
|
#### View
|
|
|
View DTOs are simple in use, but complicated in their quantity. A view is a way to limit or remodel the information that is transferred back to services from the LabraCORE API. Often, views only provide the same fields as their representing Entity class.
|
|
|
|
|
|
Views are structured in a peculiar way in LabraCORE. A core principal in structuring views is how many recursive steps need to be taken to serialize an entity into a View. For instance, when serializing an Edition that contains a number of Persons, we could take 1 recursive step from the edition class to serialize all Person classes it holds.
|
|
|
|
|
|
When 0 such hops are required, we call the DTO a summary DTO. These summaries are therefore easy to serialize and deserialize and contain the least amount of nested data possible. Anything other than that is either a Layer_n DTO or a Details DTO.
|
|
|
|
|
|
An example of a View DTO:
|
|
|
```
|
|
|
@Data
|
|
|
@Builder
|
|
|
@NoArgsConstructor
|
|
|
@AllArgsContstructor
|
|
|
@EqualsAndHashCode(callSuper = false)
|
|
|
public class DogViewDTO extends View<Dog> {
|
|
|
private String name;
|
|
|
private Integer age;
|
|
|
}
|
|
|
```
|
|
|
To convert an entity to a view, we use `View.convert`. In this example `View.convert(dog, DogViewDTO.class)`. |
|
|
\ No newline at end of file |