Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import com.example.log4u.domain.diary.SortType;
import com.example.log4u.domain.diary.dto.DiaryRequestDto;
import com.example.log4u.domain.diary.dto.DiaryResponseDto;
import com.example.log4u.domain.diary.service.DiaryService;
import com.example.log4u.domain.diary.facade.DiaryFacade;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
Expand All @@ -30,7 +30,7 @@
@Slf4j
public class DiaryController {

private final DiaryService diaryService;
private final DiaryFacade diaryFacade;

@GetMapping("/users/{userId}")
public ResponseEntity<PageResponse<DiaryResponseDto>> getDiariesByUserId(
Expand All @@ -39,7 +39,7 @@ public ResponseEntity<PageResponse<DiaryResponseDto>> getDiariesByUserId(
@RequestParam(required = false) Long cursorId,
@RequestParam(defaultValue = "12") int size
) {
PageResponse<DiaryResponseDto> response = diaryService.getDiariesByCursor(customOAuth2User.getUserId(),
PageResponse<DiaryResponseDto> response = diaryFacade.getDiariesByCursor(customOAuth2User.getUserId(),
targetUserId, cursorId, size);

return ResponseEntity.ok(response);
Expand All @@ -50,7 +50,7 @@ public ResponseEntity<Void> createDiary(
@AuthenticationPrincipal CustomOAuth2User customOAuth2User,
@Valid @RequestBody DiaryRequestDto request
) {
diaryService.saveDiary(customOAuth2User.getUserId(), request);
diaryFacade.createDiary(customOAuth2User.getUserId(), request);
return ResponseEntity.status(HttpStatus.CREATED).build();
}

Expand All @@ -63,7 +63,7 @@ public ResponseEntity<PageResponse<DiaryResponseDto>> searchDiaries(
@RequestParam(defaultValue = "6") int size
) {
return ResponseEntity.ok(
diaryService.searchDiariesByCursor(keyword, sort, cursorId, size)
diaryFacade.searchDiariesByCursor(keyword, sort, cursorId, size)
);
}

Expand All @@ -72,7 +72,7 @@ public ResponseEntity<DiaryResponseDto> getDiary(
@AuthenticationPrincipal CustomOAuth2User customOAuth2User,
@PathVariable Long diaryId
) {
DiaryResponseDto diary = diaryService.getDiary(customOAuth2User.getUserId(), diaryId);
DiaryResponseDto diary = diaryFacade.getDiary(customOAuth2User.getUserId(), diaryId);
return ResponseEntity.ok(diary);
}

Expand All @@ -82,16 +82,16 @@ public ResponseEntity<Void> modifyDiary(
@PathVariable Long diaryId,
@Valid @RequestBody DiaryRequestDto request
) {
diaryService.updateDiary(customOAuth2User.getUserId(), diaryId, request);
diaryFacade.updateDiary(customOAuth2User.getUserId(), diaryId, request);
return ResponseEntity.ok().build();
}

@DeleteMapping("/{diaryId}")
public ResponseEntity<?> deleteDiary(
public ResponseEntity<Void> deleteDiary(
@AuthenticationPrincipal CustomOAuth2User customOAuth2User,
@PathVariable Long diaryId
) {
diaryService.deleteDiary(customOAuth2User.getUserId(), diaryId);
diaryFacade.deleteDiary(customOAuth2User.getUserId(), diaryId);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
}
127 changes: 127 additions & 0 deletions src/main/java/com/example/log4u/domain/diary/facade/DiaryFacade.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package com.example.log4u.domain.diary.facade;

import java.util.List;

import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Component;

import com.example.log4u.common.dto.PageResponse;
import com.example.log4u.domain.diary.SortType;
import com.example.log4u.domain.diary.dto.DiaryRequestDto;
import com.example.log4u.domain.diary.dto.DiaryResponseDto;
import com.example.log4u.domain.diary.entity.Diary;
import com.example.log4u.domain.diary.service.DiaryService;
import com.example.log4u.domain.like.service.LikeService;
import com.example.log4u.domain.map.service.MapService;
import com.example.log4u.domain.media.entity.Media;
import com.example.log4u.domain.media.service.MediaService;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class DiaryFacade {

private final DiaryService diaryService;
private final MediaService mediaService;
private final MapService mapService;
private final LikeService likeService;

/**
* 다이어리 생성 use case
* <ul><li>호출 과정</li></ul>
* 1. mediaService: 섬네일 이미지 url 생성<br>
* 2. diaryService: 다이어리 생성<br>
* 2. mediaService: 해당 다이어리의 이미지 저장<br>
* 3. mapService: 해당 구역 카운트 증가
* */
public void createDiary(Long userId, DiaryRequestDto request) {
String thumbnailUrl = mediaService.extractThumbnailUrl(request.mediaList());
Diary diary = diaryService.saveDiary(userId, request, thumbnailUrl);
mediaService.saveMedia(diary.getDiaryId(), request.mediaList());
mapService.increaseRegionDiaryCount(request.location().latitude(), request.location().longitude());
}

/**
* 다이어리 삭제 use case
* <ul><li>호출 과정</li></ul>
* 1. diaryService: 다이어리 검증
* 2. mediaService: 해당 다이어리 이미지 삭제<br>
* 3. diaryService: 다이어리 삭제<br>
* */
public void deleteDiary(Long userId, Long diaryId) {
Diary diary = diaryService.getDiaryAfterValidateOwnership(userId, diaryId);
mediaService.deleteMediaByDiaryId(diaryId);
diaryService.deleteDiary(diary);
}

/**
* 다이어리 수정 use case
* <ul><li>호출 과정</li></ul>
* 1. diaryService: 다이어리 검증<br>
* 2. mediaService: 해당 다이어리 이미지 삭제<br>
* 3. diaryService: 다이어리 수정
* */
public void updateDiary(Long userId, Long diaryId, DiaryRequestDto request) {
Diary diary = diaryService.getDiaryAfterValidateOwnership(userId, diaryId);
if (request.mediaList() != null) {
mediaService.updateMediaByDiaryId(diary.getDiaryId(), request.mediaList());
}
String newThumbnailUrl = mediaService.extractThumbnailUrl(request.mediaList());
diaryService.updateDiary(diary, request, newThumbnailUrl);
}

/**
* 다이어리 단건 조회 use case
* <ul><li>호출 과정</li></ul>
* 1. diaryService: 공개 범위 검증 후 다이어리 조회<br>
* 2. likeService: 좋아요 기록 조회<br>
* 3. mediaService: 해당 다이어리의 이미지 조회<br>
* 4. 모든 정보 조합 후 dto 변환 해 반환
* */
public DiaryResponseDto getDiary(Long userId, Long diaryId) {
Diary diary = diaryService.getDiaryAfterValidateAccess(userId, diaryId);
boolean isLiked = likeService.isLiked(userId, diaryId);
List<Media> media = mediaService.getMediaByDiaryId(diary.getDiaryId());
return DiaryResponseDto.of(diary, media, isLiked);
}

/**
* 다이어리 목록 조회 By UserId use case
* <ul><li>호출 과정</li></ul>
* 1. diaryService : DiaryResponseDto Slice 객체 조회<br>
* 2. nextCursor 정보 생성<br>
* 3. PageResponse 조합 후 반환
* */
public PageResponse<DiaryResponseDto> getDiariesByCursor(
Long userId,
Long targetUserId,
Long cursorId,
int size
) {
Slice<DiaryResponseDto> dtoSlice = diaryService.getDiaryResponseDtoSlice(userId, targetUserId, cursorId, size);
// 다음 커서 ID 계산
Long nextCursor = !dtoSlice.isEmpty() ? dtoSlice.getContent().getLast().diaryId() : null;
return PageResponse.of(dtoSlice, nextCursor);
}

/**
* 다이어리 검색 목록 조회 By Cursor use case
* <ul><li>호출 과정</li></ul>
* 1. diaryService : DiaryResponseDto Slice 객체 조회<br>
* 2. nextCursor 정보 생성<br>
* 3. PageResponse 조합 후 반환<br>
* */
public PageResponse<DiaryResponseDto> searchDiariesByCursor(
String keyword,
SortType sort,
Long cursorId,
int size
) {
Slice<DiaryResponseDto> dtoSlice = diaryService.searchDiariesByCursor(keyword, sort, cursorId, size);
// 다음 커서 ID 계산
Long nextCursor = !dtoSlice.isEmpty() ? dtoSlice.getContent().getLast().diaryId() : null;
return PageResponse.of(dtoSlice, nextCursor);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import com.example.log4u.domain.diary.repository.DiaryRepository;
import com.example.log4u.domain.follow.repository.FollowRepository;
import com.example.log4u.domain.like.repository.LikeRepository;
import com.example.log4u.domain.map.service.MapService;
import com.example.log4u.domain.media.entity.Media;
import com.example.log4u.domain.media.service.MediaService;

Expand All @@ -37,23 +36,17 @@ public class DiaryService {
private final DiaryRepository diaryRepository;
private final FollowRepository followRepository;
private final MediaService mediaService;
private final MapService mapService;
private final LikeRepository likeRepository;

// 다이어리 생성
@Transactional
public void saveDiary(Long userId, DiaryRequestDto request) {
String thumbnailUrl = mediaService.extractThumbnailUrl(request.mediaList());
Diary diary = diaryRepository.save(
DiaryRequestDto.toEntity(userId, request, thumbnailUrl)
);
mediaService.saveMedia(diary.getDiaryId(), request.mediaList());
mapService.increaseRegionDiaryCount(request.location().latitude(), request.location().longitude());
public Diary saveDiary(Long userId, DiaryRequestDto request, String thumbnailUrl) {
return diaryRepository.save(DiaryRequestDto.toEntity(userId, request, thumbnailUrl));
}

// 다이어리 검색
@Transactional(readOnly = true)
public PageResponse<DiaryResponseDto> searchDiariesByCursor(
public Slice<DiaryResponseDto> searchDiariesByCursor(
String keyword,
SortType sort,
Long cursorId,
Expand All @@ -66,17 +59,12 @@ public PageResponse<DiaryResponseDto> searchDiariesByCursor(
cursorId != null ? cursorId : Long.MAX_VALUE,
PageRequest.of(0, size)
);

Slice<DiaryResponseDto> dtoSlice = mapToDtoSlice(diaries);

// 다음 커서 ID 계산
Long nextCursor = !dtoSlice.isEmpty() ? dtoSlice.getContent().getLast().diaryId() : null;

return PageResponse.of(dtoSlice, nextCursor);
return mapToDtoSlice(diaries);
}

// 다이어리 상세 조회
@Transactional(readOnly = true)
@Deprecated(since = "파사드 패턴 도입으로 인해 불필요해짐", forRemoval = false)
public DiaryResponseDto getDiary(Long userId, Long diaryId) {
Diary diary = findDiaryOrThrow(diaryId);

Expand All @@ -89,43 +77,27 @@ public DiaryResponseDto getDiary(Long userId, Long diaryId) {

// 다이어리 목록 (프로필 페이지)
@Transactional(readOnly = true)
public PageResponse<DiaryResponseDto> getDiariesByCursor(Long userId, Long targetUserId, Long cursorId, int size) {
public Slice<DiaryResponseDto> getDiaryResponseDtoSlice(Long userId, Long targetUserId, Long cursorId, int size) {
List<VisibilityType> visibilities = determineAccessibleVisibilities(userId, targetUserId);

Slice<Diary> diaries = diaryRepository.findByUserIdAndVisibilityInAndCursorId(
targetUserId,
visibilities,
cursorId != null ? cursorId : Long.MAX_VALUE,
PageRequest.of(0, size)
);

Slice<DiaryResponseDto> dtoSlice = mapToDtoSlice(diaries);

Long nextCursor = !dtoSlice.isEmpty() ? dtoSlice.getContent().getLast().diaryId() : null;

return PageResponse.of(dtoSlice, nextCursor);
return mapToDtoSlice(diaries);
}

// 다이어리 수정
@Transactional
public void updateDiary(Long userId, Long diaryId, DiaryRequestDto request) {
Diary diary = findDiaryOrThrow(diaryId);
validateOwner(diary, userId);

if (request.mediaList() != null) {
mediaService.updateMediaByDiaryId(diary.getDiaryId(), request.mediaList());
}

String newThumbnailUrl = mediaService.extractThumbnailUrl(request.mediaList());
public void updateDiary(Diary diary, DiaryRequestDto request, String newThumbnailUrl) {
diary.update(request, newThumbnailUrl);
diaryRepository.save(diary);
}

// 다이어리 삭제
@Transactional
public void deleteDiary(Long userId, Long diaryId) {
Diary diary = findDiaryOrThrow(diaryId);
validateOwner(diary, userId);
mediaService.deleteMediaByDiaryId(diaryId);
public void deleteDiary(Diary diary) {
diaryRepository.delete(diary);
}

Expand Down Expand Up @@ -173,7 +145,7 @@ private void validateOwner(Diary diary, Long userId) {
}
}

// 다이어리 목록 조회 시 권한 체크
// 다이어리 목록 조회 시 권한 체크(공개 정책)
private List<VisibilityType> determineAccessibleVisibilities(Long userId, Long targetUserId) {
if (userId.equals(targetUserId)) {
return List.of(VisibilityType.PUBLIC, VisibilityType.PRIVATE, VisibilityType.FOLLOWER);
Expand All @@ -186,6 +158,20 @@ private List<VisibilityType> determineAccessibleVisibilities(Long userId, Long t
return List.of(VisibilityType.PUBLIC);
}

// 파사드 패턴에서 사용할 검증 로직(소유 검증)
public Diary getDiaryAfterValidateOwnership(Long diaryId, Long userId) {
Diary diary = findDiaryOrThrow(diaryId);
validateOwner(diary, userId);
return diary;
}

// 파사드 패턴에서 사용할 검증 로직(공개 범위 검증)
public Diary getDiaryAfterValidateAccess(Long diaryId, Long userId) {
Diary diary = findDiaryOrThrow(diaryId);
validateDiaryAccess(diary, userId);
return diary;
}

// 다이어리 상세 조회 시 권한 체크
private void validateDiaryAccess(Diary diary, Long userId) {
if (diary.getVisibility() == VisibilityType.PRIVATE) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.example.log4u.domain.like.service;

import java.util.Optional;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -52,4 +50,9 @@ private void validateDuplicateLike(Long userId, Long diaryId) {
throw new DuplicateLikeException();
}
}

// 파사드 패턴에서 사용할 함수
public boolean isLiked(Long userId, Long diaryId) {
return likeRepository.existsByUserIdAndDiaryId(userId, diaryId);
}
}
Loading