Today’s article is quite good and also an important topic in Spring Boot. Specifically, let’s find out how the data will change when going through different layers. And what are the concepts of Entity, Domain model and DTO.

Overview Architecture Spring Boot

Source code architecture and data architecture

In the previous sections, we learned that every Spring Boot application follows two basic patterns:

  • MVC pattern
  • 3-layer model (3 tiers)

And so we put together a complete application with the following structure.

The above diagram is used to organize the source code in the program. Thanks to that we divide into Controller, Service, Repository corresponding to layers. However, in terms of data organization, the diagram will become as follows.

This model also includes 3 layers, in which the names of the layers are changed to the corresponding components in Spring Boot.

Accordingly, corresponding to each layer, the data will have a different form. In other words, each layer should only handle certain types of data. Each type of data will have different tasks and purposes. Of course, the code is also divided accordingly.

For example in the diagram, the Controller should not touch data in the form of domain models or entities, only allowed to receive and return DTOs.

Why divide multiple types of data?

Due to following the principle of SoC – separation of concerns in software design. Specifically, we have broken down the Spring Boot application as follows.

Spring Boot = Presentation layer + Service layer + Data access layer

It’s breaking up the source code according to the SoC. However, at a lower level, the SoC embodies the first principle of SOLID (Single responsibility), which means that each class should only perform a single task.

Therefore, in the past, data had only one form, but there were many layers, each layer behaves differently with data, so data has performed many tasks. This violates Single responsibility, so we need to break it down into multiple data types.

Another reason is that if the data has only one form, sensitive data will be leaked. Take for example Facebook’s friend search function, which should only return data with basic information (avatar, name, …). If there is only one data type, all information will be returned. Although the client only displays the necessary information, returning all of it can be used by bad actors to steal sensitive information.

Therefore, separating data into separate forms is also a way to enhance application security.

Types of data

Two types of data

According to the above diagram, data in Spring Boot application is divided into 2 main types:

  • Public: means to exchange, share with the outside via REST API or communicate with other services in the microservice. Data is now in DTO format.
  • Private: the data used inside the application, outside should not know. Data is now in the Domain model or Entity.
    Data types can have many different names, but in general, they still belong to the same two parts as above. Therefore, when applied to Spring Boot architecture, we will consider which type of data is suitable for which layer.

From the above 2 types of public and private, we have 3 types of data:

  • DTO (Data transfer object): are classes that encapsulate data to transfer between client – server or between services in microservices. The purpose of creating a DTO is to reduce the amount of unnecessary information that needs to be transferred, and also increase security.
  • Domain model: are classes representing domains, understood as business objects such as Client, Report, Department, … for example. In a real application, classes representing calculation results, classes as input parameters for calculation services, etc. are considered domain models.
  • Entity: is also the domain model but corresponds to the table in the DB, which can be mapped to the DB. Note that only entities can represent data in the DB.

Data types have the corresponding suffix, except entity. For example, the User entity has no suffix, if it’s a domain model, it’s a UserModel, or with a DTO, it’s a UserDto,… also.

Principle of selecting data corresponding to layer

Well, I don’t even know what to call it. In short, each layer in the 3-layer model will process, receive, and return data of certain types.

Applying to the 3-layer model in the diagram, we can derive the general design principle:

  • Web layer: should only handle DTO, which means that Controllers should only receive and return data as DTO.
  • Service layer: receive DTO (from controller sent through) or Domain model (from other internal services). The data is processed (can interact with the DB), finally returned by the Service to the Web layer as a DTO.
  • Repository layer: only works on Entity, because it’s the appropriate object, can be mapped to the DB.

For other Spring Boot components that don’t belong to any layers, then:

  • Custom Repository: this is a layer that does not go through the repository but manipulates the database directly. Therefore, this class is treated like Service.

Model mapping

As data passes through different layers, it transforms into different forms. For example, the DTO from the controller goes into the service, it will be mapped to the domain model or entity, and then when entering the Repository, it must become Entity. And vice versa is also true.

The conversion between data types, for example DTO to Entity, DTO to domain model, domain model to entity or vice versa, is called model mapping.

Implementing model mapping is usually using a library like ModelMapper (how to use it will be in the next post). However, the simplest one can write pure copy code as follows.

UserDto.java

@Getter
public class UserDto {
    String name;
    String age;

    public void loadFromEntity(User entity) {
        this.name = entity.getName();
        this.age = entity.getAge();
    }
}

User.java

@Getter
public class User {
    String name;
    String age;
    String crush;

    public void loadFromDto(UserDto dto) {
        this.name = dto.getName();
        this.age = dto.getAge();
    }
}

The above code when used will look like this

// In controller, convert from DTO to entity
User user = new User();
user.loadFromDto(userDto);

// or return the similar from Entity to DTO
User user = userService.getUser(username);
userDto userDto = new UserDto();
userDto.loadFromEntity(user);
return userDto;

A simpler way is instead of writing the copy method, copy it in the constructor. Therefore, the conversion code will be shorter.

User user = new User(userDto);  // DTO > entity
UserDto userDto = new UserDto(user);  // Entity > DTO

How is reality?

When applied in practice, there are many different situations that occur. Do not simply follow the following form.

Controller receives DTO > Service converts DTO into model or entity, then processes > Repository receives Entity to put in DB

Repository is taken from DB to Entity > Service and then into DTO > Controller and returns DTO

But there are other cases like:

  • Controller does not accept DTO but accepts primitive parameters such as int, float,…
  • Get a List DTO
  • Returns a List DTO

Therefore, in practice one can change to suit the project.

The standard example is that the Service will do the mapping to the DTO and vice versa, the controller will only receive the DTO. But sometimes to reduce the load on the service, this mapping will be done by the controller (however, the controller can be bloated, while it should be kept thin – as little code as possible).

But either way, the general rule is that mapping is always done at the edge of the code. That is, if the mapping is in the service, the transformations should always be at the beginning, or at the end of the method when they are processed.

Also, to reduce boilerplate code, we usually reduce the rigor a bit if not necessary. Eg:

  • Sometimes without a domain model, the Service can convert the DTO directly into an entity.
  • Service can also return Entity or Model, if they are too simple and do not contain sensitive information. At this time, there is no need for a DTO, but the controller returns Entity or Model always to avoid confusion (although because it is against the rules when public these two guys, it should be considered).

There are many controversial opinions about using DTO as an anti pattern. Personally, I don’t see that, sometimes DTO is still quite useful, and can be customized to be more appropriate and effective.

This post is long enough. To be honest, this is the article that I spent the most time on, because I have to touch a lot about architecture. Just yesterday, I even pulled out the old project to refactor it properly, to better understand the architecture I’m about to present and the possible side effects.

Leave a Reply

Your email address will not be published. Required fields are marked *