Skip to content

Commit

Permalink
[BE] Refactor/#634: Bookmark 객체 의존성 분리 및 복합키 적용 (#636)
Browse files Browse the repository at this point in the history
* refactor: PermissionId 생성자 접근 제어자 수정

* refactor: BookmarkId 구현

* refactor: Bookmark 의존성 분리 (간접 참조)

* refactor: 불필요한 mocking 로직 제거

* refactor: 미사용 import문 제거

* refactor: Bookmark 조회 로직 수정 (JPQL 제거)

* refactor: PermissionId Null 검증 로직 추가

* refactor: BookmarkId Null 검증 로직 추가
  • Loading branch information
cpot5620 authored Dec 15, 2023
1 parent 55f46fe commit 04f3a0f
Show file tree
Hide file tree
Showing 25 changed files with 232 additions and 216 deletions.
Original file line number Diff line number Diff line change
@@ -1,59 +1,43 @@
package com.mapbefine.mapbefine.bookmark.application;

import static com.mapbefine.mapbefine.bookmark.exception.BookmarkErrorCode.CONFLICT_TOPIC_ALREADY_ADD;
import static com.mapbefine.mapbefine.bookmark.exception.BookmarkErrorCode.FORBIDDEN_TOPIC_ADD;
import static com.mapbefine.mapbefine.bookmark.exception.BookmarkErrorCode.FORBIDDEN_TOPIC_DELETE;
import static com.mapbefine.mapbefine.bookmark.exception.BookmarkErrorCode.ILLEGAL_TOPIC_ID;

import com.mapbefine.mapbefine.auth.domain.AuthMember;
import com.mapbefine.mapbefine.bookmark.domain.Bookmark;
import com.mapbefine.mapbefine.bookmark.domain.BookmarkId;
import com.mapbefine.mapbefine.bookmark.domain.BookmarkRepository;
import com.mapbefine.mapbefine.bookmark.exception.BookmarkException.BookmarkBadRequestException;
import com.mapbefine.mapbefine.bookmark.exception.BookmarkException.BookmarkConflictException;
import com.mapbefine.mapbefine.bookmark.exception.BookmarkException.BookmarkForbiddenException;
import com.mapbefine.mapbefine.member.domain.Member;
import com.mapbefine.mapbefine.member.domain.MemberRepository;
import com.mapbefine.mapbefine.topic.domain.Topic;
import com.mapbefine.mapbefine.topic.domain.TopicRepository;
import java.util.NoSuchElementException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static com.mapbefine.mapbefine.bookmark.exception.BookmarkErrorCode.*;

@Service
@Transactional
public class BookmarkCommandService {

private final BookmarkRepository bookmarkRepository;

private final MemberRepository memberRepository;

private final TopicRepository topicRepository;

public BookmarkCommandService(
BookmarkRepository bookmarkRepository,
MemberRepository memberRepository,
TopicRepository topicRepository
) {
this.bookmarkRepository = bookmarkRepository;
this.memberRepository = memberRepository;
this.topicRepository = topicRepository;
}

public Long addTopicInBookmark(AuthMember authMember, Long topicId) {
public void addTopicInBookmark(AuthMember authMember, Long topicId) {
validateBookmarkDuplication(authMember, topicId);
Topic topic = getTopicById(topicId);
validateBookmarkingPermission(authMember, topic);
Member member = findMemberById(authMember);

Bookmark bookmark = Bookmark.createWithAssociatedTopicAndMember(topic, member);
Bookmark bookmark = Bookmark.of(topic, authMember.getMemberId());
bookmarkRepository.save(bookmark);

return bookmark.getId();
}

private Topic getTopicById(Long topicId) {
return topicRepository.findById(topicId)
.orElseThrow(() -> new BookmarkBadRequestException(ILLEGAL_TOPIC_ID));
topic.increaseBookmarkCount();
}

private void validateBookmarkDuplication(AuthMember authMember, Long topicId) {
Expand All @@ -63,7 +47,12 @@ private void validateBookmarkDuplication(AuthMember authMember, Long topicId) {
}

private boolean isExistBookmark(AuthMember authMember, Long topicId) {
return bookmarkRepository.existsByMemberIdAndTopicId(authMember.getMemberId(), topicId);
return bookmarkRepository.existsById(BookmarkId.of(topicId, authMember.getMemberId()));
}

private Topic getTopicById(Long topicId) {
return topicRepository.findById(topicId)
.orElseThrow(() -> new BookmarkBadRequestException(ILLEGAL_TOPIC_ID));
}

private void validateBookmarkingPermission(AuthMember authMember, Topic topic) {
Expand All @@ -74,27 +63,14 @@ private void validateBookmarkingPermission(AuthMember authMember, Topic topic) {
throw new BookmarkForbiddenException(FORBIDDEN_TOPIC_ADD);
}

private Member findMemberById(AuthMember authMember) {
Long memberId = authMember.getMemberId();

return memberRepository.findById(memberId)
.orElseThrow(() -> new NoSuchElementException("findMemberById; memberId=" + memberId));
}

// TODO: 2023/12/03 BookmarkCount의 정합성을 어떻게 맞출 것인가 ? 매번 topic 조회하는 것은 불필요한 행위같아보임
public void deleteTopicInBookmark(AuthMember authMember, Long topicId) {
validateBookmarkDeletingPermission(authMember, topicId);
BookmarkId bookmarkId = BookmarkId.of(topicId, authMember.getMemberId());

Bookmark bookmark = findBookmarkByMemberIdAndTopicId(authMember.getMemberId(), topicId);
bookmarkRepository.deleteById(bookmarkId);
Topic topic = getTopicById(topicId);

topic.removeBookmark(bookmark);
}

private Bookmark findBookmarkByMemberIdAndTopicId(Long memberId, Long topicId) {
return bookmarkRepository.findByMemberIdAndTopicId(memberId, topicId)
.orElseThrow(() -> new NoSuchElementException(
"findBookmarkByMemberIdAndTopicId; memberId=" + memberId + " topicId=" + topicId
));
topic.decreaseBookmarkCount();
}

private void validateBookmarkDeletingPermission(AuthMember authMember, Long topicId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
package com.mapbefine.mapbefine.bookmark.domain;

import static com.mapbefine.mapbefine.bookmark.exception.BookmarkErrorCode.ILLEGAL_MEMBER_ID;
import static com.mapbefine.mapbefine.bookmark.exception.BookmarkErrorCode.ILLEGAL_TOPIC_ID;

import com.mapbefine.mapbefine.bookmark.exception.BookmarkException.BookmarkBadRequestException;
import com.mapbefine.mapbefine.member.domain.Member;
import com.mapbefine.mapbefine.topic.domain.Topic;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import java.util.Objects;
import jakarta.persistence.MapsId;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -22,40 +16,29 @@
@Getter
public class Bookmark {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@EmbeddedId
private BookmarkId id;

@ManyToOne
@MapsId("topicId")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "topic_id", nullable = false)
private Topic topic;

@ManyToOne
@JoinColumn(name = "member_id", nullable = false)
private Member member;

private Bookmark(Topic topic, Member member) {
private Bookmark(BookmarkId id, Topic topic) {
this.id = id;
this.topic = topic;
this.member = member;
}

public static Bookmark createWithAssociatedTopicAndMember(Topic topic, Member member) {
validateNotNull(topic, member);
Bookmark bookmark = new Bookmark(topic, member);

topic.addBookmark(bookmark);
member.addBookmark(bookmark);
public static Bookmark of(Topic topic, Long memberId) {
return new Bookmark(BookmarkId.of(topic.getId(), memberId), topic);
}

return bookmark;
public Long getTopicId() {
return id.getTopicId();
}

private static void validateNotNull(Topic topic, Member member) {
if (Objects.isNull(topic)) {
throw new BookmarkBadRequestException(ILLEGAL_TOPIC_ID);
}
if (Objects.isNull(member)) {
throw new BookmarkBadRequestException(ILLEGAL_MEMBER_ID);
}
public Long getMemberId() {
return id.getMemberId();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.mapbefine.mapbefine.bookmark.domain;

import com.mapbefine.mapbefine.bookmark.exception.BookmarkErrorCode;
import com.mapbefine.mapbefine.bookmark.exception.BookmarkException.BookmarkBadRequestException;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Objects;

@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class BookmarkId implements Serializable {

@Column(nullable = false, updatable = false)
private Long topicId;

@Column(nullable = false, updatable = false)
private Long memberId;

private BookmarkId(Long topicId, Long memberId) {
this.topicId = topicId;
this.memberId = memberId;
}

public static BookmarkId of(Long topicId, Long memberId) {
validateNotNull(topicId, memberId);

return new BookmarkId(topicId, memberId);
}

private static void validateNotNull(Long topicId, Long memberId) {
if (Objects.isNull(topicId)) {
throw new BookmarkBadRequestException(BookmarkErrorCode.ILLEGAL_TOPIC_ID);
}

if (Objects.isNull(memberId)) {
throw new BookmarkBadRequestException(BookmarkErrorCode.ILLEGAL_MEMBER_ID);
}
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BookmarkId that = (BookmarkId) o;
return Objects.equals(getTopicId(), that.getTopicId()) && Objects.equals(getMemberId(), that.getMemberId());
}

@Override
public int hashCode() {
return Objects.hash(getTopicId(), getMemberId());
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
package com.mapbefine.mapbefine.bookmark.domain;

import java.util.Optional;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface BookmarkRepository extends JpaRepository<Bookmark, Long> {
import java.util.List;

Optional<Bookmark> findByMemberIdAndTopicId(Long memberId, Long topicId);
public interface BookmarkRepository extends JpaRepository<Bookmark, BookmarkId> {

boolean existsByMemberIdAndTopicId(Long memberId, Long topicId);
List<Long> findAllIdTopicIdByIdMemberId(Long memberId);

@EntityGraph(attributePaths = "topic")
List<Bookmark> findAllByIdMemberId(Long memberId);

@Modifying(clearAutomatically = true)
@Query("delete from Bookmark b where b.member.id = :memberId")
@Query("delete from Bookmark b where b.id.memberId = :memberId")
void deleteAllByMemberId(@Param("memberId") Long memberId);

@Modifying(clearAutomatically = true)
@Query("delete from Bookmark b where b.topic.id = :topicId")
@Query("delete from Bookmark b where b.id.topicId = :topicId")
void deleteAllByTopicId(@Param("topicId") Long topicId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import com.mapbefine.mapbefine.auth.domain.AuthMember;
import com.mapbefine.mapbefine.bookmark.application.BookmarkCommandService;
import com.mapbefine.mapbefine.common.interceptor.LoginRequired;
import java.net.URI;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
Expand All @@ -22,17 +22,17 @@ public BookmarkController(BookmarkCommandService bookmarkCommandService) {
}

@LoginRequired
@PostMapping("/topics")
public ResponseEntity<Void> addTopicInBookmark(AuthMember authMember, @RequestParam Long id) {
Long bookmarkId = bookmarkCommandService.addTopicInBookmark(authMember, id);
@PostMapping("/topics/{topicId}")
public ResponseEntity<Void> addTopicInBookmark(AuthMember authMember, @PathVariable Long topicId) {
bookmarkCommandService.addTopicInBookmark(authMember, topicId);

return ResponseEntity.created(URI.create("/bookmarks/topics" + bookmarkId)).build();
return ResponseEntity.status(HttpStatus.CREATED).build();
}

@LoginRequired
@DeleteMapping("/topics")
public ResponseEntity<Void> deleteTopicInBookmark(AuthMember authMember, @RequestParam Long id) {
bookmarkCommandService.deleteTopicInBookmark(authMember, id);
@DeleteMapping("/topics/{topicId}")
public ResponseEntity<Void> deleteTopicInBookmark(AuthMember authMember, @PathVariable Long topicId) {
bookmarkCommandService.deleteTopicInBookmark(authMember, topicId);

return ResponseEntity.noContent().build();
}
Expand Down
Loading

0 comments on commit 04f3a0f

Please sign in to comment.