-
Service Layer 에 읽기 쓰기 분리기Project/TravelFeelDog 2023. 8. 10. 13:56
기존의 프로젝트를 진행을 하는데 있어 서비스 레이어는 다음과 같은 목적으로 작성을 하였다.
1. 어플리케이션에서 사용할 로직을 담당한다 .
2. 클라이언트의 인터페이스를 담당하는 컨트롤러 프랜제테이션 계층과 도메인의 비즈니스 로직을 담당하는 모델의 결합을 끊는 역할
3.Dto 매핑을 하는 역할.
4. 다른 도메인 , 루트 에그리거트의 접근을 제어하는 역할.
서비스는 데이터의 crud 와 관련된 로직을 제어 및 호출 하는 역할을 하였지만 하나의 모델과 관련된 기능이 늘어나면서 가독성이 떨어지고 하나의 클라스에서 담당하는 역할이 많아지면서 클라스의 목적이 모호해지는 현상이 생겼다.
추가적으로 , 조회를 위한 조회 성능을 위한 옵션을 설정한 트랜잭션이 자원을 생성하는 매서드 내부에서 호출 되면서 ,
트랜잭션의 전파로 인하여 성능 최적화를 의도와는 다르게 이루지 못했다라는 사실도 발견을 했다.
@Service("memberReadService") @RequiredArgsConstructor @Transactional(readOnly = true) public class MemberService { ... public Member findByToken(String firebaseToken) { return memberRepository.findByToken(firebaseToken) .orElseThrow(() -> new NoSuchElementException( "Member not found by token:" + firebaseToken)); } }
@Transactioanl 조회 전용이 설정이 되어있는 findByToken 이라는 매서드가 , 피드를 생성하는 메서드 내에서 호출이 된다라면 ,
조회 전용의 이점을 받지 못하는 상황이다.
여기서 말하는 이점이란 , 스냅샷을 저장하지 않아 변경 감지의 작업을 수행하지 않고(메모리 절약 및 수동 flush) 데이터베이스의 구조가 master-slave 의 경우 읽기 전용 데이터베이스를 읽는 등의 장점이다.
@PostMapping(value = "/post", produces = "application/json;charset=UTF-8") public ApiResponse postFeed(@Valid @RequestBody FeedPostRequestDto requestDto) { Feed feed = feedWriteService.postFeed(requestDto); return ApiResponse.success(new FeedStaticResponseDto(feed)); }
@Transactional @Service @RequiredArgsConstructor public class FeedWriteService { private final FeedRepository feedRepository; private final MemberService memberService; private final FeedTagService feedTagService; public Feed postFeed(FeedPostRequestDto requestDto) { Member writer = memberService.findByToken(requestDto.getMemberToken()); List<Tag> tags = feedTagService.makeTagsByContents(requestDto.getFeedTags()); Feed feed = Feed.create(writer,requestDto.getTitle(),requestDto.getBody()); feed.setFeedImages(requestDto.getFeedImageUrls()); feed.setTags(tags); return feedRepository.save(feed); }
위의 코드들을 바꿔주자 , 피드를 생성하는 유스케이스 클라스를 생성하여 , 조회전용 메서드 자원을 생성하는 메서드를 각각 호출 하는 흐름을 만들어 준다.
(MemberService 는 인터페이스로 읽기 전용과 쓰기 업데이트 전용 메서드 인터페이스를 가진다.)
@Service @RequiredArgsConstructor public class CreateFeedUsecase { private final MemberService memberService; private final FeedTagService feedTagService; private final FeedWriteService feedWriteService; public FeedStaticResponseDto execute(FeedPostRequestDto requestDto){ // 읽기 전용 트랜잭션 Member writer = memberService.findByToken(requestDto.getMemberToken()); List<Tag> tags = feedTagService.getTagsByContents(requestDto.getFeedTags()); // 쓰기 전용 트랜잭션 시작 Feed feed = feedWriteService.postFeed(requestDto,writer,tags); return new FeedStaticResponseDto(feed); } }
@PostMapping(value = "/post", produces = "application/json;charset=UTF-8") public ApiResponse postFeed(@Valid @RequestBody FeedPostRequestDto requestDto) { return ApiResponse.success(createFeedUsecase.execute(requestDto)); }
@Transactional @Service @RequiredArgsConstructor public class FeedWriteService { private final FeedRepository feedRepository; public Feed postFeed(FeedPostRequestDto requestDto, Member writer, List<Tag> tags) { Feed feed = Feed.create(writer, requestDto.getTitle(), requestDto.getBody()); feed.setFeedImages(requestDto.getFeedImageUrls()); feed.setTags(tags); return feedRepository.save(feed); }
변경이후의 postFeed 메서드는 객체를 외부로 부터 주입받아 테스트의 용이한 메서드로 바뀌는 부과적인 효과도 생겼다.
'Project > TravelFeelDog' 카테고리의 다른 글
SpringBoot 3.1.0 으로 마이그레이션 (0) 2023.09.07 SpringBoot MySql bulkInsert 도입기 (0) 2023.08.30 Firebase 에서 OAuth2 , JWT 로 전환기(1) (0) 2023.08.08 yml 환경 변수 설정 (0) 2023.07.18 경로 에러 추가 하기 (0) 2023.07.18