Reputation: 121
We are a small team starting a new project, and developing the back-end using Spring-Boot framework.
The back-end project will have the following standard layers of a Spring-Boot application:
We also have an entity package where all the classes representing database entities are located. We also use a library to map entities with Api models.
It did not last long and our first debate within the team occurred. So I thought it's a good idea to ask for advice out there.
My other partners think that the controllers should contain as few lines of code as possible. Ideally containing only one line of code, where the respective Service is called, and the rest is handled in the Service.
I am against that approach, because if we would do that, the Service and the Controller would end up having the same interface, which feels wrong.
However, I agree with the principle that the controllers should have as few lines of code as possible. I think that the controllers methods should accept and return only Api models, and no concept of entities should be present in the controllers.
At the same time, I think that the services method should accept and return entities, and not work with ApiModels.
Should we introduce a new layer between Controller and Service? Is there some standard, for which I am not aware, related to this topic?
I am going to give you a concrete example with two approaches (mine and my partners'):
First approach:
public class UserController implements UsersApi {
@Autowired
private UserService userService;
@Override
public ResponseEntity<UserAPIModel> createUser(@Valid UserAPIModel body) {
User createdUser = userService.save(UserMapper.INSTANCE.toUserEntity(body));
return ResponseEntity.ok(UserMapper.INSTANCE.toUserAPIModel(createdUser));
}
}
And the service:
public class UserService {
@Autowired
private UserRepository repository;
public User save(User entity) {
return repository.save(entity);
}
}
The second approach:
public class UserController implements UsersApi {
@Autowired
private UserService userService;
@Override
public ResponseEntity<UserAPIModel> createUser(@Valid UserAPIModel body) {
return ResponseEntity.ok(userService.createUser(body));
}
}
and the service:
public class UserService {
@Autowired
private UserRepository repository;
public UserAPIModel createUser(UserAPIModel body) {
User user = repository.save(UserMapper.INSTANCE.toUserEntity(body));
return UserMapper.INSTANCE.toUserAPIModel(user);
}
}
As you can see in the first approach, the mapping from entity to Api Model and vice versa is done in the Controller. In the second approach this mapping is done in the Service.
Which one would you recommend? Or do you think it's better to introduce a new layer between Controller and Service?
Making a decision on this simple example, can help us to create a more general rule and set a standard for similar problems in the future.
Upvotes: 1
Views: 1999
Reputation: 393
Ho guys!
I too started working on Springboot a few months ago, I can tell you that from personal experience it is better to create a Mapper between Controller and Service in order to proceed better. Generally, especially in this type of projects, it is imperative to diversify and not to make the Controller or the Service do tasks outside of their use. The controller in this case you only need to start a command, there shouldn't be any kind of direct connections to Repo either. from experience I tell you that in larger projects this mentality pays off
Upvotes: 0
Reputation: 11110
First of all, your question is very broad, which incorporates many questions in one post, which you should not be doing on this site. In the future, try to have more focused questions instead.
TL;DR:
@Controller
is a Web-Layer, i.e. layer responsible to handle HTTP Request-Response responsibility, where your Java code should semantically talk web, i.e. responsibility of your controller should be to accept the HTTP Request, and respond with HTTP Response. Whether or not you do some logic in between, should not be much a pure responsibility of controller, it should stand on the shoulders of its dependencies (@Service, in most cases).
@Service
is a Business-Layer, i.e. layer responsible to process business-related tasks, where all the major business-logic, transactions, file processing, batch processing or etc. may be taking place.
You can mix-and-match these two, but it's not a good idea. Rather follow a clear separation of concerns pattern, where @Controller will be responsible for web layer, and @Service - for business logic.
My other partners think that the controllers should contain as few lines of code as possible
Not necessarily, it depends. Controller might do some work, depending what is the given circumstance and there is no controller-specific Clean Code practice. Clean Code instructs you, that generally, your methods should not be too long and messy (there is a funny rule, that no method should go longer than a height of your 5 fingers, when laid horizontally), but again, this is very generic and debatable rule;
Ideally containing only one line of code
Do not follow this as a rule. This is rarely real, unless your controller does nothing except calling some service API (like return findAll();
);
where the respective Service is called, and the rest is handled in the Service
You partner is right, if he/she means, that business-related (that is transactional, DB accessor, Batch, or any other type of process, that is OUT OF the web-layer responsibility) should better be handled in a Service layer;
@Service components are oriented to be acting as Business Service Facade;
I am against that approach, because if we would do that, the Service and the Controller would end up having the same interface, which feels wrong.
No, they will not. @Controller does not need any Java Interface, in the classical sense of this phrase (e.g. public interface Foo{..}
), it's a Spring managed component, and you don't inject its implementations, which is not the case when we're talking about @Service components;
However, I agree with the principle that the controllers should have as few lines of code as possible.
Don't conceptualize this on controller. Method, generally, should be as clear as possible, and there is nothing wrong if controller is a bit long.. it's not about controller, as such, it's about your methods' design, whether or not you can extract/abstract something out and make your code cleaner. However, apply YAGNI principle when possible and don't rush with premature optimization with respect of the controller's code lines. Don't go too long.. but don't bother yourself much here on counting lines;
I think that the controllers methods should accept and return only API models, and no concept of entities should be present in the controllers.
Wrong. @Controller's responsibility is not directly bound to the API model, it's responsibility is to accept HTTP Message, do something with it (either with the help of @Service, UtilityClass, or etc.) and respond back with HTTP Response. In some cases, you can make your controller be dependent on Data Access layer directly, but again - it depends.
Whether you'll have accepting models or not, it also very much depends and you can see my other answer explaining whether you should use DTO or not;
Should we introduce a new layer between Controller and Service?
NO, unless it's necessary. Controller should be the first layer where your request ends up; it should usually rely on Service, and Service, eventually, will rely on Data Persistence layer (Spring Data, JDBC, Integrations, or any other thing).
Upvotes: 3