ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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 메서드는 객체를 외부로 부터 주입받아 테스트의 용이한 메서드로 바뀌는 부과적인 효과도 생겼다.

     

    https://github.com/HUFS-Capstone-23-01/TravelFeelDog-Server/blob/develop/travelfeeldog/src/main/java/travelfeeldog/domain/community/feed/application/usecase/CreateFeedUsecase.java

     

Designed by Tistory.