what is Best practice to communicate from controller to service ? and why ? and if you can send me some documentation/book/topic about this.
@RestController
@RequestMapping("api/post")
public class PostController {
@Autowired
PostService postService;
@PostMapping("posts")
public GetPostOutput getPosts(@RequestBody GetPostInput getPostInput) {
return postService.getPosts(getPostInput);
}
}
services:
public interface PostService {
GetPostOutput getPosts(GetPostInput getPostInput);
}
OR :
@RestController
@RequestMapping("api/post")
public class PostController {
@Autowired
PostService postService;
@PostMapping("posts")
public GetPostOutput getPosts(@RequestBody GetPostInput getPostInput) {
startDate=getPostInput.getStartDate();
endDate=getPostInput.getEndDate();
return postService.getPosts(startDate,endDate);
}
}
public interface PostService {
GetPostOutput getPosts(startDate,endDate);
}
CodePudding user response:
@RequestBody class i.e. GetPostInput should only contain the relevant fields such as startDate and endDate in it. As it should be clear that what all fields are expected in the request body, to avoid confusions.
And it's always preferred to pass a single parameter(request body object) in the service layer instead of all fields separately to easily welcome additions/deletions in the request body. (Which would not require the code change at controller level and also in method declarations.) Further breakdown of business logic can be handled at service level.
CodePudding user response:
Does your "Post" object naturally contain startDate
and endDate
? Or are they actually blog posts that contain a date
field, which you are filtering using startDate and endDate? If the latter is the case then your second example makes more sense.
@PostMapping("posts")
public GetPostOutput getPosts(@RequestBody GetPostInput getPostInput) {
LocalDate startDate=getPostInput.getStartDate();
LocalDate endDate=getPostInput.getEndDate();
return postService.getPosts(startDate,endDate);
}
Post class
public class Post {
private LocalDate date;
// getter and setter
}
Otherwise, there is no hard and fast rule to this.
If the application is small and the logic is simple, it may be sufficient to simply pass the request body object to the service, as in your first example.
As the application grows larger, you may want to provide separate classes for different purposes -- e.g. EmployeeRequest
for your @RequestBody object, Employee
as your DTO for passing employee data around the application, EmployeeEntity
for your database @Entity object. In such a case neither of your examples would apply, and you would instead do some transformation from the EmployeeRequest
request body object to a Employee
DTO, and pass this DTO to the service.
One benefit of doing so is that you can keep the classes clean, as the annotations related to @Entity are not mixed together with your REST API validation annotations. Additionally, your database entity may contain fields that are irrelevant to your request body (e.g. createdAt, updatedAt) and vice-versa.
Generally, it is ok for the controller to contain some presentational logic, such as transforming the @RequestBody to feed to different service calls or composing the results of several service calls into a single @ResponseBody object. Keep the main business logic in the services.
Some principles that you may want to consider:
- Keep classes highly-cohesive: Does your "Post" object naturally contain
startDate
andendDate
? Or are they actually blog posts that should just contain adate
field? - Avoid using "God objects"
- On the other hand, having too many method parameters may also be a sign of a code smell
Finally as a sidenote, in your example you seem to be using a POST-request to retrieve data. A GET-request with @RequestParam might be more appropriate for this purpose.
@GetMapping("posts")
public GetPostOutput getPosts(
@RequestParam(name = "start_date") LocalDate startDate,
@RequestParam(name = "end_date") LocalDate endDate) {
return postService.getPosts(startDate,endDate);
}