The difference between the level of service and the controller in practice

I read a lot of theories about the differences between the service level and the controller, and I have some questions about how to put this into practice. One answer to Service Level and Controller: who cares what? He speaks:

I am trying to limit the controllers to doing work related to checking http parameters, which determine which service method to call with which parameters, what to put in httpsession or request, what kind of redirection or forwarding, or similar materials related to the website.

and http://www.bennadel.com/blog/2379-a-better-understanding-of-mvc-model-view-controller-thanks-to-steven-neiland.htm :

Red Flags: my controller architecture may degrade if:

The controller makes too many requests for the service level. The controller makes a series of service level requests that do not return data. The controller requests a level of service without passing arguments.

I am currently developing a web application with Spring MVC, and I have this way of saving user modified email:

/** * <p>If no errors exist, current password is right and new email is unique, * updates user email and redirects to {@link #profile(Principal)} */ @RequestMapping(value = "/saveEmail",method = RequestMethod.POST) public ModelAndView saveEmail( @Valid @ModelAttribute("changeEmailBean") ChangeEmailBean changeEmailBean, BindingResult changeEmailResult, Principal user, HttpServletRequest request){ if(changeEmailResult.hasErrors()){ ModelAndView model = new ModelAndView("/client/editEmail"); return model; } final String oldEmail = user.getName(); Client client = (Client) clientService.getUserByEmail(oldEmail); if(!clientService.isPasswordRight(changeEmailBean.getCurrentPassword(), client.getPassword())){ ModelAndView model = new ModelAndView("/client/editEmail"); model.addObject("wrongPassword","Password doesn't match to real"); return model; } final String newEmail = changeEmailBean.getNewEmail(); if(clientService.isEmailChanged(oldEmail, newEmail)){ if(clientService.isEmailUnique(newEmail)){ clientService.editUserEmail(oldEmail, newEmail); refreshUsername(newEmail); ModelAndView profile = new ModelAndView("redirect:/client/profile"); return profile; }else{ ModelAndView model = new ModelAndView("/client/editEmail"); model.addObject("email", oldEmail); model.addObject("emailExists","Such email is registered in system already"); return model; } } ModelAndView profile = new ModelAndView("redirect:/client/profile"); return profile; } 

You can see that I have many requests for the service level, and I redirect from the controller - this is the business logic. Please show the best version of this method.

And one more example. I have this method that returns a user profile:

 /** * Returns {@link ModelAndView} client profile * @param user - principal, from whom we get {@code Client} * @throws UnsupportedEncodingException */ @RequestMapping(value = "/profile", method = RequestMethod.GET) public ModelAndView profile(Principal user) throws UnsupportedEncodingException{ Client clientFromDB = (Client)clientService.getUserByEmail(user.getName()); ModelAndView model = new ModelAndView("/client/profile"); model.addObject("client", clientFromDB); if(clientFromDB.getAvatar() != null){ model.addObject("image", convertAvaForRendering(clientFromDB.getAvatar())); } return model; } 

The convertAvaForRendering method (clientFromDB.getAvatar ()) is placed in the superclass of this controller, this is the correct placement of this method, or it should be placed in the service level

Help, this is really important for me.

+5
source share
2 answers

In both examples, why do you need to use Client ? This is the smell of code.

Because a service level call is also a call that sets the boundary of a database transaction, creating multiple calls means that they are executed in different transactions and therefore are not necessarily consistent with each other.

This is one of the reasons why multiple calls are not welcome. @ArthurNoseda mentions another good reason in his answer .

In your first case, there should be one service level call, for example. something like that:

 if (changeEmailResult.hasErrors()) { return new ModelAndView("/client/editEmail"); } try { clientService.updateUserEmail(user.getName(), changeEmailBean.getCurrentPassword(), changeEmailBean.getNewEmail()); } catch (InvalidPasswordException unused) { ModelAndView model = new ModelAndView("/client/editEmail"); model.addObject("wrongPassword", "Password doesn't match to real"); return model; } catch (DuplicateEmailException unused) { ModelAndView model = new ModelAndView("/client/editEmail"); model.addObject("email", oldEmail); model.addObject("emailExists", "Such email is registered in system already"); return model; } refreshUsername(newEmail); return new ModelAndView("redirect:/client/profile"); 

You can also use the return value instead of exceptions.

As you can see, this will delegate the business logic of changing email to the service level, while preserving all UI-related actions in the controller where they belong.

+4
source

A Spring Controller usually bound to the Spring API (with classes such as Model , ModelAndView ...) or the Servlet API ( HttpServletRequest , HttpServletResponse ...), Methods can return String results that are allowed for the template name (JSP ...) . Controller , of course, is biased towards web-based graphical interfaces.

Service , on the other hand, should be designed with business logic in mind and no assumptions about the customer. We could remove the service, expose it as a web service, implement a web interface or Swing client. A Service should not depend on Spring MVC, the servlet API, or the like. Thus, if you need to reconfigure the application, you can reuse most of the business logic.

As for the note regarding too many calls to the service level from the controller level, this is basically a performance issue, which IMHO is something else. If every service level call queries a database, performance problems may occur. This service level and controller level do not work in the same JVM, and you may run into performance issues. This is another very important aspect of designing your application, but it indicates that you should phase your service calls to provide tougher operations at the controller level.

+5
source

All Articles