Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
4 changes: 4 additions & 0 deletions src/main/java/com/catcher/app/AppApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@ComponentScan(basePackages = {"com.catcher.core", "com.catcher.resource"})
@EnableJpaRepositories(basePackages = {"com.catcher.datasource"})
@EntityScan(basePackages = {"com.catcher.core.domain.entity"})
public class AppApplication {

public static void main(String[] args) {
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/catcher/core/GetCommentCommandExecutor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.catcher.core;

import com.catcher.core.domain.command.Command;
import com.catcher.core.domain.command.GetParentCommentsByPageCommand;
import com.catcher.core.domain.entity.Comment;
import com.catcher.datasource.CommentRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@RequiredArgsConstructor
public class GetCommentCommandExecutor implements CommandExecutor<Page<Comment>> {
Comment thread
cheolwon1994 marked this conversation as resolved.
Outdated

private final CommentRepository commentRepository;

@Override
@Transactional
public Page<Comment> run(final Command command) {
//1. datasource layer 호출(DB)
//2. 가공해서 넘겨줘야 한다
return switch (command.getClass().getSimpleName()) {
case "GetParentCommentsByPageCommand" -> getParentCommentsByPage(command);
default -> null;
};
}

private Page<Comment> getParentCommentsByPage(final Command command) {

final GetParentCommentsByPageCommand getParentCommentsByPageCommand = (GetParentCommentsByPageCommand) command;
return commentRepository.findByParentIsNull(getParentCommentsByPageCommand.getPageable());
}
}
60 changes: 60 additions & 0 deletions src/main/java/com/catcher/core/PostCommentCommandExecutor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.catcher.core;

import com.catcher.core.domain.command.Command;
import com.catcher.core.domain.command.PostCommentCommand;
import com.catcher.core.domain.command.PostCommentReplyCommand;
import com.catcher.core.domain.entity.Comment;
import com.catcher.datasource.CommentRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@RequiredArgsConstructor
public class PostCommentCommandExecutor implements CommandExecutor<Comment> {

private final CommentRepository commentRepository;

@Override
@Transactional
public Comment run(final Command command) {
//1. datasource layer 호출(DB)
//2. 가공해서 넘겨줘야 한다
return switch (command.getClass().getSimpleName()) {
case "PostCommentCommand" -> postComment(command);
case "PostCommentReplyCommand" -> postCommentReply(command);
default -> null;
};
}

private Comment postComment(final Command command) {

final PostCommentCommand postCommentCommand = (PostCommentCommand)command;
return commentRepository.save(Comment
.builder()
.userId(postCommentCommand.getUserId())
.contents(postCommentCommand.getContents())
.build());
}

private Comment postCommentReply(final Command command) {

final PostCommentReplyCommand postCommentReplyCommand = (PostCommentReplyCommand) command;

final Comment parentComment = commentRepository
.findById(postCommentReplyCommand.getParentId())
.orElseThrow(); //TODO: fill custom exception

final Comment reply = Comment
.builder()
.userId(postCommentReplyCommand.getUserId())
.parent(parentComment)
.contents(postCommentReplyCommand.getContents())
.build();

parentComment.getReplies().add(reply);

return reply;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.catcher.core.domain.command;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.data.domain.Pageable;

@Data
@AllArgsConstructor
public class GetParentCommentsByPageCommand implements Command {

private Pageable pageable;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.catcher.core.domain.command;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class PostCommentCommand implements Command {

Long userId;

String contents;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.catcher.core.domain.command;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class PostCommentReplyCommand implements Command {

Long userId;

Long parentId;

String contents;
}
33 changes: 33 additions & 0 deletions src/main/java/com/catcher/core/domain/entity/Comment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.catcher.core.domain.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne
@JoinColumn(name = "parent_id")
private Comment parent;

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@Builder.Default
private List<Comment> replies = new ArrayList<>();

private Long userId;

private String contents;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.catcher.core.domain.request;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder // TODO: 테스트를 위해서만 필요한 경우?
@NoArgsConstructor
@AllArgsConstructor
public class PostCommentReplyRequest {

private Long userId;

private Long parentId;

private String contents;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.catcher.core.domain.request;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder // TODO: 테스트를 위해서만 필요한 경우?
@NoArgsConstructor
@AllArgsConstructor
public class PostCommentRequest {

private Long userId;

private String contents;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.catcher.core.domain.response;

import com.catcher.core.domain.entity.Comment;
import lombok.Builder;
import lombok.Getter;
import org.springframework.data.domain.Page;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Getter
@Builder
public class GetCommentsByPageResponse {

private Long id;

private String contents;

private List<GetCommentsByPageResponse> childComments;

public static List<GetCommentsByPageResponse> createGetCommentsByPageResponseList(Page<Comment> commentPage) {
Comment thread
pingu9 marked this conversation as resolved.
return commentPage
.stream()
.map(GetCommentsByPageResponse::buildRecursiveCommentResponse)
.collect(Collectors.toList());
}

private static GetCommentsByPageResponse buildRecursiveCommentResponse(Comment comment) {
GetCommentsByPageResponse response = GetCommentsByPageResponse
.builder()
.id(comment.getId())
.contents(comment.getContents())
.childComments(new ArrayList<>())
.build();

for (Comment reply : comment.getReplies()) {
response.getChildComments().add(buildRecursiveCommentResponse(reply));
}

return response;
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/catcher/core/service/CommentService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.catcher.core.service;

import com.catcher.core.GetCommentCommandExecutor;
import com.catcher.core.domain.command.GetParentCommentsByPageCommand;
import com.catcher.core.domain.entity.Comment;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class CommentService {

private final GetCommentCommandExecutor getCommentCommandExecutor;

public Page<Comment> getCommentsWithSize(final Pageable pageable) {
return getCommentCommandExecutor.run(new GetParentCommentsByPageCommand(pageable));
}
}
14 changes: 14 additions & 0 deletions src/main/java/com/catcher/datasource/CommentRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.catcher.datasource;

import com.catcher.core.domain.entity.Comment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CommentRepository extends JpaRepository<Comment, Long> {

// TODO: N+1 join
// @Query(value = "SELECT DISTINCT c FROM Comment c LEFT OUTER JOIN FETCH c.replies WHERE c.parent IS NULL")
Page<Comment> findByParentIsNull(Pageable pageable);

}
49 changes: 49 additions & 0 deletions src/main/java/com/catcher/resource/CommentAPiController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.catcher.resource;

import com.catcher.core.PostCommentCommandExecutor;
import com.catcher.core.domain.command.PostCommentCommand;
import com.catcher.core.domain.command.PostCommentReplyCommand;
import com.catcher.core.domain.request.PostCommentReplyRequest;
import com.catcher.core.domain.request.PostCommentRequest;
import com.catcher.core.domain.response.GetCommentsByPageResponse;
import com.catcher.core.service.CommentService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RequiredArgsConstructor
@RestController
@RequestMapping("/comment")
public class CommentAPiController {

private final PostCommentCommandExecutor postCommentCommandExecutor;

private final CommentService commentService;

@PostMapping
public void postComment(@RequestBody PostCommentRequest postCommentRequest) {
postCommentCommandExecutor.run(new PostCommentCommand(
postCommentRequest.getUserId(),
postCommentRequest.getContents())
);
}

@PostMapping("/reply")
public void replyComment(@RequestBody PostCommentReplyRequest postCommentReplyRequest) {
postCommentCommandExecutor.run(new PostCommentReplyCommand(
postCommentReplyRequest.getUserId(),
postCommentReplyRequest.getParentId(),
postCommentReplyRequest.getContents()
));
}

@GetMapping
public List<GetCommentsByPageResponse> getComments(@PageableDefault(size = 20, sort = {"id"}) Pageable pageable) {
final var commentPage = commentService.getCommentsWithSize(pageable);

return GetCommentsByPageResponse.createGetCommentsByPageResponseList(commentPage);
}
}
4 changes: 4 additions & 0 deletions src/test/java/com/catcher/app/AppApplicationTests.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package com.catcher.app;

import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootTest
@ComponentScan(basePackages = {"com.catcher.resource"})
@EnableJpaRepositories(basePackages = {"com.catcher.datasource"})
@EntityScan(basePackages = {"com.catcher.core.domain.entity"})
class AppApplicationTests {

@Test
Expand Down
11 changes: 11 additions & 0 deletions src/test/java/com/catcher/datasource/TestCommentRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.catcher.datasource;

import com.catcher.core.domain.entity.Comment;
import org.springframework.data.jpa.repository.JpaRepository;

// TODO: 프로덕션 코드에는 없는 기능이 필요한 경우?
Comment thread
pingu9 marked this conversation as resolved.
public interface TestCommentRepository extends JpaRepository<Comment, Long> {

Comment findFirstByUserIdAndContentsOrderByIdDesc(Long userId, String contents);

}
Loading