Service Layer는 API layer에서 전달받은 요청을 실질적인 비즈니스 로직을 처리하는 계층이다.
API에서 DTO로 변경해 전달받은 요청을 추가로 가공하고 도메인 엔티티로 매핑하여 Data Access Layer와 연동하는 작업이 이루어지는 계층이다.
Data Access 계층에 필요한 컴포넌트를 Spring container의 DI를 통해 주입받아 의존 관계를 가질 수 있다.
아래 코드는 다음과 같은 문제를 가지고 있다.
@RestController
@RequestMapping("/v2/members")
@Validated
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
@PostMapping
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberDto) {
Member member = new Member();
member.setEmail(memberDto.getEmail());
member.setName(memberDto.getName());
member.setPhone(memberDto.getPhone());
Member response = memberService.saveMember(member);
return new ResponseEntity<>(response, HttpStatus.CREATED);
}
@PatchMapping("/{member-id}")
public ResponseEntity patchMember(@PathVariable("member-id") @Positive long memberId,
@Valid @RequestBody MemberPatchDto memberPatchDto) {
// No need Business logic
Member member = new Member();
member.setMemberId(memberPatchDto.getMemberId());
member.setName(memberPatchDto.getName());
member.setPhone(memberPatchDto.getPhone());
Member response = memberService.updateMember(member);
return new ResponseEntity<>(response, HttpStatus.OK);
}
@GetMapping("/{member-id}")
public ResponseEntity getMember(@PathVariable("member-id") @Positive long memberId) {
System.out.println("# memberId: " + memberId);
Member response = memberService.findMember(memberId);
// not implementation
return new ResponseEntity<>(response, HttpStatus.OK);
}
@GetMapping
public ResponseEntity getMembers() {
System.out.println("# get Members");
// not implementation
List<Member> response = memberService.findMembers();
return new ResponseEntity<>(response, HttpStatus.OK);
}
@DeleteMapping("/{member-id}")
public ResponseEntity deleteMember(@PathVariable("member-id") @Positive long memberId) {
System.out.println("# deleted memberId: " + memberId);
// No need business logic
memberService.deleteMember(memberId);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
@Service
public class MemberService {
public Member createMember(Member member) {
return member;
}
public Member updateMember(Member member) {
return member;
}
public Member getMember(long memberId) {
return new Member(memberId, "thom", "[email protected]", "010-1234-1234");
}
public List<Member> getMembers() {
return List.of(
new Member(1, "thom", "[email protected]", "010-1234-1234"),
new Member(2, "jonny", "[email protected]", "010-4555-1234"),
new Member(3, "eddie", "[email protected]", "010-1233-1234")
);
}
public void deleteMember(long memberId) {
}
}
각 계층에서 필요한 로직에만 집중할 수 있도록, Mapper 클래스를 생성해서 DTO와 도메인 객체 간의 변경 작업을 위임할 수 있다.
아래 코드는 MemberDto를 각 Member 도메인 객체로 바꾸어주고, Member를 ResponseDto로 변경해주는 로직을 가지고 있다.
@Component
public class MemberMapper {
public Member createMemberFrom(MemberPostDto memberPostDto) {
return new Member(
0L,
memberPostDto.getName(),
memberPostDto.getEmail(),
memberPostDto.getPhone());
}
public Member createMemberFrom(MemberPatchDto patchDto) {
return new Member(
patchDto.getMemberId(),
patchDto.getName(),
null,
patchDto.getPhone()
);
}
public MemberResponseDto createMemberResponseDtoFrom(Member member) {
return new MemberResponseDto(
member.getMemberId(),
member.getEmail(),
member.getName(),
member.getPhone());
}
}