From 8653bd86a9af409c6f2dfe0a3a9cec34d5a8fa21 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sat, 3 May 2025 23:05:31 +0900 Subject: [PATCH 01/25] feat(comment): add comment-service build gradle file --- monolith/main-runner/build.gradle.kts | 1 + monolith/main-runner/src/main/resources/application.yml | 1 + settings.gradle.kts | 1 + 3 files changed, 3 insertions(+) diff --git a/monolith/main-runner/build.gradle.kts b/monolith/main-runner/build.gradle.kts index 0098171..f976217 100644 --- a/monolith/main-runner/build.gradle.kts +++ b/monolith/main-runner/build.gradle.kts @@ -9,6 +9,7 @@ dependencies { implementation(project(":cors-webmvc")) // service implementation(project(":board")) + implementation(project(":comment")) // webmvc implementation("org.springframework.boot:spring-boot-starter-web") // db diff --git a/monolith/main-runner/src/main/resources/application.yml b/monolith/main-runner/src/main/resources/application.yml index 9fe712b..d0c7c58 100644 --- a/monolith/main-runner/src/main/resources/application.yml +++ b/monolith/main-runner/src/main/resources/application.yml @@ -6,6 +6,7 @@ spring: import: - properties.web/main.cors.yml - board.yml + - comment.yml server: port: 5000 diff --git a/settings.gradle.kts b/settings.gradle.kts index d09e594..dd1ad23 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,3 +7,4 @@ apply(from = "core/core.settings.gradle.kts") apply(from = "monolith/monolith.settings.gradle.kts") apply(from = "$services/board/board.settings.gradle.kts") +apply(from = "$services/comment/comment.settings.gradle.kts") \ No newline at end of file From fe8311f4da495658b16474f11d5878ec4337d727 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sat, 3 May 2025 23:06:29 +0900 Subject: [PATCH 02/25] fix(board): add flyway to board.database-local.yml --- .../main/resources/properties/db/board.database-local.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/board/driven/rdb/src/main/resources/properties/db/board.database-local.yml b/services/board/driven/rdb/src/main/resources/properties/db/board.database-local.yml index 2532871..5e721ef 100644 --- a/services/board/driven/rdb/src/main/resources/properties/db/board.database-local.yml +++ b/services/board/driven/rdb/src/main/resources/properties/db/board.database-local.yml @@ -1,5 +1,11 @@ spring: datasource: + driver-class-name: org.postgresql.Driver url: ${BOARD_POSTGRESQL_URL:jdbc:postgresql://localhost:5433/demo} username: ${BOARD_POSTGRESQL_USERNAME:root} password: ${BOARD_POSTGRESQL_PASSWORD:root} + + flyway: + baseline-on-migrate: true + locations: + - db/postgresql/migration/v1_0 \ No newline at end of file From 11a0274d3bee8e2653c7886f2126aa3377ca1b1f Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sat, 3 May 2025 23:16:16 +0900 Subject: [PATCH 03/25] feat(comment/reply): create domain, exception, read-model to api module --- services/comment/api/build.gradle.kts | 5 ++ .../src/main/java/nettee/comment/Comment.java | 43 +++++++++++++ .../nettee/comment/type/CommentStatus.java | 18 ++++++ .../src/main/java/nettee/reply/Reply.java | 44 +++++++++++++ .../java/nettee/reply/type/ReplyStatus.java | 18 ++++++ .../comment/CommentCommandErrorCode.java | 62 +++++++++++++++++++ .../comment/CommentCommandException.java | 36 +++++++++++ .../nettee/comment/CommentQueryErrorCode.java | 61 ++++++++++++++++++ .../nettee/comment/CommentQueryException.java | 31 ++++++++++ .../nettee/reply/ReplyCommandErrorCode.java | 62 +++++++++++++++++++ .../nettee/reply/ReplyCommandException.java | 36 +++++++++++ .../nettee/reply/ReplyQueryErrorCode.java | 61 ++++++++++++++++++ .../nettee/reply/ReplyQueryException.java | 31 ++++++++++ .../comment/api/readmodel/build.gradle.kts | 3 + .../comment/model/CommentQueryModels.java | 32 ++++++++++ .../nettee/reply/model/ReplyQueryModels.java | 33 ++++++++++ 16 files changed, 576 insertions(+) create mode 100644 services/comment/api/build.gradle.kts create mode 100644 services/comment/api/domain/src/main/java/nettee/comment/Comment.java create mode 100644 services/comment/api/domain/src/main/java/nettee/comment/type/CommentStatus.java create mode 100644 services/comment/api/domain/src/main/java/nettee/reply/Reply.java create mode 100644 services/comment/api/domain/src/main/java/nettee/reply/type/ReplyStatus.java create mode 100644 services/comment/api/exception/src/main/java/nettee/comment/CommentCommandErrorCode.java create mode 100644 services/comment/api/exception/src/main/java/nettee/comment/CommentCommandException.java create mode 100644 services/comment/api/exception/src/main/java/nettee/comment/CommentQueryErrorCode.java create mode 100644 services/comment/api/exception/src/main/java/nettee/comment/CommentQueryException.java create mode 100644 services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandErrorCode.java create mode 100644 services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandException.java create mode 100644 services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryErrorCode.java create mode 100644 services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryException.java create mode 100644 services/comment/api/readmodel/build.gradle.kts create mode 100644 services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java create mode 100644 services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java diff --git a/services/comment/api/build.gradle.kts b/services/comment/api/build.gradle.kts new file mode 100644 index 0000000..c6638c4 --- /dev/null +++ b/services/comment/api/build.gradle.kts @@ -0,0 +1,5 @@ +dependencies { + api(project(":comment:api:domain")) + api(project(":comment:api:exception")) + api(project(":comment:api:readmodel")) +} \ No newline at end of file diff --git a/services/comment/api/domain/src/main/java/nettee/comment/Comment.java b/services/comment/api/domain/src/main/java/nettee/comment/Comment.java new file mode 100644 index 0000000..448d201 --- /dev/null +++ b/services/comment/api/domain/src/main/java/nettee/comment/Comment.java @@ -0,0 +1,43 @@ +package nettee.comment; + +import java.time.Instant; +import java.util.Objects; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import nettee.comment.type.CommentStatus; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Comment { + + private Long id; + + private String content; + + private CommentStatus status; + + private Instant createdAt; + + private Instant updatedAt; + + @Builder( + builderClassName = "updateCommentBuilder", + builderMethodName = "prepareUpdate", + buildMethodName = "update" + ) + public void update(String content) { + Objects.requireNonNull(content, "content cannot be null"); + + this.content = content; + this.updatedAt = Instant.now(); + } + + public void softDelete() { + this.status = CommentStatus.REMOVED; + } + +} diff --git a/services/comment/api/domain/src/main/java/nettee/comment/type/CommentStatus.java b/services/comment/api/domain/src/main/java/nettee/comment/type/CommentStatus.java new file mode 100644 index 0000000..323397e --- /dev/null +++ b/services/comment/api/domain/src/main/java/nettee/comment/type/CommentStatus.java @@ -0,0 +1,18 @@ +package nettee.comment.type; + +import java.util.EnumSet; +import java.util.Set; + +public enum CommentStatus { + + PENDING, + ACTIVE, + REMOVED; + + private static final Set GENERAL_QUERY_STATUS = EnumSet.of(ACTIVE); + + public static Set getGeneralQueryStatus() { + return GENERAL_QUERY_STATUS; + } + +} diff --git a/services/comment/api/domain/src/main/java/nettee/reply/Reply.java b/services/comment/api/domain/src/main/java/nettee/reply/Reply.java new file mode 100644 index 0000000..5a09965 --- /dev/null +++ b/services/comment/api/domain/src/main/java/nettee/reply/Reply.java @@ -0,0 +1,44 @@ +package nettee.reply; + +import java.time.Instant; +import java.util.Objects; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import nettee.reply.type.ReplyStatus; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Reply { + + private Long id; + + private Long parentId; + + private String content; + + private ReplyStatus status; + + private Instant createdAt; + + private Instant updatedAt; + + @Builder( + builderClassName = "updateReplyBuilder", + builderMethodName = "prepareUpdate", + buildMethodName = "update" + ) + public void update(String content) { + Objects.requireNonNull(content, "content cannot be null"); + + this.content = content; + this.updatedAt = Instant.now(); + } + + public void softDelete() { + this.status = ReplyStatus.REMOVED; + } +} diff --git a/services/comment/api/domain/src/main/java/nettee/reply/type/ReplyStatus.java b/services/comment/api/domain/src/main/java/nettee/reply/type/ReplyStatus.java new file mode 100644 index 0000000..25b13b5 --- /dev/null +++ b/services/comment/api/domain/src/main/java/nettee/reply/type/ReplyStatus.java @@ -0,0 +1,18 @@ +package nettee.reply.type; + +import java.util.EnumSet; +import java.util.Set; + +public enum ReplyStatus { + + PENDING, + ACTIVE, + REMOVED; + + private static final Set GENERAL_QUERY_STATUS = EnumSet.of(ACTIVE); + + public static Set getGeneralQueryStatus() { + return GENERAL_QUERY_STATUS; + } + +} diff --git a/services/comment/api/exception/src/main/java/nettee/comment/CommentCommandErrorCode.java b/services/comment/api/exception/src/main/java/nettee/comment/CommentCommandErrorCode.java new file mode 100644 index 0000000..d5431f6 --- /dev/null +++ b/services/comment/api/exception/src/main/java/nettee/comment/CommentCommandErrorCode.java @@ -0,0 +1,62 @@ +package nettee.comment; + +import java.util.Map; +import java.util.function.Supplier; +import nettee.common.ErrorCode; +import org.springframework.http.HttpStatus; + +public enum CommentCommandErrorCode implements ErrorCode { + COMMENT_NOT_FOUND("댓글을 찾을 수 없습니다.", HttpStatus.NOT_FOUND), + COMMENT_GONE("더 이상 존재하지 않는 댓글입니다.", HttpStatus.GONE), + COMMENT_FORBIDDEN("권한이 없습니다.", HttpStatus.FORBIDDEN), + DEFAULT("댓글 조작 오류", HttpStatus.INTERNAL_SERVER_ERROR), + COMMENT_ALREADY_EXIST("댓글이 이미 존재합니다.", HttpStatus.CONFLICT); + + private final String message; + private final HttpStatus httpStatus; + + CommentCommandErrorCode(String message, HttpStatus httpStatus) { + this.message = message; + this.httpStatus = httpStatus; + } + + @Override + public String message() { + return message; + } + + @Override + public HttpStatus httpStatus() { + return httpStatus; + } + + @Override + public CommentCommandException exception() { + return new CommentCommandException(this); + } + + @Override + public CommentCommandException exception(Throwable cause) { + return new CommentCommandException(this, cause); + } + + @Override + public RuntimeException exception(Runnable runnable) { + return new CommentCommandException(this, runnable); + } + + @Override + public RuntimeException exception(Runnable runnable, Throwable cause) { + return new CommentCommandException(this, runnable, cause); + } + + @Override + public RuntimeException exception(Supplier> payload) { + return new CommentCommandException(this, payload); + } + + @Override + public RuntimeException exception(Supplier> payload, Throwable cause) { + return new CommentCommandException(this, payload, cause); + } +} diff --git a/services/comment/api/exception/src/main/java/nettee/comment/CommentCommandException.java b/services/comment/api/exception/src/main/java/nettee/comment/CommentCommandException.java new file mode 100644 index 0000000..2d517a4 --- /dev/null +++ b/services/comment/api/exception/src/main/java/nettee/comment/CommentCommandException.java @@ -0,0 +1,36 @@ +package nettee.comment; + +import java.util.Map; +import java.util.function.Supplier; +import nettee.common.CustomException; +import nettee.common.ErrorCode; + +public class CommentCommandException extends CustomException { + + /** + * CommentErrorCodeLazyHolder를 파라미터로 받기 위해, ErrorCode 타입으로 임시 설정함. + */ + public CommentCommandException(ErrorCode errorCode) { + super(errorCode); + } + + public CommentCommandException(ErrorCode errorCode, Throwable cause) { + super(errorCode, cause); + } + + public CommentCommandException(ErrorCode errorCode, Runnable runnable) { + super(errorCode, runnable); + } + + public CommentCommandException(ErrorCode errorCode, Runnable runnable, Throwable cause) { + super(errorCode, runnable, cause); + } + + public CommentCommandException(ErrorCode errorCode, Supplier> payload) { + super(errorCode, payload); + } + + public CommentCommandException(ErrorCode errorCode, Supplier> payload, Throwable cause) { + super(errorCode, payload, cause); + } +} diff --git a/services/comment/api/exception/src/main/java/nettee/comment/CommentQueryErrorCode.java b/services/comment/api/exception/src/main/java/nettee/comment/CommentQueryErrorCode.java new file mode 100644 index 0000000..100bc06 --- /dev/null +++ b/services/comment/api/exception/src/main/java/nettee/comment/CommentQueryErrorCode.java @@ -0,0 +1,61 @@ +package nettee.comment; + +import java.util.Map; +import java.util.function.Supplier; +import nettee.common.ErrorCode; +import org.springframework.http.HttpStatus; + +public enum CommentQueryErrorCode implements ErrorCode { + COMMENT_NOT_FOUND("댓글을 찾을 수 없습니다.", HttpStatus.NOT_FOUND), + COMMENT_GONE("더 이상 존재하지 않는 댓글입니다.", HttpStatus.GONE), + COMMENT_FORBIDDEN("권한이 없습니다.", HttpStatus.FORBIDDEN), + DEFAULT("댓글 조작 오류", HttpStatus.INTERNAL_SERVER_ERROR); + + private final String message; + private final HttpStatus httpStatus; + + CommentQueryErrorCode(String message, HttpStatus httpStatus) { + this.message = message; + this.httpStatus = httpStatus; + } + + @Override + public String message() { + return message; + } + + @Override + public HttpStatus httpStatus() { + return httpStatus; + } + + @Override + public CommentQueryException exception() { + return new CommentQueryException(this); + } + + @Override + public CommentQueryException exception(Throwable cause) { + return new CommentQueryException(this, cause); + } + + @Override + public RuntimeException exception(Runnable runnable) { + return new CommentQueryException(this, runnable); + } + + @Override + public RuntimeException exception(Runnable runnable, Throwable cause) { + return new CommentQueryException(this, runnable, cause); + } + + @Override + public RuntimeException exception(Supplier> payload) { + return new CommentQueryException(this, payload); + } + + @Override + public RuntimeException exception(Supplier> payload, Throwable cause) { + return new CommentQueryException(this, payload, cause); + } +} diff --git a/services/comment/api/exception/src/main/java/nettee/comment/CommentQueryException.java b/services/comment/api/exception/src/main/java/nettee/comment/CommentQueryException.java new file mode 100644 index 0000000..5d35896 --- /dev/null +++ b/services/comment/api/exception/src/main/java/nettee/comment/CommentQueryException.java @@ -0,0 +1,31 @@ +package nettee.comment; + +import java.util.Map; +import java.util.function.Supplier; +import nettee.common.CustomException; + +public class CommentQueryException extends CustomException { + public CommentQueryException(CommentQueryErrorCode errorCode) { + super(errorCode); + } + + public CommentQueryException(CommentQueryErrorCode errorCode, Throwable cause) { + super(errorCode, cause); + } + + public CommentQueryException(CommentQueryErrorCode errorCode, Runnable runnable) { + super(errorCode, runnable); + } + + public CommentQueryException(CommentQueryErrorCode errorCode, Runnable runnable, Throwable cause) { + super(errorCode, runnable, cause); + } + + public CommentQueryException(CommentQueryErrorCode errorCode, Supplier> payload) { + super(errorCode, payload); + } + + public CommentQueryException(CommentQueryErrorCode errorCode, Supplier> payload, Throwable cause) { + super(errorCode, payload, cause); + } +} diff --git a/services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandErrorCode.java b/services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandErrorCode.java new file mode 100644 index 0000000..b3f0010 --- /dev/null +++ b/services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandErrorCode.java @@ -0,0 +1,62 @@ +package nettee.reply; + +import java.util.Map; +import java.util.function.Supplier; +import nettee.common.ErrorCode; +import org.springframework.http.HttpStatus; + +public enum ReplyCommandErrorCode implements ErrorCode { + REPLY_NOT_FOUND("답글을 찾을 수 없습니다.", HttpStatus.NOT_FOUND), + REPLY_GONE("더 이상 존재하지 않는 답글입니다.", HttpStatus.GONE), + REPLY_FORBIDDEN("권한이 없습니다.", HttpStatus.FORBIDDEN), + DEFAULT("답글 조작 오류", HttpStatus.INTERNAL_SERVER_ERROR), + REPLY_ALREADY_EXIST("답글이 이미 존재합니다.", HttpStatus.CONFLICT); + + private final String message; + private final HttpStatus httpStatus; + + ReplyCommandErrorCode(String message, HttpStatus httpStatus) { + this.message = message; + this.httpStatus = httpStatus; + } + + @Override + public String message() { + return message; + } + + @Override + public HttpStatus httpStatus() { + return httpStatus; + } + + @Override + public ReplyCommandException exception() { + return new ReplyCommandException(this); + } + + @Override + public ReplyCommandException exception(Throwable cause) { + return new ReplyCommandException(this, cause); + } + + @Override + public RuntimeException exception(Runnable runnable) { + return new ReplyCommandException(this, runnable); + } + + @Override + public RuntimeException exception(Runnable runnable, Throwable cause) { + return new ReplyCommandException(this, runnable, cause); + } + + @Override + public RuntimeException exception(Supplier> payload) { + return new ReplyCommandException(this, payload); + } + + @Override + public RuntimeException exception(Supplier> payload, Throwable cause) { + return new ReplyCommandException(this, payload, cause); + } +} diff --git a/services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandException.java b/services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandException.java new file mode 100644 index 0000000..414f12a --- /dev/null +++ b/services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandException.java @@ -0,0 +1,36 @@ +package nettee.reply; + +import java.util.Map; +import java.util.function.Supplier; +import nettee.common.CustomException; +import nettee.common.ErrorCode; + +public class ReplyCommandException extends CustomException { + + /** + * ReplyErrorCodeLazyHolder를 파라미터로 받기 위해, ErrorCode 타입으로 임시 설정함. + */ + public ReplyCommandException(ErrorCode errorCode) { + super(errorCode); + } + + public ReplyCommandException(ErrorCode errorCode, Throwable cause) { + super(errorCode, cause); + } + + public ReplyCommandException(ErrorCode errorCode, Runnable runnable) { + super(errorCode, runnable); + } + + public ReplyCommandException(ErrorCode errorCode, Runnable runnable, Throwable cause) { + super(errorCode, runnable, cause); + } + + public ReplyCommandException(ErrorCode errorCode, Supplier> payload) { + super(errorCode, payload); + } + + public ReplyCommandException(ErrorCode errorCode, Supplier> payload, Throwable cause) { + super(errorCode, payload, cause); + } +} diff --git a/services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryErrorCode.java b/services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryErrorCode.java new file mode 100644 index 0000000..91bd5c9 --- /dev/null +++ b/services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryErrorCode.java @@ -0,0 +1,61 @@ +package nettee.reply; + +import java.util.Map; +import java.util.function.Supplier; +import nettee.common.ErrorCode; +import org.springframework.http.HttpStatus; + +public enum ReplyQueryErrorCode implements ErrorCode { + REPLY_NOT_FOUND("답글을 찾을 수 없습니다.", HttpStatus.NOT_FOUND), + REPLY_GONE("더 이상 존재하지 않는 답글입니다.", HttpStatus.GONE), + REPLY_FORBIDDEN("권한이 없습니다.", HttpStatus.FORBIDDEN), + DEFAULT("답글 조작 오류", HttpStatus.INTERNAL_SERVER_ERROR); + + private final String message; + private final HttpStatus httpStatus; + + ReplyQueryErrorCode(String message, HttpStatus httpStatus) { + this.message = message; + this.httpStatus = httpStatus; + } + + @Override + public String message() { + return message; + } + + @Override + public HttpStatus httpStatus() { + return httpStatus; + } + + @Override + public ReplyQueryException exception() { + return new ReplyQueryException(this); + } + + @Override + public ReplyQueryException exception(Throwable cause) { + return new ReplyQueryException(this, cause); + } + + @Override + public RuntimeException exception(Runnable runnable) { + return new ReplyQueryException(this, runnable); + } + + @Override + public RuntimeException exception(Runnable runnable, Throwable cause) { + return new ReplyQueryException(this, runnable, cause); + } + + @Override + public RuntimeException exception(Supplier> payload) { + return new ReplyQueryException(this, payload); + } + + @Override + public RuntimeException exception(Supplier> payload, Throwable cause) { + return new ReplyQueryException(this, payload, cause); + } +} diff --git a/services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryException.java b/services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryException.java new file mode 100644 index 0000000..0f87edf --- /dev/null +++ b/services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryException.java @@ -0,0 +1,31 @@ +package nettee.reply; + +import java.util.Map; +import java.util.function.Supplier; +import nettee.common.CustomException; + +public class ReplyQueryException extends CustomException { + public ReplyQueryException(ReplyQueryErrorCode errorCode) { + super(errorCode); + } + + public ReplyQueryException(ReplyQueryErrorCode errorCode, Throwable cause) { + super(errorCode, cause); + } + + public ReplyQueryException(ReplyQueryErrorCode errorCode, Runnable runnable) { + super(errorCode, runnable); + } + + public ReplyQueryException(ReplyQueryErrorCode errorCode, Runnable runnable, Throwable cause) { + super(errorCode, runnable, cause); + } + + public ReplyQueryException(ReplyQueryErrorCode errorCode, Supplier> payload) { + super(errorCode, payload); + } + + public ReplyQueryException(ReplyQueryErrorCode errorCode, Supplier> payload, Throwable cause) { + super(errorCode, payload, cause); + } +} diff --git a/services/comment/api/readmodel/build.gradle.kts b/services/comment/api/readmodel/build.gradle.kts new file mode 100644 index 0000000..6c95b71 --- /dev/null +++ b/services/comment/api/readmodel/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + api(project(":comment:api:domain")) +} \ No newline at end of file diff --git a/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java b/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java new file mode 100644 index 0000000..bef349d --- /dev/null +++ b/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java @@ -0,0 +1,32 @@ +package nettee.comment.model; + +import lombok.Builder; + +import java.time.Instant; +import nettee.comment.type.CommentStatus; + +public final class CommentQueryModels { + + private CommentQueryModels() { + } + + @Builder + public record CommentDetail( + Long id, + String content, + CommentStatus status, + Instant createdAt, + Instant updatedAt + ) { + } + + @Builder + public record CommentSummary( + Long id, + String title, + CommentStatus status, + Instant createdAt, + Instant updatedAt + ) { + } +} \ No newline at end of file diff --git a/services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java b/services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java new file mode 100644 index 0000000..f580abb --- /dev/null +++ b/services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java @@ -0,0 +1,33 @@ +package nettee.reply.model; + +import java.time.Instant; +import lombok.Builder; +import nettee.reply.type.ReplyStatus; + +public final class ReplyQueryModels { + + private ReplyQueryModels() { + } + + @Builder + public record ReplyDetail( + Long id, + Long parentId, + String content, + ReplyStatus status, + Instant createdAt, + Instant updatedAt + ) { + } + + @Builder + public record ReplySummary( + Long id, + Long parentId, + String title, + ReplyStatus status, + Instant createdAt, + Instant updatedAt + ) { + } +} \ No newline at end of file From 6a8053d841b0c254bef13fa0ccfd38df7ce2a7fa Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sat, 3 May 2025 23:17:00 +0900 Subject: [PATCH 04/25] feat(comment/reply): create port to application module --- services/comment/application/build.gradle.kts | 3 +++ .../port/CommentCommandRepositoryPort.java | 12 ++++++++++++ .../port/CommentQueryRepositoryPort.java | 16 ++++++++++++++++ .../reply/port/ReplyCommandRepositoryPort.java | 12 ++++++++++++ .../reply/port/ReplyQueryRepositoryPort.java | 17 +++++++++++++++++ 5 files changed, 60 insertions(+) create mode 100644 services/comment/application/build.gradle.kts create mode 100644 services/comment/application/src/main/java/nettee/comment/port/CommentCommandRepositoryPort.java create mode 100644 services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java create mode 100644 services/comment/application/src/main/java/nettee/reply/port/ReplyCommandRepositoryPort.java create mode 100644 services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java diff --git a/services/comment/application/build.gradle.kts b/services/comment/application/build.gradle.kts new file mode 100644 index 0000000..af95832 --- /dev/null +++ b/services/comment/application/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + api(project(":comment:api")) +} \ No newline at end of file diff --git a/services/comment/application/src/main/java/nettee/comment/port/CommentCommandRepositoryPort.java b/services/comment/application/src/main/java/nettee/comment/port/CommentCommandRepositoryPort.java new file mode 100644 index 0000000..0c9b842 --- /dev/null +++ b/services/comment/application/src/main/java/nettee/comment/port/CommentCommandRepositoryPort.java @@ -0,0 +1,12 @@ +package nettee.comment.port; + +import nettee.comment.Comment; + +public interface CommentCommandRepositoryPort { + + Comment save(Comment comment); + + Comment update(Comment comment); + + void delete(Comment comment); +} diff --git a/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java b/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java new file mode 100644 index 0000000..49ccd7c --- /dev/null +++ b/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java @@ -0,0 +1,16 @@ +package nettee.comment.port; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import nettee.comment.Comment; + +public interface CommentQueryRepositoryPort { + Optional findById(Long id); + + // post_id에 해당하는 최초 comment 10개 조회 + List find10ByPostId(Long postId); + + // post_id, 현재 페이지의 마지막 이후의 comment 10개 조회 + List find10ByPostIdAfterCommentId(Long postId, Instant createdAt); +} diff --git a/services/comment/application/src/main/java/nettee/reply/port/ReplyCommandRepositoryPort.java b/services/comment/application/src/main/java/nettee/reply/port/ReplyCommandRepositoryPort.java new file mode 100644 index 0000000..4f59c24 --- /dev/null +++ b/services/comment/application/src/main/java/nettee/reply/port/ReplyCommandRepositoryPort.java @@ -0,0 +1,12 @@ +package nettee.reply.port; + +import nettee.reply.Reply; + +public interface ReplyCommandRepositoryPort { + + Reply save(Reply reply); + + Reply update(Reply reply); + + void delete(Reply reply); +} diff --git a/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java b/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java new file mode 100644 index 0000000..4eadb4c --- /dev/null +++ b/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java @@ -0,0 +1,17 @@ +package nettee.reply.port; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import nettee.reply.Reply; + +public interface ReplyQueryRepositoryPort { + + Optional findById(Long id); + + // comment_id에 해당하는 최초 reply 10개 조회 + List find10ByCommentId(Long commentId); + + // comment_id, 현재 페이지의 마지막 이후의 reply 10개 조회 + List find10ByCommentIdAfter(Long commentId, Instant createdAt); +} From 95edb7dccc0e512cd3fe4402c6e12e70bc12eea8 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sat, 3 May 2025 23:18:53 +0900 Subject: [PATCH 05/25] feat(comment/reply): create entity to driven/entity module --- services/comment/driven/rdb/build.gradle.kts | 20 +++ .../nettee/comment/entity/CommentEntity.java | 26 ++++ .../entity/type/CommentEntityStatus.java | 121 ++++++++++++++++++ .../mapper/CommentEntityMapper.java | 14 ++ .../java/nettee/reply/entity/ReplyEntity.java | 26 ++++ .../reply/entity/type/ReplyEntityStatus.java | 120 +++++++++++++++++ .../mapper/CommentEntityMapper.java | 14 ++ 7 files changed, 341 insertions(+) create mode 100644 services/comment/driven/rdb/build.gradle.kts create mode 100644 services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatus.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatus.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/CommentEntityMapper.java diff --git a/services/comment/driven/rdb/build.gradle.kts b/services/comment/driven/rdb/build.gradle.kts new file mode 100644 index 0000000..ee3d71f --- /dev/null +++ b/services/comment/driven/rdb/build.gradle.kts @@ -0,0 +1,20 @@ +dependencies { + val bom = dependencyManagement.importedProperties + + api(project(":comment:api")) + api(project(":comment:application")) + api(project(":jpa-core")) + + // spring + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + + // querydsl + implementation("com.querydsl:querydsl-jpa:${bom["querydsl.version"]}:jakarta") + annotationProcessor("com.querydsl:querydsl-apt:${bom["querydsl.version"]}:jakarta") + annotationProcessor("jakarta.persistence:jakarta.persistence-api") + + // mapstruct + implementation("org.mapstruct:mapstruct:1.6.3") + annotationProcessor("org.mapstruct:mapstruct-processor:1.6.3") + annotationProcessor("org.projectlombok:lombok-mapstruct-binding:0.2.0") +} \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java new file mode 100644 index 0000000..834eb8f --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java @@ -0,0 +1,26 @@ +package nettee.comment.entity; + +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import nettee.comment.entity.type.CommentEntityStatus; +import nettee.jpa.support.LongBaseTimeEntity; +import org.hibernate.annotations.DynamicUpdate; + +@Getter +@DynamicUpdate +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity(name = "comment") +public class CommentEntity extends LongBaseTimeEntity { + + @Id + private Long id; + + private String content; + + @Convert + private CommentEntityStatus status; +} diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatus.java b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatus.java new file mode 100644 index 0000000..a52ef89 --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatus.java @@ -0,0 +1,121 @@ +package nettee.comment.entity.type; + +import nettee.comment.type.CommentStatus; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import static nettee.comment.CommentCommandErrorCode.DEFAULT; + +public enum CommentEntityStatus { + REMOVED( + SemanticCodeParameters.builder() + .canRead(false) + .classifyingBits(0b0000_0000_0000_0000) + ), + PENDING( + SemanticCodeParameters.builder() + .canRead(false) + .classifyingBits(0b0000_0000_0000_0001) + ), + ACTIVE( + SemanticCodeParameters.builder() + .canRead(true) + .classifyingBits(0b0000_0000_0000_0010) + ); + + /* + R000 0000 0000 0000 0PPP PPPP PPPP PPPP + R: generally readable status (1: readable, 0: unreadable) + 0: classifying bits (16 bits) + P: detailed or padded bits (15 bits) + */ + private static final int TLB_PADDING_SIZE = 31; + private static final int CLASSIFYING_PADDING_SIZE = 15; + + private final int code; + + static { + // NOTE util 함수가 추가되면 리팩토링 + assert Arrays.stream(values()) + .map(CommentEntityStatus::getCode) + .collect(Collectors.toSet()) + .size() + == values().length + : "CommentEntityStatus의 모든 code 필드가 고유해야 합니다."; + } + + CommentEntityStatus(SemanticCodeParameters semanticCodeParameters) { + this( + semanticCodeParameters.canRead, + semanticCodeParameters.classifyingBits, + semanticCodeParameters.detailBits + ); + } + + CommentEntityStatus(boolean canRead, int classifyingBits, int detailBits) { + this.code = (canRead ? 1 << TLB_PADDING_SIZE : 0) + | (classifyingBits << CLASSIFYING_PADDING_SIZE) + | detailBits; + } + + public int getCode() { + return code; + } + + public static CommentEntityStatus valueOf(CommentStatus commentStatus) { + assert Set.of(CommentStatus.REMOVED, CommentStatus.PENDING, CommentStatus.ACTIVE) + .containsAll(Arrays.stream(CommentStatus.values()).collect(Collectors.toSet())) + : "commentStatus 중 일부가 CommentEntityStatus::valueOf 함수에서 매핑되지 않습니다."; + + return switch (commentStatus){ + case REMOVED -> REMOVED; + case PENDING -> PENDING; + case ACTIVE -> ACTIVE; + default -> throw new Error("commentStatus 중 일부가 CommentEntityStatus::valueOf 함수에서 매핑되지 않습니다."); + }; + } + + public static CommentEntityStatus valueOf(int value) { + return switch (value) { + case 0b0__0000_0000_0000_0000__000_0000_0000_0000 -> REMOVED; + case 0b0__0000_0000_0000_0001__000_0000_0000_0000 -> PENDING; + case 0b1__0000_0000_0000_0010__000_0000_0000_0000 -> ACTIVE; + default -> throw DEFAULT.exception(); + }; + } + + static class SemanticCodeParameters { + boolean canRead; + Integer classifyingBits; + int detailBits; + + private SemanticCodeParameters() {} + + public static SemanticCodeParameters builder() { + return new SemanticCodeParameters<>(); + } + + SemanticCodeParameters classifyingBits(Integer classifyingBits) { + this.classifyingBits = classifyingBits; + return (SemanticCodeParameters) this; + } + + SemanticCodeParameters canRead(boolean canRead) { + this.canRead = canRead; + return (SemanticCodeParameters) this; + } + + SemanticCodeParameters detailBits(int detailBits) { + this.detailBits = detailBits; + return this; + } + + } + + // NOTE move to other module after its place is determined + // Marker interfaces + interface Missing {} + interface Present {} +} \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java new file mode 100644 index 0000000..c4ecf67 --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java @@ -0,0 +1,14 @@ +package nettee.comment.persistence.mapper; + +import nettee.comment.Comment; +import nettee.comment.entity.CommentEntity; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface CommentEntityMapper { + + Comment toDomain(CommentEntity commentEntity); + + CommentEntity toEntity(Comment comment); + +} \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java new file mode 100644 index 0000000..08c6cf9 --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java @@ -0,0 +1,26 @@ +package nettee.reply.entity; + +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import nettee.jpa.support.LongBaseTimeEntity; +import nettee.reply.entity.type.ReplyEntityStatus; +import org.hibernate.annotations.DynamicUpdate; + +@Getter +@DynamicUpdate +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity(name = "comment") +public class ReplyEntity extends LongBaseTimeEntity { + + @Id + private Long id; + + private String content; + + @Convert + private ReplyEntityStatus status; +} diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatus.java b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatus.java new file mode 100644 index 0000000..7ba94dc --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatus.java @@ -0,0 +1,120 @@ +package nettee.reply.entity.type; + +import static nettee.reply.ReplyCommandErrorCode.DEFAULT; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; +import nettee.reply.type.ReplyStatus; + +public enum ReplyEntityStatus { + REMOVED( + SemanticCodeParameters.builder() + .canRead(false) + .classifyingBits(0b0000_0000_0000_0000) + ), + PENDING( + SemanticCodeParameters.builder() + .canRead(false) + .classifyingBits(0b0000_0000_0000_0001) + ), + ACTIVE( + SemanticCodeParameters.builder() + .canRead(true) + .classifyingBits(0b0000_0000_0000_0010) + ); + + /* + R000 0000 0000 0000 0PPP PPPP PPPP PPPP + R: generally readable status (1: readable, 0: unreadable) + 0: classifying bits (16 bits) + P: detailed or padded bits (15 bits) + */ + private static final int TLB_PADDING_SIZE = 31; + private static final int CLASSIFYING_PADDING_SIZE = 15; + + private final int code; + + static { + // NOTE util 함수가 추가되면 리팩토링 + assert Arrays.stream(values()) + .map(ReplyEntityStatus::getCode) + .collect(Collectors.toSet()) + .size() + == values().length + : "ReplyEntityStatus의 모든 code 필드가 고유해야 합니다."; + } + + ReplyEntityStatus(SemanticCodeParameters semanticCodeParameters) { + this( + semanticCodeParameters.canRead, + semanticCodeParameters.classifyingBits, + semanticCodeParameters.detailBits + ); + } + + ReplyEntityStatus(boolean canRead, int classifyingBits, int detailBits) { + this.code = (canRead ? 1 << TLB_PADDING_SIZE : 0) + | (classifyingBits << CLASSIFYING_PADDING_SIZE) + | detailBits; + } + + public int getCode() { + return code; + } + + public static ReplyEntityStatus valueOf(ReplyStatus replyStatus) { + assert Set.of(ReplyStatus.REMOVED, ReplyStatus.PENDING, ReplyStatus.ACTIVE) + .containsAll(Arrays.stream(replyStatus.values()).collect(Collectors.toSet())) + : "replyStatus 중 일부가 ReplyEntityStatus::valueOf 함수에서 매핑되지 않습니다."; + + return switch (replyStatus){ + case REMOVED -> REMOVED; + case PENDING -> PENDING; + case ACTIVE -> ACTIVE; + default -> throw new Error("replyStatus 중 일부가 ReplyEntityStatus::valueOf 함수에서 매핑되지 않습니다."); + }; + } + + public static ReplyEntityStatus valueOf(int value) { + return switch (value) { + case 0b0__0000_0000_0000_0000__000_0000_0000_0000 -> REMOVED; + case 0b0__0000_0000_0000_0001__000_0000_0000_0000 -> PENDING; + case 0b1__0000_0000_0000_0010__000_0000_0000_0000 -> ACTIVE; + default -> throw DEFAULT.exception(); + }; + } + + static class SemanticCodeParameters { + boolean canRead; + Integer classifyingBits; + int detailBits; + + private SemanticCodeParameters() {} + + public static SemanticCodeParameters builder() { + return new SemanticCodeParameters<>(); + } + + SemanticCodeParameters classifyingBits(Integer classifyingBits) { + this.classifyingBits = classifyingBits; + return (SemanticCodeParameters) this; + } + + SemanticCodeParameters canRead(boolean canRead) { + this.canRead = canRead; + return (SemanticCodeParameters) this; + } + + SemanticCodeParameters detailBits(int detailBits) { + this.detailBits = detailBits; + return this; + } + + } + + // NOTE move to other module after its place is determined + // Marker interfaces + interface Missing {} + interface Present {} +} \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/CommentEntityMapper.java b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/CommentEntityMapper.java new file mode 100644 index 0000000..a555a6e --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/CommentEntityMapper.java @@ -0,0 +1,14 @@ +package nettee.reply.persistence.mapper; + +import nettee.comment.Comment; +import nettee.reply.entity.ReplyEntity; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface CommentEntityMapper { + + Comment toDomain(ReplyEntity commentEntity); + + ReplyEntity toEntity(Comment comment); + +} \ No newline at end of file From e41ab5b2e397ca7a06a0b0102c3fb8f77e435be2 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sat, 3 May 2025 23:19:41 +0900 Subject: [PATCH 06/25] build(comment/reply): create yml, postgresql script to entity module --- .../migration/v1_0/V1_0_0__enable_uuid.sql | 1 + .../v1_0/V1_0_1__create_tb_comment.sql | 18 +++++++++++++++++ .../v1_0/V1_0_1__create_tb_reply.sql | 20 +++++++++++++++++++ ...1_0_2__alter_comment_status_as_integer.sql | 1 + .../V1_0_2__alter_reply_status_as_integer.sql | 1 + .../properties/db/comment.database-local.yml | 11 ++++++++++ .../properties/db/comment.database.yml | 11 ++++++++++ 7 files changed, 63 insertions(+) create mode 100644 services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_0__enable_uuid.sql create mode 100644 services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_comment.sql create mode 100644 services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_reply.sql create mode 100644 services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_comment_status_as_integer.sql create mode 100644 services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_reply_status_as_integer.sql create mode 100644 services/comment/driven/rdb/src/main/resources/properties/db/comment.database-local.yml create mode 100644 services/comment/driven/rdb/src/main/resources/properties/db/comment.database.yml diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_0__enable_uuid.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_0__enable_uuid.sql new file mode 100644 index 0000000..682131d --- /dev/null +++ b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_0__enable_uuid.sql @@ -0,0 +1 @@ +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_comment.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_comment.sql new file mode 100644 index 0000000..a5d9879 --- /dev/null +++ b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_comment.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS comment ( + id BIGSERIAL, + content VARCHAR(255) + status VARCHAR(255), + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP, + + CONSTRAINT pk_comment PRIMARY KEY (id) +); + +--테이블 코멘트 +COMMENT ON TABLE comment IS '댓글'; + +-- 컬럼 코멘트 +COMMENT ON COLUMN comment.content IS '내용'; +COMMENT ON COLUMN comment.status IS '상태'; +COMMENT ON COLUMN comment.created_at IS '생성시간'; +COMMENT ON COLUMN comment.updated_at IS '마지막 수정시간'; \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_reply.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_reply.sql new file mode 100644 index 0000000..095c57c --- /dev/null +++ b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_reply.sql @@ -0,0 +1,20 @@ +CREATE TABLE IF NOT EXISTS reply ( + id BIGSERIAL, + parent_id BIGSERIAL, + content VARCHAR(255) + status VARCHAR(255), + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP, + + CONSTRAINT pk_comment PRIMARY KEY (id) +); + +--테이블 코멘트 +COMMENT ON TABLE comment IS '답글'; + +-- 컬럼 코멘트 +COMMENT ON COLUMN comment.parent_id IS '부모 댓글 ID'; +COMMENT ON COLUMN comment.content IS '내용'; +COMMENT ON COLUMN comment.status IS '상태'; +COMMENT ON COLUMN comment.created_at IS '생성시간'; +COMMENT ON COLUMN comment.updated_at IS '마지막 수정시간'; \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_comment_status_as_integer.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_comment_status_as_integer.sql new file mode 100644 index 0000000..9ad7036 --- /dev/null +++ b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_comment_status_as_integer.sql @@ -0,0 +1 @@ +ALTER TABLE comment ALTER COLUMN status TYPE INTEGER USING status::INTEGER; \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_reply_status_as_integer.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_reply_status_as_integer.sql new file mode 100644 index 0000000..3152301 --- /dev/null +++ b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_reply_status_as_integer.sql @@ -0,0 +1 @@ +ALTER TABLE reply ALTER COLUMN status TYPE INTEGER USING status::INTEGER; \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/resources/properties/db/comment.database-local.yml b/services/comment/driven/rdb/src/main/resources/properties/db/comment.database-local.yml new file mode 100644 index 0000000..7422de6 --- /dev/null +++ b/services/comment/driven/rdb/src/main/resources/properties/db/comment.database-local.yml @@ -0,0 +1,11 @@ +spring: + datasource: + driver-class-name: org.postgresql.Driver + url: ${COMMENT_POSTGRESQL_URL:jdbc:postgresql://localhost:5433/demo} + username: ${COMMENT_POSTGRESQL_USERNAME:root} + password: ${COMMENT_POSTGRESQL_PASSWORD:root} + + flyway: + baseline-on-migrate: true + locations: + - db/postgresql/migration/v1_0 \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/resources/properties/db/comment.database.yml b/services/comment/driven/rdb/src/main/resources/properties/db/comment.database.yml new file mode 100644 index 0000000..1d09b72 --- /dev/null +++ b/services/comment/driven/rdb/src/main/resources/properties/db/comment.database.yml @@ -0,0 +1,11 @@ +spring: + datasource: + driver-class-name: org.postgresql.Driver + url: ${COMMENT_POSTGRESQL_URL} + username: ${COMMENT_POSTGRESQL_USERNAME} + password: ${COMMENT_POSTGRESQL_PASSWORD} + + flyway: + baseline-on-migrate: true + locations: + - db/postgresql/migration/v1_0 \ No newline at end of file From 223fd5dd5f7d63f89c6b6e9af101398c0b88eaff Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sat, 3 May 2025 23:23:23 +0900 Subject: [PATCH 07/25] build(comment/reply): build comment module configurations (settings.gradle.kts, yml, build.gradle.kts) --- services/comment/build.gradle.kts | 6 ++++ services/comment/comment.settings.gradle.kts | 35 +++++++++++++++++++ .../comment/src/main/resources/comment.yml | 5 +++ 3 files changed, 46 insertions(+) create mode 100644 services/comment/build.gradle.kts create mode 100644 services/comment/comment.settings.gradle.kts create mode 100644 services/comment/src/main/resources/comment.yml diff --git a/services/comment/build.gradle.kts b/services/comment/build.gradle.kts new file mode 100644 index 0000000..c0bd3e5 --- /dev/null +++ b/services/comment/build.gradle.kts @@ -0,0 +1,6 @@ +dependencies { + api(project(":comment:api")) + api(project(":comment:application")) + api(project(":comment:rdb-adapter")) + api(project(":comment:webmvc-adapter")) +} \ No newline at end of file diff --git a/services/comment/comment.settings.gradle.kts b/services/comment/comment.settings.gradle.kts new file mode 100644 index 0000000..39ee11a --- /dev/null +++ b/services/comment/comment.settings.gradle.kts @@ -0,0 +1,35 @@ +fun getDirectories(vararg names: String): (String) -> File { + var dir = rootDir + for (name in names) { + dir = dir.resolve(name) + } + return { targetName -> + val directory = dir.walkTopDown().maxDepth(3) + .filter(File::isDirectory) + .associateBy { it.name } + directory[targetName] ?: throw Error("그런 폴더가 없습니다: $targetName") + } +} + +val comment = getDirectories("services", "comment") + +// SERVICE/COMMENT +include( + ":comment", + ":comment:api", + ":comment:api:domain", + ":comment:api:exception", + ":comment:api:readmodel", + ":comment:application", + ":comment:rdb-adapter", + ":comment:webmvc-adapter", +) + +project(":comment").projectDir = comment("comment") +project(":comment:api").projectDir = comment("api") +project(":comment:api:domain").projectDir = comment("domain") +project(":comment:api:exception").projectDir = comment("exception") +project(":comment:api:readmodel").projectDir = comment("readmodel") +project(":comment:application").projectDir = comment("application") +project(":comment:rdb-adapter").projectDir = comment("rdb") +project(":comment:webmvc-adapter").projectDir = comment("web-mvc") diff --git a/services/comment/src/main/resources/comment.yml b/services/comment/src/main/resources/comment.yml new file mode 100644 index 0000000..bf62a73 --- /dev/null +++ b/services/comment/src/main/resources/comment.yml @@ -0,0 +1,5 @@ +spring: + config: + import: + - properties/db/comment.database.yml +# - comment-web.yml \ No newline at end of file From 7efcd7837f0bbda649183b98900f40c3deac8d72 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sun, 11 May 2025 21:24:55 +0900 Subject: [PATCH 08/25] fix: merge conflict resolved in build.gradle.kts --- .github/ISSUE_TEMPLATE/01. main-issue.yml | 4 +- .github/ISSUE_TEMPLATE/03. bug-report.yml | 93 +++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 18 +++- build.gradle.kts | 14 ++- core/core.settings.gradle.kts | 8 +- core/jpa-core/build.gradle.kts | 3 +- .../jpa/support/SnowflakeBaseEntity.java | 16 ++++ .../jpa/support/SnowflakeBaseTimeEntity.java | 21 +++++ .../nettee-snowflake-id-api/build.gradle.kts | 3 + .../constants/SnowflakeConstants.java | 25 +++++ .../InvalidDatacenterIdException.java | 18 ++++ .../exception/InvalidWorkerIdException.java | 18 ++++ .../snowflake/persistence/id/Snowflake.java | 66 +++++++++++++ .../properties/SnowflakeProperties.java | 26 ++++++ .../SnowflakeConstructingValidator.java | 23 +++++ .../build.gradle.kts | 4 + .../annotation/SnowflakeGenerated.java | 16 ++++ .../generator/SnowflakeIdGenerator.java | 20 ++++ gradle.properties | 89 ++++++++++++++++++ monolith/main-runner/build.gradle.kts | 13 ++- .../src/main/resources/application.yml | 3 +- .../properties/persistence/main.snowflake.yml | 3 + .../web}/main.cors.yml | 0 .../nettee/main/sample/entity/Sample.java | 10 ++ .../sample/persistence/SampleRepository.java | 7 ++ .../nettee/main/snowflake/SnowflakeTest.kt | 80 ++++++++++++++++ .../src/test/resources/application.yml | 38 ++++++++ services/board/api/build.gradle.kts | 10 +- services/board/application/build.gradle.kts | 2 +- services/board/board.settings.gradle.kts | 43 +++++---- services/board/build.gradle.kts | 13 ++- services/board/driven/rdb/build.gradle.kts | 4 +- .../board/driving/web-mvc/build.gradle.kts | 4 +- .../nettee/board/web/BoardCommandApi.java | 2 +- 34 files changed, 672 insertions(+), 45 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/03. bug-report.yml create mode 100644 core/jpa-core/src/main/java/nettee/jpa/support/SnowflakeBaseEntity.java create mode 100644 core/jpa-core/src/main/java/nettee/jpa/support/SnowflakeBaseTimeEntity.java create mode 100644 core/snowflake/nettee-snowflake-id-api/build.gradle.kts create mode 100644 core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/constants/SnowflakeConstants.java create mode 100644 core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/exception/InvalidDatacenterIdException.java create mode 100644 core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/exception/InvalidWorkerIdException.java create mode 100644 core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/persistence/id/Snowflake.java create mode 100644 core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/properties/SnowflakeProperties.java create mode 100644 core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/validator/SnowflakeConstructingValidator.java create mode 100644 core/snowflake/nettee-snowflake-id-hibernate/build.gradle.kts create mode 100644 core/snowflake/nettee-snowflake-id-hibernate/src/main/java/nettee/hibenate/annotation/SnowflakeGenerated.java create mode 100644 core/snowflake/nettee-snowflake-id-hibernate/src/main/java/nettee/hibenate/generator/SnowflakeIdGenerator.java create mode 100644 gradle.properties create mode 100644 monolith/main-runner/src/main/resources/properties/persistence/main.snowflake.yml rename monolith/main-runner/src/main/resources/{properties.web => properties/web}/main.cors.yml (100%) create mode 100644 monolith/main-runner/src/test/java/nettee/main/sample/entity/Sample.java create mode 100644 monolith/main-runner/src/test/java/nettee/main/sample/persistence/SampleRepository.java create mode 100644 monolith/main-runner/src/test/kotlin/nettee/main/snowflake/SnowflakeTest.kt create mode 100644 monolith/main-runner/src/test/resources/application.yml diff --git a/.github/ISSUE_TEMPLATE/01. main-issue.yml b/.github/ISSUE_TEMPLATE/01. main-issue.yml index 6f55e66..43a3d7d 100644 --- a/.github/ISSUE_TEMPLATE/01. main-issue.yml +++ b/.github/ISSUE_TEMPLATE/01. main-issue.yml @@ -1,6 +1,6 @@ name: Main Issue description: Describe the task list. -title: "[MAIN] " +title: "☀️ " #labels: [] #projects: [] #assignees: @@ -9,7 +9,7 @@ body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this bug report! + Thanks for taking the time to fill out this issue! # - type: input # id: contact # attributes: diff --git a/.github/ISSUE_TEMPLATE/03. bug-report.yml b/.github/ISSUE_TEMPLATE/03. bug-report.yml new file mode 100644 index 0000000..d445a79 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03. bug-report.yml @@ -0,0 +1,93 @@ +name: Bug Report +description: Describe the bug you encountered. +title: "🐞 " +labels: ["type: bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: dropdown + id: bug-type + attributes: + label: 🐞 Bug Types (Multiple) + description: | + 어떤 종류의 문제인가요? + + - 🐞 오류 (에러, 예외 발생, 논리적 오류 등) + - 🏗 컨벤션·리팩터링 (코드 스타일, 아키텍처 등) + - ⚡️ 성능 (지연, 과도한 자원, 병목 현상 등) + - ✏️ 오타·설명 오류 (문서, 문자열, 주석, 식별자 등) + - 🔧 기타 (빌드, 환경변수 등) + + **❗️ 보안 취약점은 pkw19961027@gmail.com으로 메일 부탁드립니다.** + +
\* 다중 선택 + multiple: true + options: + - 🐞 오류 + - 🏗 컨벤션·리팩터링 + - ⚡️ 성능 + - ✏️ 오타·설명 오류 + - 🔧 기타 + default: 0 + validations: + required: true + - type: input + id: summary + attributes: + label:
💬 Summary of the Bug + description: 버그를 한 문장 정도로 요약해 주세요. + placeholder: ex. 로그인 후 화면이 멈춥니다. + validations: + required: true + - type: textarea + id: steps-to-reproduce + attributes: + label:
🔁 오류 재현 (Reproduction Steps) + description: 버그를 재현하기 위한 구체적인 단계들을 순서대로 적어 주세요. + placeholder: | + 1. + 2. + 3. + + validations: + required: false + - type: textarea + id: logs + attributes: + label:
📝 관련 로그 (Logs) + description: 에러 메시지나 로그가 있다면 복사해서 붙여 주세요. + placeholder: | + Error: Cannot read property 'foo' of undefined + at … + render: console + validations: + required: false + - type: textarea + id: expected-behavior + attributes: + label:
🎯 기대 동작 (Expected Behavior) + description: 정상적으로 동작했을 때 어떤 결과가 나와야 하는지 설명해 주세요. + placeholder: ex. 로그인 후 대시보드 페이지가 보여야 합니다. + validations: + required: true + - type: textarea + id: actual-behavior + attributes: + label:
🔍 실제 동작 (Actual Behavior) + description: 어떤 동작이 나타나는지 설명해 주세요. + placeholder: ex. 로그인 후 버튼이 반응하지 않습니다. + validations: + required: true + - type: textarea + id: additional-note + attributes: + label:
부연 설명 (Additional Notes) + description: 관련 브랜치나 커밋, Active profile, 환경변수 등 기타 참고할 만한 정보가 있으면 적어 주세요. + placeholder: Tell us any other details you think will help us fix this! + value: | + + + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6ce1225..2913818 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,3 @@ -# Pull Request - ## Issues - Resolves #0 @@ -11,12 +9,26 @@ - 주요 변경 사항에 대한 간단한 설명을 작성해 주세요. - 관련 이슈 번호를 포함해 주세요 (예: `#123`). +
+ +## Review Points + + + + +- 상세한 리뷰를 원하는 영역은 작업 의도와 함께 설명해 주세요. + +
+ ## How Has This Been Tested? - 변경 사항을 테스트하는 방법에 대해 설명해 주세요. - 어떤 환경에서 테스트가 이루어졌는지 명시해 주세요. + + +
+ ## Additional Notes - 이 PR과 관련된 추가적인 정보가 있다면 여기에 기재해 주세요. - diff --git a/build.gradle.kts b/build.gradle.kts index dcc14bb..1a3bcbc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,9 +10,6 @@ plugins { } allprojects { - group = "me.nettee" - version = "1.0-SNAPSHOT" - repositories { mavenCentral() } @@ -34,7 +31,18 @@ subprojects { } } + configurations { + compileOnly { + extendsFrom(configurations.annotationProcessor.get()) + } + configureEach { + exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging") + } + } + dependencies { + // FIXME determine the placement of logging library later + implementation("org.springframework.boot:spring-boot-starter-log4j2") if(project.name != "common") { api(project(":common")) } diff --git a/core/core.settings.gradle.kts b/core/core.settings.gradle.kts index 242a7ed..da76ea8 100644 --- a/core/core.settings.gradle.kts +++ b/core/core.settings.gradle.kts @@ -9,10 +9,14 @@ include( ":jpa-core", ":exception-handler-core", ":cors-api", - ":cors-webmvc" + ":cors-webmvc", + ":snowflake-id-api", + ":snowflake-id-hibernate", ) project(":jpa-core").projectDir = core["jpa-core"]!! project(":exception-handler-core").projectDir = core["exception-handler-core"]!! project(":cors-webmvc").projectDir = core["nettee-cors-webmvc"]!! -project(":cors-api").projectDir = core["nettee-cors-api"]!! \ No newline at end of file +project(":cors-api").projectDir = core["nettee-cors-api"]!! +project(":snowflake-id-api").projectDir = core["nettee-snowflake-id-api"]!! +project(":snowflake-id-hibernate").projectDir = core["nettee-snowflake-id-hibernate"]!! \ No newline at end of file diff --git a/core/jpa-core/build.gradle.kts b/core/jpa-core/build.gradle.kts index 4af48f6..db413e1 100644 --- a/core/jpa-core/build.gradle.kts +++ b/core/jpa-core/build.gradle.kts @@ -1,5 +1,6 @@ dependencies { - implementation("org.springframework.boot:spring-boot-starter-data-jpa") + api(project(":snowflake-id-hibernate")) + api("org.springframework.boot:spring-boot-starter-data-jpa") // querydsl implementation("com.querydsl:querydsl-jpa:${dependencyManagement.importedProperties["querydsl.version"]}:jakarta") diff --git a/core/jpa-core/src/main/java/nettee/jpa/support/SnowflakeBaseEntity.java b/core/jpa-core/src/main/java/nettee/jpa/support/SnowflakeBaseEntity.java new file mode 100644 index 0000000..a23b765 --- /dev/null +++ b/core/jpa-core/src/main/java/nettee/jpa/support/SnowflakeBaseEntity.java @@ -0,0 +1,16 @@ +package nettee.jpa.support; + +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; +import nettee.hibenate.annotation.SnowflakeGenerated; + +import java.io.Serializable; + +@Getter +@MappedSuperclass +public abstract class SnowflakeBaseEntity implements Serializable { + @Id + @SnowflakeGenerated + private Long id; +} diff --git a/core/jpa-core/src/main/java/nettee/jpa/support/SnowflakeBaseTimeEntity.java b/core/jpa-core/src/main/java/nettee/jpa/support/SnowflakeBaseTimeEntity.java new file mode 100644 index 0000000..150c6aa --- /dev/null +++ b/core/jpa-core/src/main/java/nettee/jpa/support/SnowflakeBaseTimeEntity.java @@ -0,0 +1,21 @@ +package nettee.jpa.support; + +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.Instant; + +@Getter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public class SnowflakeBaseTimeEntity extends SnowflakeBaseEntity { + @CreatedDate + private Instant createdAt; + + @LastModifiedDate + private Instant updatedAt; +} diff --git a/core/snowflake/nettee-snowflake-id-api/build.gradle.kts b/core/snowflake/nettee-snowflake-id-api/build.gradle.kts new file mode 100644 index 0000000..1355541 --- /dev/null +++ b/core/snowflake/nettee-snowflake-id-api/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + compileOnly("org.springframework.boot:spring-boot-autoconfigure:3.4.3") +} \ No newline at end of file diff --git a/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/constants/SnowflakeConstants.java b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/constants/SnowflakeConstants.java new file mode 100644 index 0000000..6c06c2b --- /dev/null +++ b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/constants/SnowflakeConstants.java @@ -0,0 +1,25 @@ +package nettee.snowflake.constants; + +public final class SnowflakeConstants { + // 한국 시간(KST): 2025-03-26 23:40:00 기준점 + public static final long NETTEE_EPOCH = 1_743_000_000_000L; + public static final String PREFIX = "nettee.persistence.snowflake"; + + private SnowflakeConstants() {} + + public static final class SnowflakeDefault { + public static final int WORKER_ID_BIT_SIZE = 5; + public static final int DATACENTER_ID_BIT_SIZE = 5; + public static final int SEQUENCE_BIT_SIZE = 12; + + public static final int WORKER_ID_SHIFT = SEQUENCE_BIT_SIZE; + public static final int DATACENTER_ID_SHIFT = SEQUENCE_BIT_SIZE + WORKER_ID_BIT_SIZE; + public static final int TIMESTAMP_LEFT_SHIFT = SEQUENCE_BIT_SIZE + WORKER_ID_BIT_SIZE + DATACENTER_ID_BIT_SIZE; + + public static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BIT_SIZE); + public static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BIT_SIZE); + public static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BIT_SIZE); + + private SnowflakeDefault() {} + } +} diff --git a/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/exception/InvalidDatacenterIdException.java b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/exception/InvalidDatacenterIdException.java new file mode 100644 index 0000000..2877234 --- /dev/null +++ b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/exception/InvalidDatacenterIdException.java @@ -0,0 +1,18 @@ +package nettee.snowflake.exception; + +import static nettee.snowflake.constants.SnowflakeConstants.SnowflakeDefault.MAX_DATACENTER_ID; + +public class InvalidDatacenterIdException extends RuntimeException { + + private final long datacenterId; + + public InvalidDatacenterIdException(long datacenterId) { + super("Datacenter ID can't be greater than %d or less than 0. Input: %d" + .formatted(MAX_DATACENTER_ID, datacenterId)); + this.datacenterId = datacenterId; + } + + public long getDatacenterId() { + return datacenterId; + } +} diff --git a/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/exception/InvalidWorkerIdException.java b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/exception/InvalidWorkerIdException.java new file mode 100644 index 0000000..68590f8 --- /dev/null +++ b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/exception/InvalidWorkerIdException.java @@ -0,0 +1,18 @@ +package nettee.snowflake.exception; + +import static nettee.snowflake.constants.SnowflakeConstants.SnowflakeDefault.MAX_WORKER_ID; + +public class InvalidWorkerIdException extends RuntimeException{ + + private final long workerId; + + public InvalidWorkerIdException(final long workerId) { + super("Worker ID can't be greater than %d or less than 0. Input: %d" + .formatted(MAX_WORKER_ID, workerId)); + this.workerId = workerId; + } + + public long getWorkerId() { + return workerId; + } +} diff --git a/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/persistence/id/Snowflake.java b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/persistence/id/Snowflake.java new file mode 100644 index 0000000..e7e58f4 --- /dev/null +++ b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/persistence/id/Snowflake.java @@ -0,0 +1,66 @@ +package nettee.snowflake.persistence.id; + +import nettee.snowflake.properties.SnowflakeProperties; +import nettee.snowflake.validator.SnowflakeConstructingValidator; + +import static nettee.snowflake.constants.SnowflakeConstants.NETTEE_EPOCH; +import static nettee.snowflake.constants.SnowflakeConstants.SnowflakeDefault.*; + +public class Snowflake { + private final long datacenterId; + private final long workerId; + private final long epoch; + + private long sequence = 0L; + private long lastTimestamp = -1L; + + public Snowflake(SnowflakeProperties properties) { + this(properties.datacenterId(), properties.workerId(), properties.epoch()); + } + + public Snowflake(long datacenterId, long workerId, long epoch) { + SnowflakeConstructingValidator.validateDatacenterId(datacenterId); + SnowflakeConstructingValidator.validateWorkerId(workerId); + + this.workerId = workerId; + this.datacenterId = datacenterId; + this.epoch = epoch >= 0 ? epoch : NETTEE_EPOCH; + } + + public synchronized long nextId() { + long timestamp = timeGen(); + + if (timestamp < lastTimestamp) { + throw new RuntimeException(String.format( + "Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp + )); + } + + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & SEQUENCE_MASK; + if (sequence == 0) { + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0L; + } + + lastTimestamp = timestamp; + return ((timestamp - epoch) << TIMESTAMP_LEFT_SHIFT) | + (datacenterId << DATACENTER_ID_SHIFT) | + (workerId << WORKER_ID_SHIFT) | + sequence; + } + + private long tilNextMillis(long lastTimestamp) { + long timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + private long timeGen() { + return System.currentTimeMillis(); + } +} diff --git a/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/properties/SnowflakeProperties.java b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/properties/SnowflakeProperties.java new file mode 100644 index 0000000..1d65f9a --- /dev/null +++ b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/properties/SnowflakeProperties.java @@ -0,0 +1,26 @@ +package nettee.snowflake.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.Objects; + +import static nettee.snowflake.constants.SnowflakeConstants.NETTEE_EPOCH; +import static nettee.snowflake.constants.SnowflakeConstants.PREFIX; + +@ConfigurationProperties(PREFIX) +public record SnowflakeProperties( + Long datacenterId, + Long workerId, + Long epoch +) { + public SnowflakeProperties { + Objects.requireNonNull(datacenterId, PREFIX + ".datacenter-id must not be null."); + Objects.requireNonNull(workerId, PREFIX + ".worker-id must not be null."); + + if (epoch == null) { + epoch = NETTEE_EPOCH; + } else if (epoch < 0) { + epoch = NETTEE_EPOCH; + } + } +} diff --git a/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/validator/SnowflakeConstructingValidator.java b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/validator/SnowflakeConstructingValidator.java new file mode 100644 index 0000000..1d76114 --- /dev/null +++ b/core/snowflake/nettee-snowflake-id-api/src/main/java/nettee/snowflake/validator/SnowflakeConstructingValidator.java @@ -0,0 +1,23 @@ +package nettee.snowflake.validator; + +import nettee.snowflake.exception.InvalidDatacenterIdException; +import nettee.snowflake.exception.InvalidWorkerIdException; + +import static nettee.snowflake.constants.SnowflakeConstants.SnowflakeDefault.MAX_DATACENTER_ID; +import static nettee.snowflake.constants.SnowflakeConstants.SnowflakeDefault.MAX_WORKER_ID; + +public class SnowflakeConstructingValidator { + private SnowflakeConstructingValidator() {} + + public static void validateDatacenterId(long datacenterId) { + if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) { + throw new InvalidDatacenterIdException(datacenterId); + } + } + + public static void validateWorkerId(long workerId) { + if (workerId > MAX_WORKER_ID || workerId < 0) { + throw new InvalidWorkerIdException(workerId); + } + } +} diff --git a/core/snowflake/nettee-snowflake-id-hibernate/build.gradle.kts b/core/snowflake/nettee-snowflake-id-hibernate/build.gradle.kts new file mode 100644 index 0000000..7f91fd0 --- /dev/null +++ b/core/snowflake/nettee-snowflake-id-hibernate/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies{ + api(project(":snowflake-id-api")) + compileOnly("org.hibernate.orm:hibernate-core") +} \ No newline at end of file diff --git a/core/snowflake/nettee-snowflake-id-hibernate/src/main/java/nettee/hibenate/annotation/SnowflakeGenerated.java b/core/snowflake/nettee-snowflake-id-hibernate/src/main/java/nettee/hibenate/annotation/SnowflakeGenerated.java new file mode 100644 index 0000000..b2cb4df --- /dev/null +++ b/core/snowflake/nettee-snowflake-id-hibernate/src/main/java/nettee/hibenate/annotation/SnowflakeGenerated.java @@ -0,0 +1,16 @@ +package nettee.hibenate.annotation; + +import nettee.hibenate.generator.SnowflakeIdGenerator; +import org.hibernate.annotations.IdGeneratorType; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention(RUNTIME) +@Target(FIELD) +@IdGeneratorType(SnowflakeIdGenerator.class) +public @interface SnowflakeGenerated { +} diff --git a/core/snowflake/nettee-snowflake-id-hibernate/src/main/java/nettee/hibenate/generator/SnowflakeIdGenerator.java b/core/snowflake/nettee-snowflake-id-hibernate/src/main/java/nettee/hibenate/generator/SnowflakeIdGenerator.java new file mode 100644 index 0000000..44af57f --- /dev/null +++ b/core/snowflake/nettee-snowflake-id-hibernate/src/main/java/nettee/hibenate/generator/SnowflakeIdGenerator.java @@ -0,0 +1,20 @@ +package nettee.hibenate.generator; + +import nettee.snowflake.persistence.id.Snowflake; +import nettee.snowflake.properties.SnowflakeProperties; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.id.IdentifierGenerator; + +public class SnowflakeIdGenerator implements IdentifierGenerator { + + private final Snowflake snowflake; + + public SnowflakeIdGenerator(SnowflakeProperties snowflakeProperties) { + this.snowflake = new Snowflake(snowflakeProperties); + } + + @Override + public Long generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) { + return snowflake.nextId(); + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..1fd9672 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,89 @@ +# ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ +# ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜🟦🟦🟦⬜ +# ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜🟦🟦🟦🟦 +# ⬜⬜⬜⬜🟦🟦🟦🟦🟦🟦⬜⬜⬜⬜🟦🟦 +# ⬜⬜⬜🟦🟦🟦🟦🟦🟦🟦🟦🟦⬜⬜🟦🟦 Gradle properties are classified with 3 categories. +# ⬜⬜🟦⬜🟦🟦🟦🟦🟦🟦⬜🟦🟦🟦🟦🟦 +# ⬜🟦🟦🟦⬜🟦⬜🟦🟦🟦🟦🟦🟦🟦🟦⬜ 1. System prop +# ⬜🟦🟦🟦🟦⬜🟦🟦🟦🟦🟦🟦🟦🟦🟦⬜ 2. Gradle config prop +# 🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦⬜⬜⬜ 3. Project prop +# 🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦⬜⬜⬜⬜ +# 🟦🟦🟦⬜🟦🟦🟦🟦⬜🟦🟦🟦⬜⬜⬜⬜ +# 🟦🟦⬜⬜⬜🟦🟦⬜⬜⬜🟦🟦⬜⬜⬜⬜ +# ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ + +## ◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢ +## System Properties +## ◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢ + +# 내장된 시스템 관련 변수를 수정할 수 있습니다. (드물게 사용되며, 일반적으로 생략할 수 있습니다.) + +# https.protocols=TLSv1.2,TLSv1.3 +# http.proxyHost=www.somehost.org +# http.proxyPort=8080 +# http.proxyUser=userid +# http.proxyPassword=password +# https.proxyHost=... +# https.proxyPort=... +# https.proxyUser=... +# https.proxyPassword=... +# http.nonProxyHosts=*.nonproxyrepos.com|localhost + +## ◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢ +## Gradle Configuration Properties +## ◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢ + +org.gradle.parallel=true +org.gradle.caching=true + +## ◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢ +## Project Common Properties +## ◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢ + +# NOTE project.group에 이 내용이 담기기 때문에 allprojects { group = "..." } 표기를 생략할 수 있습니다. +group=me.nettee +version=0.1.0 + +## ◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢ +## Service Modules +## ◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢ + +# article +article=:article +articleApi=:article:article-api +articleDomain=:article:article-api:article-domain +articleException=:article:article-api:article-exception +articleReadModel=:article:article-api:article-readmodel +articleApplication=:article:article-application +articleRdbAdapter=:article:article-rdb-adapter +articleWebMvcAdapter=:article:article-webmvc + +# board +board=:board +boardApi=:board:board-api +boardDomain=:board:board-api:board-domain +boardException=:board:board-api:board-exception +boardReadModel=:board:board-api:board-readmodel +boardApplication=:board:board-application +boardRdbAdapter=:board:board-rdb-adapter +boardWebMvcAdapter=:board:board-webmvc + +# comment +comment=:comment +commentApi=:comment:comment-api +commentDomain=:comment:comment-api:comment-domain +commentException=:comment:comment-api:comment-exception +commentReadModel=:comment:comment-api:comment-readmodel +commentApplication=:comment:comment-application +commentRdbAdapter=:comment:comment-rdb-adapter +commentWebMvcAdapter=:comment:comment-webmvc + +# views +views=:views +viewsApi=:views:views-api +viewsDomain=:views:views-api:views-domain +viewsException=:views:views-api:views-exception +viewsReadModel=:views:views-api:views-readmodel +viewsApplication=:views:views-application +viewsRdbAdapter=:views:views-rdb-adapter +viewsWebMvcAdapter=:views:views-webmvc diff --git a/monolith/main-runner/build.gradle.kts b/monolith/main-runner/build.gradle.kts index f976217..d6c86f4 100644 --- a/monolith/main-runner/build.gradle.kts +++ b/monolith/main-runner/build.gradle.kts @@ -1,5 +1,7 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar +val board: String by project + version = "0.0.1-SNAPSHOT" dependencies { @@ -7,16 +9,21 @@ dependencies { implementation(project(":exception-handler-core")) implementation(project(":jpa-core")) implementation(project(":cors-webmvc")) - // service - implementation(project(":board")) - implementation(project(":comment")) + + // services + implementation(project(board)) + // webmvc implementation("org.springframework.boot:spring-boot-starter-web") + // db runtimeOnly("org.postgresql:postgresql:42.7.4") + // flyway implementation("org.flywaydb:flyway-database-postgresql") + // test + testImplementation("com.h2database:h2") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("com.fasterxml.jackson.core:jackson-databind") testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin") diff --git a/monolith/main-runner/src/main/resources/application.yml b/monolith/main-runner/src/main/resources/application.yml index d0c7c58..66934f0 100644 --- a/monolith/main-runner/src/main/resources/application.yml +++ b/monolith/main-runner/src/main/resources/application.yml @@ -4,7 +4,8 @@ spring: config: import: - - properties.web/main.cors.yml + - properties/web/main.cors.yml + - properties/persistence/main.snowflake.yml - board.yml - comment.yml diff --git a/monolith/main-runner/src/main/resources/properties/persistence/main.snowflake.yml b/monolith/main-runner/src/main/resources/properties/persistence/main.snowflake.yml new file mode 100644 index 0000000..b097f61 --- /dev/null +++ b/monolith/main-runner/src/main/resources/properties/persistence/main.snowflake.yml @@ -0,0 +1,3 @@ +nettee.persistence.snowflake: + datacenter-id: ${SNOWFLAKE_DC_ID:0} + worker-id: ${SNOWFLAKE_WORKER_ID:0} \ No newline at end of file diff --git a/monolith/main-runner/src/main/resources/properties.web/main.cors.yml b/monolith/main-runner/src/main/resources/properties/web/main.cors.yml similarity index 100% rename from monolith/main-runner/src/main/resources/properties.web/main.cors.yml rename to monolith/main-runner/src/main/resources/properties/web/main.cors.yml diff --git a/monolith/main-runner/src/test/java/nettee/main/sample/entity/Sample.java b/monolith/main-runner/src/test/java/nettee/main/sample/entity/Sample.java new file mode 100644 index 0000000..f5d4cdf --- /dev/null +++ b/monolith/main-runner/src/test/java/nettee/main/sample/entity/Sample.java @@ -0,0 +1,10 @@ +package nettee.main.sample.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import nettee.jpa.support.SnowflakeBaseEntity; + +@Entity +@Table(name = "sample") +public class Sample extends SnowflakeBaseEntity { +} diff --git a/monolith/main-runner/src/test/java/nettee/main/sample/persistence/SampleRepository.java b/monolith/main-runner/src/test/java/nettee/main/sample/persistence/SampleRepository.java new file mode 100644 index 0000000..2accad8 --- /dev/null +++ b/monolith/main-runner/src/test/java/nettee/main/sample/persistence/SampleRepository.java @@ -0,0 +1,7 @@ +package nettee.main.sample.persistence; + +import nettee.main.sample.entity.Sample; +import org.springframework.data.repository.CrudRepository; + +public interface SampleRepository extends CrudRepository { +} \ No newline at end of file diff --git a/monolith/main-runner/src/test/kotlin/nettee/main/snowflake/SnowflakeTest.kt b/monolith/main-runner/src/test/kotlin/nettee/main/snowflake/SnowflakeTest.kt new file mode 100644 index 0000000..52b6ae5 --- /dev/null +++ b/monolith/main-runner/src/test/kotlin/nettee/main/snowflake/SnowflakeTest.kt @@ -0,0 +1,80 @@ +package nettee.main.snowflake + +import io.kotest.core.spec.style.FreeSpec +import io.kotest.extensions.spring.SpringExtension +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.longs.shouldBeGreaterThan +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import kotlinx.coroutines.* +import nettee.main.sample.entity.Sample +import nettee.main.sample.persistence.SampleRepository +import nettee.snowflake.properties.SnowflakeProperties +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.Bean +import java.util.concurrent.ConcurrentHashMap + +@DataJpaTest +class SnowflakeTest( + @Autowired val repository: SampleRepository +) : FreeSpec({ + + "[POST] Snowflake ID 채번 정책" - { + "Snowflake ID가 생성이 됐을 때" - { + val sample = repository.save(Sample()) + + "Snowflake ID 정상적으로 생성된다" { + sample.id shouldNotBe null + } + + "Snowflake ID가 양수이다" { + sample.id shouldBeGreaterThan 0L + } + + "Snowflake ID의 크기는 부호 비트를 제외한 63bit 이내를 충족한다" { + // 63번째 비트가 꺼져 있는지(0) 확인하여, Snowflake ID가 63비트 이내(양수 범위)임을 보장한다. + // 0 and 1 => 0 이고 1 and 1 -> 1 + sample.id and (1L.shl(63)) shouldBe 0L + } + } + } + + "[POST] Snowflake ID 동시성 테스트" - { + "[병렬 처리] 100개의 Snowflake ID의 동시 생성 요청" - { + // Set을 이용하여 키를 저장하고 중복없는 지를 판단 + val concurrentSet = ConcurrentHashMap.newKeySet() + + // 코루틴 실행 + val coroutineScope = CoroutineScope(Dispatchers.Default) + + val jobs = List(100) { + // 병렬 작업 생성 + coroutineScope.launch { + // 새로운 sample 엔티티 저장 요청 + val sample = repository.save(Sample()) + println("Saved id=${sample.id}, time=${System.currentTimeMillis()}") + concurrentSet.add(sample.id) + } + } + + jobs.joinAll() + + "ID 중복이 없어야 한다" { + concurrentSet shouldHaveSize 100 + } + } + } +}) { + override fun extensions() = listOf(SpringExtension) + + @TestConfiguration + class SnowflakeTestConfig { + + @Bean + fun snowflakeProperties(): SnowflakeProperties { + return SnowflakeProperties(1L, 1L, null) + } + } +} \ No newline at end of file diff --git a/monolith/main-runner/src/test/resources/application.yml b/monolith/main-runner/src/test/resources/application.yml new file mode 100644 index 0000000..edd2dbb --- /dev/null +++ b/monolith/main-runner/src/test/resources/application.yml @@ -0,0 +1,38 @@ +spring: + application.name: multi-module + h2: + console: + enabled: true + path: /h2-console + datasource: + driver-class-name: org.h2.Driver + # https://www.h2database.com/html/features.html#in_memory_databases 참조 + url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE; + username: sa + password: + jpa: + generate-ddl: 'true' + hibernate: + ddl-auto: create + properties: + hibernate: + show_sql: true + format_sql: true + use_sql_comments: true + flyway: + baseline-on-migrate: false + enabled: false + +app.cors.endpoints: + - path: "/**" + allowed: + headers: "*" + methods: "*" + origins: + - http://localhost:3000 + - https://localhost:3000 + credentials: true + exposed: + headers: "*" + max-age: 3_600 + diff --git a/services/board/api/build.gradle.kts b/services/board/api/build.gradle.kts index 0dcf7c2..1f79251 100644 --- a/services/board/api/build.gradle.kts +++ b/services/board/api/build.gradle.kts @@ -1,5 +1,9 @@ +val boardDomain: String by project +val boardException: String by project +val boardReadModel: String by project + dependencies { - api(project(":board:api:domain")) - api(project(":board:api:exception")) - api(project(":board:api:readmodel")) + api(project(boardDomain)) + api(project(boardException)) + api(project(boardReadModel)) } \ No newline at end of file diff --git a/services/board/application/build.gradle.kts b/services/board/application/build.gradle.kts index dd0d3a4..0d3814b 100644 --- a/services/board/application/build.gradle.kts +++ b/services/board/application/build.gradle.kts @@ -1,3 +1,3 @@ dependencies { - api(project(":board:api")) + api(project(":board:board-api")) } \ No newline at end of file diff --git a/services/board/board.settings.gradle.kts b/services/board/board.settings.gradle.kts index 46f080a..9550d88 100644 --- a/services/board/board.settings.gradle.kts +++ b/services/board/board.settings.gradle.kts @@ -1,3 +1,12 @@ +val board: String by settings +val boardApi: String by settings +val boardDomain: String by settings +val boardException: String by settings +val boardReadModel: String by settings +val boardApplication: String by settings +val boardRdbAdapter: String by settings +val boardWebMvcAdapter: String by settings + fun getDirectories(vararg names: String): (String) -> File { var dir = rootDir for (name in names) { @@ -11,25 +20,25 @@ fun getDirectories(vararg names: String): (String) -> File { } } -val board = getDirectories("services", "board") +val boardDirectory = getDirectories("services", "board") // SERVICE/BOARD include( - ":board", - ":board:api", - ":board:api:domain", - ":board:api:exception", - ":board:api:readmodel", - ":board:application", - ":board:rdb-adapter", - ":board:webmvc-adapter", + board, + boardApi, + boardDomain, + boardException, + boardReadModel, + boardApplication, + boardRdbAdapter, + boardWebMvcAdapter, ) -project(":board").projectDir = board("board") -project(":board:api").projectDir = board("api") -project(":board:api:domain").projectDir = board("domain") -project(":board:api:exception").projectDir = board("exception") -project(":board:api:readmodel").projectDir = board("readmodel") -project(":board:application").projectDir = board("application") -project(":board:rdb-adapter").projectDir = board("rdb") -project(":board:webmvc-adapter").projectDir = board("web-mvc") +project(board).projectDir = boardDirectory("board") +project(boardApi).projectDir = boardDirectory("api") +project(boardDomain).projectDir = boardDirectory("domain") +project(boardException).projectDir = boardDirectory("exception") +project(boardReadModel).projectDir = boardDirectory("readmodel") +project(boardApplication).projectDir = boardDirectory("application") +project(boardRdbAdapter).projectDir = boardDirectory("rdb") +project(boardWebMvcAdapter).projectDir = boardDirectory("web-mvc") diff --git a/services/board/build.gradle.kts b/services/board/build.gradle.kts index 3565f94..5f9bb4b 100644 --- a/services/board/build.gradle.kts +++ b/services/board/build.gradle.kts @@ -1,6 +1,11 @@ +val boardApi: String by project +val boardApplication: String by project +val boardRdbAdapter: String by project +val boardWebMvcAdapter: String by project + dependencies { - api(project(":board:api")) - api(project(":board:application")) - api(project(":board:rdb-adapter")) - api(project(":board:webmvc-adapter")) + api(project(boardApi)) + api(project(boardApplication)) + api(project(boardRdbAdapter)) + api(project(boardWebMvcAdapter)) } \ No newline at end of file diff --git a/services/board/driven/rdb/build.gradle.kts b/services/board/driven/rdb/build.gradle.kts index 9617183..34e30c1 100644 --- a/services/board/driven/rdb/build.gradle.kts +++ b/services/board/driven/rdb/build.gradle.kts @@ -1,8 +1,8 @@ dependencies { val bom = dependencyManagement.importedProperties - api(project(":board:api")) - api(project(":board:application")) + api(project(":board:board-api")) + api(project(":board:board-application")) api(project(":jpa-core")) // spring diff --git a/services/board/driving/web-mvc/build.gradle.kts b/services/board/driving/web-mvc/build.gradle.kts index 1f453c7..19a4629 100644 --- a/services/board/driving/web-mvc/build.gradle.kts +++ b/services/board/driving/web-mvc/build.gradle.kts @@ -1,6 +1,6 @@ dependencies { - api(project(":board:api")) - api(project(":board:application")) + api(project(":board:board-api")) + api(project(":board:board-application")) // validation compileOnly("jakarta.validation:jakarta.validation-api") diff --git a/services/board/driving/web-mvc/src/main/java/nettee/board/web/BoardCommandApi.java b/services/board/driving/web-mvc/src/main/java/nettee/board/web/BoardCommandApi.java index 8e5e54b..2f26934 100644 --- a/services/board/driving/web-mvc/src/main/java/nettee/board/web/BoardCommandApi.java +++ b/services/board/driving/web-mvc/src/main/java/nettee/board/web/BoardCommandApi.java @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("boards") +@RequestMapping("/boards") @RequiredArgsConstructor public class BoardCommandApi { private final BoardCreateUseCase boardCreateUseCase; From 22bb9f33a3288af0fb987b5923e66180cb1d99c8 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sun, 11 May 2025 22:09:09 +0900 Subject: [PATCH 09/25] fix(comment/reply): rename postgresql files --- .../migration/v1_0/V1_0_0__enable_uuid.sql | 1 - .../v1_0/V1_0_1__create_tb_reply.sql | 20 ------------------- ...ment.sql => V1_0_3__create_tb_comment.sql} | 2 +- .../v1_0/V1_0_4__create_tb_reply.sql | 20 +++++++++++++++++++ ..._0_5__alter_comment_status_as_integer.sql} | 0 ...V1_0_6__alter_reply_status_as_integer.sql} | 0 6 files changed, 21 insertions(+), 22 deletions(-) delete mode 100644 services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_0__enable_uuid.sql delete mode 100644 services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_reply.sql rename services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/{V1_0_1__create_tb_comment.sql => V1_0_3__create_tb_comment.sql} (94%) create mode 100644 services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_4__create_tb_reply.sql rename services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/{V1_0_2__alter_comment_status_as_integer.sql => V1_0_5__alter_comment_status_as_integer.sql} (100%) rename services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/{V1_0_2__alter_reply_status_as_integer.sql => V1_0_6__alter_reply_status_as_integer.sql} (100%) diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_0__enable_uuid.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_0__enable_uuid.sql deleted file mode 100644 index 682131d..0000000 --- a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_0__enable_uuid.sql +++ /dev/null @@ -1 +0,0 @@ -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_reply.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_reply.sql deleted file mode 100644 index 095c57c..0000000 --- a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_reply.sql +++ /dev/null @@ -1,20 +0,0 @@ -CREATE TABLE IF NOT EXISTS reply ( - id BIGSERIAL, - parent_id BIGSERIAL, - content VARCHAR(255) - status VARCHAR(255), - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP, - - CONSTRAINT pk_comment PRIMARY KEY (id) -); - ---테이블 코멘트 -COMMENT ON TABLE comment IS '답글'; - --- 컬럼 코멘트 -COMMENT ON COLUMN comment.parent_id IS '부모 댓글 ID'; -COMMENT ON COLUMN comment.content IS '내용'; -COMMENT ON COLUMN comment.status IS '상태'; -COMMENT ON COLUMN comment.created_at IS '생성시간'; -COMMENT ON COLUMN comment.updated_at IS '마지막 수정시간'; \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_comment.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_3__create_tb_comment.sql similarity index 94% rename from services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_comment.sql rename to services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_3__create_tb_comment.sql index a5d9879..677013d 100644 --- a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_1__create_tb_comment.sql +++ b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_3__create_tb_comment.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS comment ( id BIGSERIAL, - content VARCHAR(255) + content VARCHAR(255), status VARCHAR(255), created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP, diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_4__create_tb_reply.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_4__create_tb_reply.sql new file mode 100644 index 0000000..7261726 --- /dev/null +++ b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_4__create_tb_reply.sql @@ -0,0 +1,20 @@ +CREATE TABLE IF NOT EXISTS reply ( + id BIGSERIAL, + parent_id BIGSERIAL, + content VARCHAR(255), + status VARCHAR(255), + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP, + + CONSTRAINT pk_reply PRIMARY KEY (id) +); + +--테이블 코멘트 +COMMENT ON TABLE reply IS '답글'; + +-- 컬럼 코멘트 +COMMENT ON COLUMN reply.parent_id IS '부모 댓글 ID'; +COMMENT ON COLUMN reply.content IS '내용'; +COMMENT ON COLUMN reply.status IS '상태'; +COMMENT ON COLUMN reply.created_at IS '생성시간'; +COMMENT ON COLUMN reply.updated_at IS '마지막 수정시간'; \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_comment_status_as_integer.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_5__alter_comment_status_as_integer.sql similarity index 100% rename from services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_comment_status_as_integer.sql rename to services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_5__alter_comment_status_as_integer.sql diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_reply_status_as_integer.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_6__alter_reply_status_as_integer.sql similarity index 100% rename from services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_2__alter_reply_status_as_integer.sql rename to services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_6__alter_reply_status_as_integer.sql From 734e71cfa55fc442ea871f49c148f938f26efd94 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sun, 11 May 2025 22:11:20 +0900 Subject: [PATCH 10/25] fix(comment/reply): fix typo/syntax in entity --- .../nettee/comment/entity/CommentEntity.java | 16 ++++++++++------ .../java/nettee/reply/entity/ReplyEntity.java | 18 +++++++++++------- .../mapper/CommentEntityMapper.java | 14 -------------- .../persistence/mapper/ReplyEntityMapper.java | 14 ++++++++++++++ 4 files changed, 35 insertions(+), 27 deletions(-) delete mode 100644 services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/CommentEntityMapper.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java index 834eb8f..8368ce4 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java @@ -2,11 +2,12 @@ import jakarta.persistence.Convert; import jakarta.persistence.Entity; -import jakarta.persistence.Id; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import nettee.comment.entity.type.CommentEntityStatus; +import nettee.comment.entity.type.CommentEntityStatusConverter; import nettee.jpa.support.LongBaseTimeEntity; import org.hibernate.annotations.DynamicUpdate; @@ -16,11 +17,14 @@ @Entity(name = "comment") public class CommentEntity extends LongBaseTimeEntity { - @Id - private Long id; + public String content; - private String content; + @Convert(converter = CommentEntityStatusConverter.class) + public CommentEntityStatus status; - @Convert - private CommentEntityStatus status; + @Builder + public CommentEntity(String content, CommentEntityStatus status) { + this.content = content; + this.status = status; + } } diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java index 08c6cf9..15f873c 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java @@ -2,25 +2,29 @@ import jakarta.persistence.Convert; import jakarta.persistence.Entity; -import jakarta.persistence.Id; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import nettee.jpa.support.LongBaseTimeEntity; import nettee.reply.entity.type.ReplyEntityStatus; +import nettee.reply.entity.type.ReplyEntityStatusConverter; import org.hibernate.annotations.DynamicUpdate; @Getter @DynamicUpdate @NoArgsConstructor(access = AccessLevel.PROTECTED) -@Entity(name = "comment") +@Entity(name = "reply") public class ReplyEntity extends LongBaseTimeEntity { - @Id - private Long id; + public String content; - private String content; + @Convert(converter = ReplyEntityStatusConverter.class) + public ReplyEntityStatus status; - @Convert - private ReplyEntityStatus status; + @Builder + public ReplyEntity(String content, ReplyEntityStatus status) { + this.content = content; + this.status = status; + } } diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/CommentEntityMapper.java b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/CommentEntityMapper.java deleted file mode 100644 index a555a6e..0000000 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/CommentEntityMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package nettee.reply.persistence.mapper; - -import nettee.comment.Comment; -import nettee.reply.entity.ReplyEntity; -import org.mapstruct.Mapper; - -@Mapper(componentModel = "spring") -public interface CommentEntityMapper { - - Comment toDomain(ReplyEntity commentEntity); - - ReplyEntity toEntity(Comment comment); - -} \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java new file mode 100644 index 0000000..532349c --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java @@ -0,0 +1,14 @@ +package nettee.reply.persistence.mapper; + +import nettee.reply.Reply; +import nettee.reply.entity.ReplyEntity; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface ReplyEntityMapper { + + Reply toDomain(ReplyEntity replyEntity); + + ReplyEntity toEntity(Reply reply); + +} \ No newline at end of file From caf146d3aa3e71cacd7372187ffc9b7cd4285ab4 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sun, 11 May 2025 22:13:45 +0900 Subject: [PATCH 11/25] build(comment/reply): modify build.gradle.kts, settings.gradle.kts --- monolith/main-runner/build.gradle.kts | 13 ++---- services/comment/api/build.gradle.kts | 10 +++-- .../comment/api/readmodel/build.gradle.kts | 4 +- services/comment/application/build.gradle.kts | 2 +- services/comment/build.gradle.kts | 13 ++++-- services/comment/comment.settings.gradle.kts | 44 ++++++++++++------- services/comment/driven/rdb/build.gradle.kts | 4 +- 7 files changed, 52 insertions(+), 38 deletions(-) diff --git a/monolith/main-runner/build.gradle.kts b/monolith/main-runner/build.gradle.kts index d6c86f4..f976217 100644 --- a/monolith/main-runner/build.gradle.kts +++ b/monolith/main-runner/build.gradle.kts @@ -1,7 +1,5 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar -val board: String by project - version = "0.0.1-SNAPSHOT" dependencies { @@ -9,21 +7,16 @@ dependencies { implementation(project(":exception-handler-core")) implementation(project(":jpa-core")) implementation(project(":cors-webmvc")) - - // services - implementation(project(board)) - + // service + implementation(project(":board")) + implementation(project(":comment")) // webmvc implementation("org.springframework.boot:spring-boot-starter-web") - // db runtimeOnly("org.postgresql:postgresql:42.7.4") - // flyway implementation("org.flywaydb:flyway-database-postgresql") - // test - testImplementation("com.h2database:h2") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("com.fasterxml.jackson.core:jackson-databind") testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin") diff --git a/services/comment/api/build.gradle.kts b/services/comment/api/build.gradle.kts index c6638c4..f334790 100644 --- a/services/comment/api/build.gradle.kts +++ b/services/comment/api/build.gradle.kts @@ -1,5 +1,9 @@ +val commentDomain: String by project +val commentException: String by project +val commentReadModel: String by project + dependencies { - api(project(":comment:api:domain")) - api(project(":comment:api:exception")) - api(project(":comment:api:readmodel")) + api(project(commentDomain)) + api(project(commentException)) + api(project(commentReadModel)) } \ No newline at end of file diff --git a/services/comment/api/readmodel/build.gradle.kts b/services/comment/api/readmodel/build.gradle.kts index 6c95b71..ddfbece 100644 --- a/services/comment/api/readmodel/build.gradle.kts +++ b/services/comment/api/readmodel/build.gradle.kts @@ -1,3 +1,5 @@ +val commentDomain: String by project + dependencies { - api(project(":comment:api:domain")) + api(project(commentDomain)) } \ No newline at end of file diff --git a/services/comment/application/build.gradle.kts b/services/comment/application/build.gradle.kts index af95832..d873068 100644 --- a/services/comment/application/build.gradle.kts +++ b/services/comment/application/build.gradle.kts @@ -1,3 +1,3 @@ dependencies { - api(project(":comment:api")) + api(project(":comment:comment-api")) } \ No newline at end of file diff --git a/services/comment/build.gradle.kts b/services/comment/build.gradle.kts index c0bd3e5..7ed76de 100644 --- a/services/comment/build.gradle.kts +++ b/services/comment/build.gradle.kts @@ -1,6 +1,11 @@ +val commentApi: String by project +val commentApplication: String by project +val commentRdbAdapter: String by project +val commentWebMvcAdapter: String by project + dependencies { - api(project(":comment:api")) - api(project(":comment:application")) - api(project(":comment:rdb-adapter")) - api(project(":comment:webmvc-adapter")) + api(project(commentApi)) + api(project(commentApplication)) + api(project(commentRdbAdapter)) + api(project(commentWebMvcAdapter)) } \ No newline at end of file diff --git a/services/comment/comment.settings.gradle.kts b/services/comment/comment.settings.gradle.kts index 39ee11a..e609685 100644 --- a/services/comment/comment.settings.gradle.kts +++ b/services/comment/comment.settings.gradle.kts @@ -1,3 +1,13 @@ +val comment: String by settings +val commentApi: String by settings +val commentDomain: String by settings +val commentException: String by settings +val commentReadModel: String by settings +val commentApplication: String by settings +val commentRdbAdapter: String by settings +val commentWebMvcAdapter: String by settings + + fun getDirectories(vararg names: String): (String) -> File { var dir = rootDir for (name in names) { @@ -11,25 +21,25 @@ fun getDirectories(vararg names: String): (String) -> File { } } -val comment = getDirectories("services", "comment") +val commentDirectory = getDirectories("services", "comment") // SERVICE/COMMENT include( - ":comment", - ":comment:api", - ":comment:api:domain", - ":comment:api:exception", - ":comment:api:readmodel", - ":comment:application", - ":comment:rdb-adapter", - ":comment:webmvc-adapter", + comment, + commentApi, + commentDomain, + commentException, + commentReadModel, + commentApplication, + commentRdbAdapter, + commentWebMvcAdapter, ) -project(":comment").projectDir = comment("comment") -project(":comment:api").projectDir = comment("api") -project(":comment:api:domain").projectDir = comment("domain") -project(":comment:api:exception").projectDir = comment("exception") -project(":comment:api:readmodel").projectDir = comment("readmodel") -project(":comment:application").projectDir = comment("application") -project(":comment:rdb-adapter").projectDir = comment("rdb") -project(":comment:webmvc-adapter").projectDir = comment("web-mvc") +project(comment).projectDir = commentDirectory("comment") +project(commentApi).projectDir = commentDirectory("api") +project(commentDomain).projectDir = commentDirectory("domain") +project(commentException).projectDir = commentDirectory("exception") +project(commentReadModel).projectDir = commentDirectory("readmodel") +project(commentApplication).projectDir = commentDirectory("application") +project(commentRdbAdapter).projectDir = commentDirectory("rdb") +project(commentWebMvcAdapter).projectDir = commentDirectory("web-mvc") diff --git a/services/comment/driven/rdb/build.gradle.kts b/services/comment/driven/rdb/build.gradle.kts index ee3d71f..2a33eb1 100644 --- a/services/comment/driven/rdb/build.gradle.kts +++ b/services/comment/driven/rdb/build.gradle.kts @@ -1,8 +1,8 @@ dependencies { val bom = dependencyManagement.importedProperties - api(project(":comment:api")) - api(project(":comment:application")) + api(project(":comment:comment-api")) + api(project(":comment:comment-application")) api(project(":jpa-core")) // spring From de2beaac50fab641ea9ee3553cf1839f079f4244 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sun, 11 May 2025 22:14:55 +0900 Subject: [PATCH 12/25] feat(comment/reply): add EntityStatusConverter --- .../type/CommentEntityStatusConverter.java | 18 ++++++++++++++++++ .../type/ReplyEntityStatusConverter.java | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatusConverter.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatusConverter.java diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatusConverter.java b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatusConverter.java new file mode 100644 index 0000000..fb47e66 --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatusConverter.java @@ -0,0 +1,18 @@ +package nettee.comment.entity.type; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +@Converter +public class CommentEntityStatusConverter implements AttributeConverter { + + @Override + public Integer convertToDatabaseColumn(CommentEntityStatus status) { + return status.getCode(); + } + + @Override + public CommentEntityStatus convertToEntityAttribute(Integer value) { + return CommentEntityStatus.valueOf(value); + } +} \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatusConverter.java b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatusConverter.java new file mode 100644 index 0000000..a685e77 --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatusConverter.java @@ -0,0 +1,19 @@ +package nettee.reply.entity.type; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + + +@Converter +public class ReplyEntityStatusConverter implements AttributeConverter { + + @Override + public Integer convertToDatabaseColumn(ReplyEntityStatus status) { + return status.getCode(); + } + + @Override + public ReplyEntityStatus convertToEntityAttribute(Integer value) { + return ReplyEntityStatus.valueOf(value); + } +} \ No newline at end of file From 2a2af19dc0d8caf53f6c093c34ede4eed5fb7e09 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sun, 11 May 2025 22:16:18 +0900 Subject: [PATCH 13/25] fix(comment/reply): modify comment/driven/rdb/build.gradle.kts --- services/comment/driven/rdb/build.gradle.kts | 2 -- 1 file changed, 2 deletions(-) diff --git a/services/comment/driven/rdb/build.gradle.kts b/services/comment/driven/rdb/build.gradle.kts index 2a33eb1..e6322c8 100644 --- a/services/comment/driven/rdb/build.gradle.kts +++ b/services/comment/driven/rdb/build.gradle.kts @@ -5,8 +5,6 @@ dependencies { api(project(":comment:comment-application")) api(project(":jpa-core")) - // spring - implementation("org.springframework.boot:spring-boot-starter-data-jpa") // querydsl implementation("com.querydsl:querydsl-jpa:${bom["querydsl.version"]}:jakarta") From 746daea78fb24f30204bd174f49b9b7ea12be9f3 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sun, 11 May 2025 22:19:26 +0900 Subject: [PATCH 14/25] fix(comment/reply): resolve conflict monolith/main-runner/build.gradle.kts --- monolith/main-runner/build.gradle.kts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/monolith/main-runner/build.gradle.kts b/monolith/main-runner/build.gradle.kts index f976217..27bf804 100644 --- a/monolith/main-runner/build.gradle.kts +++ b/monolith/main-runner/build.gradle.kts @@ -1,5 +1,8 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar +val board: String by project +val comment: String by project + version = "0.0.1-SNAPSHOT" dependencies { @@ -7,16 +10,22 @@ dependencies { implementation(project(":exception-handler-core")) implementation(project(":jpa-core")) implementation(project(":cors-webmvc")) - // service - implementation(project(":board")) - implementation(project(":comment")) + + // services + implementation(project(board)) + implementation(project(comment)) + // webmvc implementation("org.springframework.boot:spring-boot-starter-web") + // db runtimeOnly("org.postgresql:postgresql:42.7.4") + // flyway implementation("org.flywaydb:flyway-database-postgresql") + // test + testImplementation("com.h2database:h2") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("com.fasterxml.jackson.core:jackson-databind") testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin") From 970cfc441b4478c753c09874554687a0e289037c Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sun, 18 May 2025 18:55:53 +0900 Subject: [PATCH 15/25] refactor(comment/reply): add builder method to entity --- .../nettee/comment/entity/CommentEntity.java | 30 ++++++++++++++++++- .../java/nettee/reply/entity/ReplyEntity.java | 30 ++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java index 8368ce4..58b0373 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java @@ -1,7 +1,9 @@ package nettee.comment.entity; +import jakarta.persistence.Column; import jakarta.persistence.Convert; import jakarta.persistence.Entity; +import java.util.Objects; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -17,14 +19,40 @@ @Entity(name = "comment") public class CommentEntity extends LongBaseTimeEntity { + @Column(nullable = false) + public Long boardId; + public String content; @Convert(converter = CommentEntityStatusConverter.class) public CommentEntityStatus status; @Builder - public CommentEntity(String content, CommentEntityStatus status) { + public CommentEntity(Long boardId, String content, CommentEntityStatus status) { + this.boardId = boardId; this.content = content; this.status = status; } + + @Builder( + builderClassName = "updateCommentEntityBuilder", + builderMethodName = "prepareCommentEntityUpdate", + buildMethodName = "update" + ) + public void update(String content) { + Objects.requireNonNull(content, "Content cannot be null!"); + + this.content = content; + } + + @Builder( + builderClassName = "updateStatusCommentEntityBuilder", + builderMethodName = "prepareCommentEntityStatusUpdate", + buildMethodName = "updateStatus" + ) + public void updateStatus(CommentEntityStatus status) { + Objects.requireNonNull(status, "status cannot be null"); + + this.status = status; + } } diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java index 15f873c..8a2980a 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java @@ -1,7 +1,9 @@ package nettee.reply.entity; +import jakarta.persistence.Column; import jakarta.persistence.Convert; import jakarta.persistence.Entity; +import java.util.Objects; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -17,14 +19,40 @@ @Entity(name = "reply") public class ReplyEntity extends LongBaseTimeEntity { + @Column(nullable = false) + public Long commentId; + public String content; @Convert(converter = ReplyEntityStatusConverter.class) public ReplyEntityStatus status; @Builder - public ReplyEntity(String content, ReplyEntityStatus status) { + public ReplyEntity(Long commentId, String content, ReplyEntityStatus status) { + this.commentId = commentId; this.content = content; this.status = status; } + + @Builder( + builderClassName = "updateReplyEntityBuilder", + builderMethodName = "prepareReplyEntityUpdate", + buildMethodName = "update" + ) + public void update(String content) { + Objects.requireNonNull(content, "Content cannot be null!"); + + this.content = content; + } + + @Builder( + builderClassName = "updateStatusReplyEntityBuilder", + builderMethodName = "prepareReplyEntityStatusUpdate", + buildMethodName = "updateStatus" + ) + public void updateStatus(ReplyEntityStatus status) { + Objects.requireNonNull(status, "status cannot be null"); + + this.status = status; + } } From 8bb4c97874c778c055ea96168c8b959414f5e50a Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sun, 18 May 2025 18:56:36 +0900 Subject: [PATCH 16/25] refactor(comment/reply): add fk to domain --- .../api/domain/src/main/java/nettee/comment/Comment.java | 2 ++ .../comment/api/domain/src/main/java/nettee/reply/Reply.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/services/comment/api/domain/src/main/java/nettee/comment/Comment.java b/services/comment/api/domain/src/main/java/nettee/comment/Comment.java index 448d201..65683ee 100644 --- a/services/comment/api/domain/src/main/java/nettee/comment/Comment.java +++ b/services/comment/api/domain/src/main/java/nettee/comment/Comment.java @@ -16,6 +16,8 @@ public class Comment { private Long id; + private Long boardId; + private String content; private CommentStatus status; diff --git a/services/comment/api/domain/src/main/java/nettee/reply/Reply.java b/services/comment/api/domain/src/main/java/nettee/reply/Reply.java index 5a09965..4316089 100644 --- a/services/comment/api/domain/src/main/java/nettee/reply/Reply.java +++ b/services/comment/api/domain/src/main/java/nettee/reply/Reply.java @@ -16,7 +16,7 @@ public class Reply { private Long id; - private Long parentId; + private Long commentId; private String content; From c8814aabe52ac4fd66d284b25ce369132b2167ea Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sun, 18 May 2025 18:59:35 +0900 Subject: [PATCH 17/25] feat(comment/reply): add RDB adapter(command,query) --- .../port/CommentCommandRepositoryPort.java | 3 +- .../port/CommentQueryRepositoryPort.java | 8 +- .../port/ReplyCommandRepositoryPort.java | 3 +- .../persistence/CommentCommandAdapter.java | 55 ++++++++++++++ .../persistence/CommentJpaRepository.java | 8 ++ .../persistence/CommentQueryAdapter.java | 74 +++++++++++++++++++ .../mapper/CommentEntityMapper.java | 4 + .../persistence/ReplyCommandAdapter.java | 55 ++++++++++++++ .../reply/persistence/ReplyJpaRepository.java | 8 ++ .../reply/persistence/ReplyQueryAdapter.java | 74 +++++++++++++++++++ .../persistence/mapper/ReplyEntityMapper.java | 5 ++ 11 files changed, 291 insertions(+), 6 deletions(-) create mode 100644 services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentCommandAdapter.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentJpaRepository.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyCommandAdapter.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyJpaRepository.java create mode 100644 services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java diff --git a/services/comment/application/src/main/java/nettee/comment/port/CommentCommandRepositoryPort.java b/services/comment/application/src/main/java/nettee/comment/port/CommentCommandRepositoryPort.java index 0c9b842..452305f 100644 --- a/services/comment/application/src/main/java/nettee/comment/port/CommentCommandRepositoryPort.java +++ b/services/comment/application/src/main/java/nettee/comment/port/CommentCommandRepositoryPort.java @@ -1,6 +1,7 @@ package nettee.comment.port; import nettee.comment.Comment; +import nettee.comment.type.CommentStatus; public interface CommentCommandRepositoryPort { @@ -8,5 +9,5 @@ public interface CommentCommandRepositoryPort { Comment update(Comment comment); - void delete(Comment comment); + void updateStatus(Long id, CommentStatus status); } diff --git a/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java b/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java index 49ccd7c..7388570 100644 --- a/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java +++ b/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java @@ -8,9 +8,9 @@ public interface CommentQueryRepositoryPort { Optional findById(Long id); - // post_id에 해당하는 최초 comment 10개 조회 - List find10ByPostId(Long postId); + // board_id에 해당하는 최초 comment 10개 조회 + List find10ByPostId(Long boardId); - // post_id, 현재 페이지의 마지막 이후의 comment 10개 조회 - List find10ByPostIdAfterCommentId(Long postId, Instant createdAt); + // board_id, 현재 페이지의 마지막 이후의 comment 10개 조회 + List find10ByPostIdAfterCommentId(Long boardId, Instant createdAt); } diff --git a/services/comment/application/src/main/java/nettee/reply/port/ReplyCommandRepositoryPort.java b/services/comment/application/src/main/java/nettee/reply/port/ReplyCommandRepositoryPort.java index 4f59c24..1f10d93 100644 --- a/services/comment/application/src/main/java/nettee/reply/port/ReplyCommandRepositoryPort.java +++ b/services/comment/application/src/main/java/nettee/reply/port/ReplyCommandRepositoryPort.java @@ -1,6 +1,7 @@ package nettee.reply.port; import nettee.reply.Reply; +import nettee.reply.type.ReplyStatus; public interface ReplyCommandRepositoryPort { @@ -8,5 +9,5 @@ public interface ReplyCommandRepositoryPort { Reply update(Reply reply); - void delete(Reply reply); + void updateStatus(Long id, ReplyStatus status); } diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentCommandAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentCommandAdapter.java new file mode 100644 index 0000000..bd1e1dd --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentCommandAdapter.java @@ -0,0 +1,55 @@ +package nettee.comment.persistence; + +import lombok.RequiredArgsConstructor; +import nettee.comment.Comment; +import nettee.comment.entity.type.CommentEntityStatus; +import nettee.comment.persistence.mapper.CommentEntityMapper; +import nettee.comment.port.CommentCommandRepositoryPort; +import nettee.comment.type.CommentStatus; +import org.springframework.dao.DataAccessException; +import org.springframework.stereotype.Repository; + +import static nettee.comment.CommentCommandErrorCode.COMMENT_NOT_FOUND; +import static nettee.comment.CommentCommandErrorCode.DEFAULT; + +@Repository +@RequiredArgsConstructor +public class CommentCommandAdapter implements CommentCommandRepositoryPort { + + private final CommentJpaRepository commentJpaRepository; + private final CommentEntityMapper commentEntityMapper; + + @Override + public Comment save(Comment comment) { + var commentEntity = commentEntityMapper.toEntity(comment); + try { + var newComment = commentJpaRepository.save(commentEntity); + commentJpaRepository.flush(); + return commentEntityMapper.toDomain(newComment); + } catch (DataAccessException e) { + throw DEFAULT.exception(e); + } + } + + @Override + public Comment update(Comment comment) { + var existComment = commentJpaRepository.findById(comment.getId()) + .orElseThrow(COMMENT_NOT_FOUND::exception); + + existComment.prepareCommentEntityUpdate() + .content(comment.getContent()) + .update(); + + return commentEntityMapper.toDomain(existComment); + } + + @Override + public void updateStatus(Long id, CommentStatus status) { + var existComment = commentJpaRepository.findById(id) + .orElseThrow(COMMENT_NOT_FOUND::exception); + + existComment.prepareCommentEntityStatusUpdate() + .status(CommentEntityStatus.valueOf(status)) + .updateStatus(); + } +} diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentJpaRepository.java b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentJpaRepository.java new file mode 100644 index 0000000..12232d6 --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentJpaRepository.java @@ -0,0 +1,8 @@ +package nettee.comment.persistence; + +import nettee.comment.entity.CommentEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CommentJpaRepository extends JpaRepository { + +} diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java new file mode 100644 index 0000000..5d917f2 --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java @@ -0,0 +1,74 @@ +package nettee.comment.persistence; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import nettee.comment.Comment; +import nettee.comment.entity.CommentEntity; +import nettee.comment.persistence.mapper.CommentEntityMapper; +import nettee.comment.port.CommentQueryRepositoryPort; +import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; +import org.springframework.stereotype.Repository; + +import static nettee.comment.entity.QCommentEntity.commentEntity; + +@Repository +public class CommentQueryAdapter extends QuerydslRepositorySupport implements CommentQueryRepositoryPort { + + private final CommentEntityMapper commentEntityMapper; + + public CommentQueryAdapter(CommentEntityMapper commentEntityMapper) { + super(CommentEntity.class); + this.commentEntityMapper = commentEntityMapper; + } + + @Override + public Optional findById(Long id) { + return commentEntityMapper.toOptionalDomain( + getQuerydsl().createQuery() + .select(commentEntity) + .from(commentEntity) + .where(commentEntity.id.eq(id)) + .fetchOne() + ); + } + + @Override + public List find10ByPostId(Long boardId) { + var entityList = getQuerydsl().createQuery() + .select(commentEntity) + .from(commentEntity) + .where(commentEntity.boardId.eq(boardId)) + .offset(0) + .limit(10) + .orderBy(commentEntity.createdAt.asc()) + .fetch(); + + var result = entityList.stream() + .map(entity -> commentEntityMapper.toDomain(entity)) + .collect(Collectors.toList()); + + return result; + } + + @Override + public List find10ByPostIdAfterCommentId(Long boardId, Instant createdAt) { + var entityList = getQuerydsl().createQuery() + .select(commentEntity) + .from(commentEntity) + .where( + commentEntity.boardId.eq(boardId).and( + commentEntity.createdAt.after(createdAt))) + .offset(0) + .limit(10) + .orderBy(commentEntity.createdAt.asc()) + .fetch(); + + var result = entityList.stream() + .map(entity -> commentEntityMapper.toDomain(entity)) + .collect(Collectors.toList()); + + return result; + } +} diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java index c4ecf67..2de37f3 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java @@ -1,5 +1,6 @@ package nettee.comment.persistence.mapper; +import java.util.Optional; import nettee.comment.Comment; import nettee.comment.entity.CommentEntity; import org.mapstruct.Mapper; @@ -11,4 +12,7 @@ public interface CommentEntityMapper { CommentEntity toEntity(Comment comment); + default Optional toOptionalDomain(CommentEntity commentEntity) { + return Optional.ofNullable(toDomain(commentEntity)); + } } \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyCommandAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyCommandAdapter.java new file mode 100644 index 0000000..d2e112a --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyCommandAdapter.java @@ -0,0 +1,55 @@ +package nettee.reply.persistence; + +import lombok.RequiredArgsConstructor; +import nettee.reply.Reply; +import nettee.reply.entity.type.ReplyEntityStatus; +import nettee.reply.persistence.mapper.ReplyEntityMapper; +import nettee.reply.port.ReplyCommandRepositoryPort; +import nettee.reply.type.ReplyStatus; +import org.springframework.dao.DataAccessException; +import org.springframework.stereotype.Repository; + +import static nettee.reply.ReplyCommandErrorCode.DEFAULT; +import static nettee.reply.ReplyCommandErrorCode.REPLY_NOT_FOUND; + +@Repository +@RequiredArgsConstructor +public class ReplyCommandAdapter implements ReplyCommandRepositoryPort { + + private final ReplyJpaRepository replyJpaRepository; + private final ReplyEntityMapper replyEntityMapper; + + @Override + public Reply save(Reply reply) { + var replyEntity = replyEntityMapper.toEntity(reply); + try { + var newReply = replyJpaRepository.save(replyEntity); + replyJpaRepository.flush(); + return replyEntityMapper.toDomain(newReply); + } catch (DataAccessException e) { + throw DEFAULT.exception(e); + } + } + + @Override + public Reply update(Reply reply) { + var existReply = replyJpaRepository.findById(reply.getId()) + .orElseThrow(REPLY_NOT_FOUND::exception); + + existReply.prepareReplyEntityUpdate() + .content(reply.getContent()) + .update(); + + return replyEntityMapper.toDomain(existReply); + } + + @Override + public void updateStatus(Long id, ReplyStatus status) { + var existReply = replyJpaRepository.findById(id) + .orElseThrow(REPLY_NOT_FOUND::exception); + + existReply.prepareReplyEntityStatusUpdate() + .status(ReplyEntityStatus.valueOf(status)) + .updateStatus(); + } +} diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyJpaRepository.java b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyJpaRepository.java new file mode 100644 index 0000000..6f4ffe8 --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyJpaRepository.java @@ -0,0 +1,8 @@ +package nettee.reply.persistence; + +import nettee.reply.entity.ReplyEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReplyJpaRepository extends JpaRepository { + +} diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java new file mode 100644 index 0000000..92ff9e7 --- /dev/null +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java @@ -0,0 +1,74 @@ +package nettee.reply.persistence; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import nettee.reply.Reply; +import nettee.reply.entity.ReplyEntity; +import nettee.reply.persistence.mapper.ReplyEntityMapper; +import nettee.reply.port.ReplyQueryRepositoryPort; +import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; +import org.springframework.stereotype.Repository; + +import static nettee.reply.entity.QReplyEntity.replyEntity; + +@Repository +public class ReplyQueryAdapter extends QuerydslRepositorySupport implements ReplyQueryRepositoryPort { + + private final ReplyEntityMapper replyEntityMapper; + + public ReplyQueryAdapter(ReplyEntityMapper replyEntityMapper) { + super(ReplyEntity.class); + this.replyEntityMapper = replyEntityMapper; + } + + @Override + public Optional findById(Long id) { + return replyEntityMapper.toOptionalDomain( + getQuerydsl().createQuery() + .select(replyEntity) + .from(replyEntity) + .where(replyEntity.id.eq(id)) + .fetchOne() + ); + } + + @Override + public List find10ByCommentId(Long commentId) { + var entityList = getQuerydsl().createQuery() + .select(replyEntity) + .from(replyEntity) + .where(replyEntity.commentId.eq(commentId)) + .offset(0) + .limit(10) + .orderBy(replyEntity.createdAt.asc()) + .fetch(); + + var result = entityList.stream() + .map(entity -> replyEntityMapper.toDomain(entity)) + .collect(Collectors.toList()); + + return result; + } + + @Override + public List find10ByCommentIdAfter(Long commentId, Instant createdAt) { + var entityList = getQuerydsl().createQuery() + .select(replyEntity) + .from(replyEntity) + .where( + replyEntity.commentId.eq(commentId).and( + replyEntity.createdAt.after(createdAt))) + .offset(0) + .limit(10) + .orderBy(replyEntity.createdAt.asc()) + .fetch(); + + var result = entityList.stream() + .map(entity -> replyEntityMapper.toDomain(entity)) + .collect(Collectors.toList()); + + return result; + } +} diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java index 532349c..698006f 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java @@ -1,5 +1,6 @@ package nettee.reply.persistence.mapper; +import java.util.Optional; import nettee.reply.Reply; import nettee.reply.entity.ReplyEntity; import org.mapstruct.Mapper; @@ -11,4 +12,8 @@ public interface ReplyEntityMapper { ReplyEntity toEntity(Reply reply); + default Optional toOptionalDomain(ReplyEntity replyEntity) { + return Optional.ofNullable(toDomain(replyEntity)); + } + } \ No newline at end of file From c4c602fb01277b8e99b4edec54b66b0de28e67f7 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Sun, 18 May 2025 19:01:11 +0900 Subject: [PATCH 18/25] refactor(comment/reply): remove getter annotation from entity --- .../rdb/src/main/java/nettee/comment/entity/CommentEntity.java | 1 - .../rdb/src/main/java/nettee/reply/entity/ReplyEntity.java | 1 - 2 files changed, 2 deletions(-) diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java index 58b0373..9a3c02e 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java @@ -13,7 +13,6 @@ import nettee.jpa.support.LongBaseTimeEntity; import org.hibernate.annotations.DynamicUpdate; -@Getter @DynamicUpdate @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity(name = "comment") diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java index 8a2980a..ec355c0 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java @@ -13,7 +13,6 @@ import nettee.reply.entity.type.ReplyEntityStatusConverter; import org.hibernate.annotations.DynamicUpdate; -@Getter @DynamicUpdate @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity(name = "reply") From 5b77667603f8046f912a64f30998633d83815f09 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Mon, 19 May 2025 14:37:26 +0900 Subject: [PATCH 19/25] refactor(comment/reply): modify function, parameter of port and adapter --- .../comment/port/CommentQueryRepositoryPort.java | 8 ++++---- .../nettee/reply/port/ReplyQueryRepositoryPort.java | 8 ++++---- .../comment/persistence/CommentQueryAdapter.java | 10 +++++----- .../nettee/reply/persistence/ReplyQueryAdapter.java | 10 +++++----- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java b/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java index 7388570..16adb3d 100644 --- a/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java +++ b/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java @@ -8,9 +8,9 @@ public interface CommentQueryRepositoryPort { Optional findById(Long id); - // board_id에 해당하는 최초 comment 10개 조회 - List find10ByPostId(Long boardId); + // board_id에 해당하는 comment 목록 조회 + List findPageByBoardId(Long boardId, int offset, int size); - // board_id, 현재 페이지의 마지막 이후의 comment 10개 조회 - List find10ByPostIdAfterCommentId(Long boardId, Instant createdAt); + // board_id, 현재 페이지의 마지막 생성일 이후의 comment 목록 조회 + List findPageByBoardIdAfter(Long boardId, Instant createdAt, int size); } diff --git a/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java b/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java index 4eadb4c..4ff0643 100644 --- a/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java +++ b/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java @@ -9,9 +9,9 @@ public interface ReplyQueryRepositoryPort { Optional findById(Long id); - // comment_id에 해당하는 최초 reply 10개 조회 - List find10ByCommentId(Long commentId); + // comment_id에 해당하는 최초 reply 목록 조회 + List findPageByCommentId(Long commentId, int offset, int size); - // comment_id, 현재 페이지의 마지막 이후의 reply 10개 조회 - List find10ByCommentIdAfter(Long commentId, Instant createdAt); + // comment_id, 현재 페이지의 마지막 이후의 reply 목록 조회 + List findPageByCommentIdAfter(Long commentId, Instant createdAt, int size); } diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java index 5d917f2..a86a3a0 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java @@ -35,13 +35,13 @@ public Optional findById(Long id) { } @Override - public List find10ByPostId(Long boardId) { + public List findPageByBoardId(Long boardId, int offset, int size) { var entityList = getQuerydsl().createQuery() .select(commentEntity) .from(commentEntity) .where(commentEntity.boardId.eq(boardId)) - .offset(0) - .limit(10) + .offset(offset) + .limit(size) .orderBy(commentEntity.createdAt.asc()) .fetch(); @@ -53,7 +53,7 @@ public List find10ByPostId(Long boardId) { } @Override - public List find10ByPostIdAfterCommentId(Long boardId, Instant createdAt) { + public List findPageByBoardIdAfter(Long boardId, Instant createdAt, int size) { var entityList = getQuerydsl().createQuery() .select(commentEntity) .from(commentEntity) @@ -61,7 +61,7 @@ public List find10ByPostIdAfterCommentId(Long boardId, Instant createdA commentEntity.boardId.eq(boardId).and( commentEntity.createdAt.after(createdAt))) .offset(0) - .limit(10) + .limit(size) .orderBy(commentEntity.createdAt.asc()) .fetch(); diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java index 92ff9e7..59af616 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java @@ -35,13 +35,13 @@ public Optional findById(Long id) { } @Override - public List find10ByCommentId(Long commentId) { + public List findPageByCommentId(Long commentId, int offset, int size) { var entityList = getQuerydsl().createQuery() .select(replyEntity) .from(replyEntity) .where(replyEntity.commentId.eq(commentId)) - .offset(0) - .limit(10) + .offset(offset) + .limit(size) .orderBy(replyEntity.createdAt.asc()) .fetch(); @@ -53,7 +53,7 @@ public List find10ByCommentId(Long commentId) { } @Override - public List find10ByCommentIdAfter(Long commentId, Instant createdAt) { + public List findPageByCommentIdAfter(Long commentId, Instant createdAt, int size) { var entityList = getQuerydsl().createQuery() .select(replyEntity) .from(replyEntity) @@ -61,7 +61,7 @@ public List find10ByCommentIdAfter(Long commentId, Instant createdAt) { replyEntity.commentId.eq(commentId).and( replyEntity.createdAt.after(createdAt))) .offset(0) - .limit(10) + .limit(size) .orderBy(replyEntity.createdAt.asc()) .fetch(); From 0488fb9bea1cd9be774adc31d2984a077431db27 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Mon, 19 May 2025 14:38:00 +0900 Subject: [PATCH 20/25] feat(comment/reply): add QueryService, CommandService, UseCase --- .../service/CommentCommandService.java | 32 +++++++++++++++++++ .../comment/service/CommentQueryService.java | 24 ++++++++++++++ .../comment/usecase/CommentCreateUseCase.java | 7 ++++ .../comment/usecase/CommentDeleteUseCase.java | 7 ++++ .../comment/usecase/CommentUpdateUseCase.java | 7 ++++ .../reply/service/ReplyCommandService.java | 32 +++++++++++++++++++ .../reply/service/ReplyQueryService.java | 24 ++++++++++++++ .../reply/usecase/ReplyCreateUseCase.java | 7 ++++ .../reply/usecase/ReplyDeleteUseCase.java | 5 +++ .../reply/usecase/ReplyUpdateUseCase.java | 7 ++++ 10 files changed, 152 insertions(+) create mode 100644 services/comment/application/src/main/java/nettee/comment/service/CommentCommandService.java create mode 100644 services/comment/application/src/main/java/nettee/comment/service/CommentQueryService.java create mode 100644 services/comment/application/src/main/java/nettee/comment/usecase/CommentCreateUseCase.java create mode 100644 services/comment/application/src/main/java/nettee/comment/usecase/CommentDeleteUseCase.java create mode 100644 services/comment/application/src/main/java/nettee/comment/usecase/CommentUpdateUseCase.java create mode 100644 services/comment/application/src/main/java/nettee/reply/service/ReplyCommandService.java create mode 100644 services/comment/application/src/main/java/nettee/reply/service/ReplyQueryService.java create mode 100644 services/comment/application/src/main/java/nettee/reply/usecase/ReplyCreateUseCase.java create mode 100644 services/comment/application/src/main/java/nettee/reply/usecase/ReplyDeleteUseCase.java create mode 100644 services/comment/application/src/main/java/nettee/reply/usecase/ReplyUpdateUseCase.java diff --git a/services/comment/application/src/main/java/nettee/comment/service/CommentCommandService.java b/services/comment/application/src/main/java/nettee/comment/service/CommentCommandService.java new file mode 100644 index 0000000..cab0310 --- /dev/null +++ b/services/comment/application/src/main/java/nettee/comment/service/CommentCommandService.java @@ -0,0 +1,32 @@ +package nettee.comment.service; + +import lombok.RequiredArgsConstructor; +import nettee.comment.Comment; +import nettee.comment.port.CommentCommandRepositoryPort; +import nettee.comment.type.CommentStatus; +import nettee.comment.usecase.CommentCreateUseCase; +import nettee.comment.usecase.CommentDeleteUseCase; +import nettee.comment.usecase.CommentUpdateUseCase; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CommentCommandService implements CommentCreateUseCase, CommentUpdateUseCase, CommentDeleteUseCase { + + private final CommentCommandRepositoryPort commentCommandRepositoryPort; + + @Override + public Comment createComment(Comment comment) { + return commentCommandRepositoryPort.save(comment); + } + + @Override + public Comment updateComment(Comment comment) { + return commentCommandRepositoryPort.update(comment); + } + + @Override + public void deleteComment(Long id) { + commentCommandRepositoryPort.updateStatus(id, CommentStatus.REMOVED); + } +} diff --git a/services/comment/application/src/main/java/nettee/comment/service/CommentQueryService.java b/services/comment/application/src/main/java/nettee/comment/service/CommentQueryService.java new file mode 100644 index 0000000..486251e --- /dev/null +++ b/services/comment/application/src/main/java/nettee/comment/service/CommentQueryService.java @@ -0,0 +1,24 @@ +package nettee.comment.service; + +import java.time.Instant; +import java.util.List; +import lombok.RequiredArgsConstructor; +import nettee.comment.Comment; +import nettee.comment.port.CommentQueryRepositoryPort; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CommentQueryService { + + private final CommentQueryRepositoryPort commentQueryRepositoryPort; + + public List getCommentListByBoardId(Long boardId) { + return commentQueryRepositoryPort.findPageByBoardId(boardId, 0, 10); + } + + public List getCommentListByBoardIdAfter(Long boardId, Instant createdAt) { + return commentQueryRepositoryPort.findPageByBoardIdAfter(boardId, createdAt, 10); + } + +} diff --git a/services/comment/application/src/main/java/nettee/comment/usecase/CommentCreateUseCase.java b/services/comment/application/src/main/java/nettee/comment/usecase/CommentCreateUseCase.java new file mode 100644 index 0000000..da1ef82 --- /dev/null +++ b/services/comment/application/src/main/java/nettee/comment/usecase/CommentCreateUseCase.java @@ -0,0 +1,7 @@ +package nettee.comment.usecase; + +import nettee.comment.Comment; + +public interface CommentCreateUseCase { + Comment createComment(Comment comment); +} diff --git a/services/comment/application/src/main/java/nettee/comment/usecase/CommentDeleteUseCase.java b/services/comment/application/src/main/java/nettee/comment/usecase/CommentDeleteUseCase.java new file mode 100644 index 0000000..47a9d69 --- /dev/null +++ b/services/comment/application/src/main/java/nettee/comment/usecase/CommentDeleteUseCase.java @@ -0,0 +1,7 @@ +package nettee.comment.usecase; + +import nettee.comment.Comment; + +public interface CommentDeleteUseCase { + void deleteComment(Long id); +} diff --git a/services/comment/application/src/main/java/nettee/comment/usecase/CommentUpdateUseCase.java b/services/comment/application/src/main/java/nettee/comment/usecase/CommentUpdateUseCase.java new file mode 100644 index 0000000..39dbd3e --- /dev/null +++ b/services/comment/application/src/main/java/nettee/comment/usecase/CommentUpdateUseCase.java @@ -0,0 +1,7 @@ +package nettee.comment.usecase; + +import nettee.comment.Comment; + +public interface CommentUpdateUseCase { + Comment updateComment(Comment comment); +} diff --git a/services/comment/application/src/main/java/nettee/reply/service/ReplyCommandService.java b/services/comment/application/src/main/java/nettee/reply/service/ReplyCommandService.java new file mode 100644 index 0000000..d066de6 --- /dev/null +++ b/services/comment/application/src/main/java/nettee/reply/service/ReplyCommandService.java @@ -0,0 +1,32 @@ +package nettee.reply.service; + +import lombok.RequiredArgsConstructor; +import nettee.reply.Reply; +import nettee.reply.port.ReplyCommandRepositoryPort; +import nettee.reply.type.ReplyStatus; +import nettee.reply.usecase.ReplyCreateUseCase; +import nettee.reply.usecase.ReplyDeleteUseCase; +import nettee.reply.usecase.ReplyUpdateUseCase; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ReplyCommandService implements ReplyCreateUseCase, ReplyUpdateUseCase, ReplyDeleteUseCase { + + private final ReplyCommandRepositoryPort replyCommandRepositoryPort; + + @Override + public Reply createReply(Reply reply) { + return replyCommandRepositoryPort.save(reply); + } + + @Override + public void deleteReply(Long id) { + replyCommandRepositoryPort.updateStatus(id, ReplyStatus.REMOVED); + } + + @Override + public Reply updateReply(Reply reply) { + return replyCommandRepositoryPort.update(reply); + } +} diff --git a/services/comment/application/src/main/java/nettee/reply/service/ReplyQueryService.java b/services/comment/application/src/main/java/nettee/reply/service/ReplyQueryService.java new file mode 100644 index 0000000..e50f0da --- /dev/null +++ b/services/comment/application/src/main/java/nettee/reply/service/ReplyQueryService.java @@ -0,0 +1,24 @@ +package nettee.reply.service; + +import java.time.Instant; +import java.util.List; +import lombok.RequiredArgsConstructor; +import nettee.reply.Reply; +import nettee.reply.port.ReplyQueryRepositoryPort; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ReplyQueryService { + + private final ReplyQueryRepositoryPort replyQueryRepositoryPort; + + public List getReplyListByCommentId(Long commentId) { + return replyQueryRepositoryPort.findPageByCommentId(commentId, 0, 10); + } + + public List getReplyListByCommentIdAfter(Long commentId, Instant createdAt) { + return replyQueryRepositoryPort.findPageByCommentIdAfter(commentId, createdAt, 10); + } + +} diff --git a/services/comment/application/src/main/java/nettee/reply/usecase/ReplyCreateUseCase.java b/services/comment/application/src/main/java/nettee/reply/usecase/ReplyCreateUseCase.java new file mode 100644 index 0000000..a853677 --- /dev/null +++ b/services/comment/application/src/main/java/nettee/reply/usecase/ReplyCreateUseCase.java @@ -0,0 +1,7 @@ +package nettee.reply.usecase; + +import nettee.reply.Reply; + +public interface ReplyCreateUseCase { + Reply createReply(Reply reply); +} diff --git a/services/comment/application/src/main/java/nettee/reply/usecase/ReplyDeleteUseCase.java b/services/comment/application/src/main/java/nettee/reply/usecase/ReplyDeleteUseCase.java new file mode 100644 index 0000000..ddfbd74 --- /dev/null +++ b/services/comment/application/src/main/java/nettee/reply/usecase/ReplyDeleteUseCase.java @@ -0,0 +1,5 @@ +package nettee.reply.usecase; + +public interface ReplyDeleteUseCase { + public void deleteReply(Long id); +} diff --git a/services/comment/application/src/main/java/nettee/reply/usecase/ReplyUpdateUseCase.java b/services/comment/application/src/main/java/nettee/reply/usecase/ReplyUpdateUseCase.java new file mode 100644 index 0000000..6d698c1 --- /dev/null +++ b/services/comment/application/src/main/java/nettee/reply/usecase/ReplyUpdateUseCase.java @@ -0,0 +1,7 @@ +package nettee.reply.usecase; + +import nettee.reply.Reply; + +public interface ReplyUpdateUseCase { + public Reply updateReply(Reply reply); +} From f0d73b2f198560484865aa8338d16affd47b9f10 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Mon, 19 May 2025 23:11:33 +0900 Subject: [PATCH 21/25] refactor(comment/reply): modify readModels, port, adapter --- .../comment/model/CommentQueryModels.java | 14 +++------ .../nettee/reply/model/ReplyQueryModels.java | 12 +------- .../port/CommentQueryRepositoryPort.java | 8 ++--- .../reply/port/ReplyQueryRepositoryPort.java | 9 +++--- .../persistence/CommentQueryAdapter.java | 29 ++++--------------- .../mapper/CommentEntityMapper.java | 7 +++-- .../reply/persistence/ReplyQueryAdapter.java | 14 ++++----- .../persistence/mapper/ReplyEntityMapper.java | 7 +++-- .../v1_0/V1_0_3__create_tb_comment.sql | 2 ++ .../v1_0/V1_0_4__create_tb_reply.sql | 4 +-- 10 files changed, 38 insertions(+), 68 deletions(-) diff --git a/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java b/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java index bef349d..03f9500 100644 --- a/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java +++ b/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java @@ -1,9 +1,11 @@ package nettee.comment.model; +import java.util.List; import lombok.Builder; import java.time.Instant; import nettee.comment.type.CommentStatus; +import nettee.reply.model.ReplyQueryModels.ReplyDetail; public final class CommentQueryModels { @@ -16,17 +18,9 @@ public record CommentDetail( String content, CommentStatus status, Instant createdAt, - Instant updatedAt + Instant updatedAt, + List replies ) { } - @Builder - public record CommentSummary( - Long id, - String title, - CommentStatus status, - Instant createdAt, - Instant updatedAt - ) { - } } \ No newline at end of file diff --git a/services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java b/services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java index f580abb..f5f78a1 100644 --- a/services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java +++ b/services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java @@ -12,7 +12,7 @@ private ReplyQueryModels() { @Builder public record ReplyDetail( Long id, - Long parentId, + Long commentId, String content, ReplyStatus status, Instant createdAt, @@ -20,14 +20,4 @@ public record ReplyDetail( ) { } - @Builder - public record ReplySummary( - Long id, - Long parentId, - String title, - ReplyStatus status, - Instant createdAt, - Instant updatedAt - ) { - } } \ No newline at end of file diff --git a/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java b/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java index 16adb3d..f1e70f1 100644 --- a/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java +++ b/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java @@ -4,13 +4,11 @@ import java.util.List; import java.util.Optional; import nettee.comment.Comment; +import nettee.comment.model.CommentQueryModels.CommentDetail; public interface CommentQueryRepositoryPort { - Optional findById(Long id); + Optional findById(Long id); // board_id에 해당하는 comment 목록 조회 - List findPageByBoardId(Long boardId, int offset, int size); - - // board_id, 현재 페이지의 마지막 생성일 이후의 comment 목록 조회 - List findPageByBoardIdAfter(Long boardId, Instant createdAt, int size); + List findPageByBoardId(Long boardId, int offset, int size); } diff --git a/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java b/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java index 4ff0643..d682716 100644 --- a/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java +++ b/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java @@ -4,14 +4,15 @@ import java.util.List; import java.util.Optional; import nettee.reply.Reply; +import nettee.reply.model.ReplyQueryModels.ReplyDetail; public interface ReplyQueryRepositoryPort { - Optional findById(Long id); + Optional findById(Long id); - // comment_id에 해당하는 최초 reply 목록 조회 - List findPageByCommentId(Long commentId, int offset, int size); + // comment_id에 해당하는 reply 목록 조회 + List findPageByCommentId(Long commentId, int offset, int size); // comment_id, 현재 페이지의 마지막 이후의 reply 목록 조회 - List findPageByCommentIdAfter(Long commentId, Instant createdAt, int size); + List findPageByCommentIdAfter(Long commentId, Instant createdAt, int size); } diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java index a86a3a0..7b46573 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java @@ -6,6 +6,7 @@ import java.util.stream.Collectors; import nettee.comment.Comment; import nettee.comment.entity.CommentEntity; +import nettee.comment.model.CommentQueryModels.CommentDetail; import nettee.comment.persistence.mapper.CommentEntityMapper; import nettee.comment.port.CommentQueryRepositoryPort; import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; @@ -24,8 +25,8 @@ public CommentQueryAdapter(CommentEntityMapper commentEntityMapper) { } @Override - public Optional findById(Long id) { - return commentEntityMapper.toOptionalDomain( + public Optional findById(Long id) { + return commentEntityMapper.toOptionalCommentDetail( getQuerydsl().createQuery() .select(commentEntity) .from(commentEntity) @@ -35,7 +36,7 @@ public Optional findById(Long id) { } @Override - public List findPageByBoardId(Long boardId, int offset, int size) { + public List findPageByBoardId(Long boardId, int offset, int size) { var entityList = getQuerydsl().createQuery() .select(commentEntity) .from(commentEntity) @@ -46,27 +47,7 @@ public List findPageByBoardId(Long boardId, int offset, int size) { .fetch(); var result = entityList.stream() - .map(entity -> commentEntityMapper.toDomain(entity)) - .collect(Collectors.toList()); - - return result; - } - - @Override - public List findPageByBoardIdAfter(Long boardId, Instant createdAt, int size) { - var entityList = getQuerydsl().createQuery() - .select(commentEntity) - .from(commentEntity) - .where( - commentEntity.boardId.eq(boardId).and( - commentEntity.createdAt.after(createdAt))) - .offset(0) - .limit(size) - .orderBy(commentEntity.createdAt.asc()) - .fetch(); - - var result = entityList.stream() - .map(entity -> commentEntityMapper.toDomain(entity)) + .map(entity -> commentEntityMapper.toCommentDetail(entity)) .collect(Collectors.toList()); return result; diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java index 2de37f3..8e06d20 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java @@ -3,16 +3,17 @@ import java.util.Optional; import nettee.comment.Comment; import nettee.comment.entity.CommentEntity; +import nettee.comment.model.CommentQueryModels.CommentDetail; import org.mapstruct.Mapper; @Mapper(componentModel = "spring") public interface CommentEntityMapper { Comment toDomain(CommentEntity commentEntity); - + CommentDetail toCommentDetail(CommentEntity commentEntity); CommentEntity toEntity(Comment comment); - default Optional toOptionalDomain(CommentEntity commentEntity) { - return Optional.ofNullable(toDomain(commentEntity)); + default Optional toOptionalCommentDetail(CommentEntity commentEntity) { + return Optional.ofNullable(toCommentDetail(commentEntity)); } } \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java index 59af616..3070062 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java @@ -4,8 +4,8 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import nettee.reply.Reply; import nettee.reply.entity.ReplyEntity; +import nettee.reply.model.ReplyQueryModels.ReplyDetail; import nettee.reply.persistence.mapper.ReplyEntityMapper; import nettee.reply.port.ReplyQueryRepositoryPort; import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; @@ -24,8 +24,8 @@ public ReplyQueryAdapter(ReplyEntityMapper replyEntityMapper) { } @Override - public Optional findById(Long id) { - return replyEntityMapper.toOptionalDomain( + public Optional findById(Long id) { + return replyEntityMapper.toOptionalReplyDetail( getQuerydsl().createQuery() .select(replyEntity) .from(replyEntity) @@ -35,7 +35,7 @@ public Optional findById(Long id) { } @Override - public List findPageByCommentId(Long commentId, int offset, int size) { + public List findPageByCommentId(Long commentId, int offset, int size) { var entityList = getQuerydsl().createQuery() .select(replyEntity) .from(replyEntity) @@ -46,14 +46,14 @@ public List findPageByCommentId(Long commentId, int offset, int size) { .fetch(); var result = entityList.stream() - .map(entity -> replyEntityMapper.toDomain(entity)) + .map(entity -> replyEntityMapper.toReplyDetail(entity)) .collect(Collectors.toList()); return result; } @Override - public List findPageByCommentIdAfter(Long commentId, Instant createdAt, int size) { + public List findPageByCommentIdAfter(Long commentId, Instant createdAt, int size) { var entityList = getQuerydsl().createQuery() .select(replyEntity) .from(replyEntity) @@ -66,7 +66,7 @@ public List findPageByCommentIdAfter(Long commentId, Instant createdAt, i .fetch(); var result = entityList.stream() - .map(entity -> replyEntityMapper.toDomain(entity)) + .map(entity -> replyEntityMapper.toReplyDetail(entity)) .collect(Collectors.toList()); return result; diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java index 698006f..30ecb4b 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java @@ -3,6 +3,7 @@ import java.util.Optional; import nettee.reply.Reply; import nettee.reply.entity.ReplyEntity; +import nettee.reply.model.ReplyQueryModels.ReplyDetail; import org.mapstruct.Mapper; @Mapper(componentModel = "spring") @@ -10,10 +11,12 @@ public interface ReplyEntityMapper { Reply toDomain(ReplyEntity replyEntity); + ReplyDetail toReplyDetail(ReplyEntity replyEntity); + ReplyEntity toEntity(Reply reply); - default Optional toOptionalDomain(ReplyEntity replyEntity) { - return Optional.ofNullable(toDomain(replyEntity)); + default Optional toOptionalReplyDetail(ReplyEntity replyEntity) { + return Optional.ofNullable(toReplyDetail(replyEntity)); } } \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_3__create_tb_comment.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_3__create_tb_comment.sql index 677013d..6e5b88f 100644 --- a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_3__create_tb_comment.sql +++ b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_3__create_tb_comment.sql @@ -1,5 +1,6 @@ CREATE TABLE IF NOT EXISTS comment ( id BIGSERIAL, + board_id BIGINT, content VARCHAR(255), status VARCHAR(255), created_at TIMESTAMP DEFAULT NOW(), @@ -13,6 +14,7 @@ COMMENT ON TABLE comment IS '댓글'; -- 컬럼 코멘트 COMMENT ON COLUMN comment.content IS '내용'; +COMMENT ON COLUMN comment.board_id IS '게시물 ID'; COMMENT ON COLUMN comment.status IS '상태'; COMMENT ON COLUMN comment.created_at IS '생성시간'; COMMENT ON COLUMN comment.updated_at IS '마지막 수정시간'; \ No newline at end of file diff --git a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_4__create_tb_reply.sql b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_4__create_tb_reply.sql index 7261726..5e3ef33 100644 --- a/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_4__create_tb_reply.sql +++ b/services/comment/driven/rdb/src/main/resources/db/postgresql/migration/v1_0/V1_0_4__create_tb_reply.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS reply ( id BIGSERIAL, - parent_id BIGSERIAL, + comment_id BIGINT, content VARCHAR(255), status VARCHAR(255), created_at TIMESTAMP DEFAULT NOW(), @@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS reply ( COMMENT ON TABLE reply IS '답글'; -- 컬럼 코멘트 -COMMENT ON COLUMN reply.parent_id IS '부모 댓글 ID'; +COMMENT ON COLUMN reply.comment_id IS '부모 댓글 ID'; COMMENT ON COLUMN reply.content IS '내용'; COMMENT ON COLUMN reply.status IS '상태'; COMMENT ON COLUMN reply.created_at IS '생성시간'; From 7097736cfa02132bd72666c4d3b090ff867d3bdb Mon Sep 17 00:00:00 2001 From: seonghooni Date: Mon, 19 May 2025 23:13:48 +0900 Subject: [PATCH 22/25] feat(comment/reply): add command service, query service --- services/comment/application/build.gradle.kts | 1 + .../service/CommentCommandService.java | 2 ++ .../comment/service/CommentQueryService.java | 31 ++++++++++++++----- .../reply/service/ReplyCommandService.java | 2 ++ .../reply/service/ReplyQueryService.java | 6 ++-- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/services/comment/application/build.gradle.kts b/services/comment/application/build.gradle.kts index d873068..652be2d 100644 --- a/services/comment/application/build.gradle.kts +++ b/services/comment/application/build.gradle.kts @@ -1,3 +1,4 @@ dependencies { api(project(":comment:comment-api")) + implementation("org.springframework.boot:spring-boot-starter-data-jpa") } \ No newline at end of file diff --git a/services/comment/application/src/main/java/nettee/comment/service/CommentCommandService.java b/services/comment/application/src/main/java/nettee/comment/service/CommentCommandService.java index cab0310..bab875e 100644 --- a/services/comment/application/src/main/java/nettee/comment/service/CommentCommandService.java +++ b/services/comment/application/src/main/java/nettee/comment/service/CommentCommandService.java @@ -8,9 +8,11 @@ import nettee.comment.usecase.CommentDeleteUseCase; import nettee.comment.usecase.CommentUpdateUseCase; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor +@Transactional public class CommentCommandService implements CommentCreateUseCase, CommentUpdateUseCase, CommentDeleteUseCase { private final CommentCommandRepositoryPort commentCommandRepositoryPort; diff --git a/services/comment/application/src/main/java/nettee/comment/service/CommentQueryService.java b/services/comment/application/src/main/java/nettee/comment/service/CommentQueryService.java index 486251e..f84dff5 100644 --- a/services/comment/application/src/main/java/nettee/comment/service/CommentQueryService.java +++ b/services/comment/application/src/main/java/nettee/comment/service/CommentQueryService.java @@ -1,10 +1,12 @@ package nettee.comment.service; -import java.time.Instant; import java.util.List; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; -import nettee.comment.Comment; +import nettee.comment.model.CommentQueryModels.CommentDetail; import nettee.comment.port.CommentQueryRepositoryPort; +import nettee.reply.model.ReplyQueryModels.ReplyDetail; +import nettee.reply.port.ReplyQueryRepositoryPort; import org.springframework.stereotype.Service; @Service @@ -12,13 +14,28 @@ public class CommentQueryService { private final CommentQueryRepositoryPort commentQueryRepositoryPort; + private final ReplyQueryRepositoryPort replyQueryRepositoryPort; - public List getCommentListByBoardId(Long boardId) { - return commentQueryRepositoryPort.findPageByBoardId(boardId, 0, 10); - } + public List getCommentsByBoardId(Long boardId) { + var comments = commentQueryRepositoryPort.findPageByBoardId(boardId, 0, 10); + + // comment별로 reply를 10개씩 가져옴 + // 현재 N+1 이므로, 최대 11(1+10)개의 쿼리를 발생시킴 + var result = comments.stream() + .map(comment -> { + var replies = replyQueryRepositoryPort.findPageByCommentId(comment.id(), 0, 10); + + return CommentDetail.builder() + .id(comment.id()) + .content(comment.content()) + .status(comment.status()) + .createdAt(comment.createdAt()) + .updatedAt(comment.updatedAt()) + .replies(replies) + .build(); + }).collect(Collectors.toList()); - public List getCommentListByBoardIdAfter(Long boardId, Instant createdAt) { - return commentQueryRepositoryPort.findPageByBoardIdAfter(boardId, createdAt, 10); + return result; } } diff --git a/services/comment/application/src/main/java/nettee/reply/service/ReplyCommandService.java b/services/comment/application/src/main/java/nettee/reply/service/ReplyCommandService.java index d066de6..f4dbb1d 100644 --- a/services/comment/application/src/main/java/nettee/reply/service/ReplyCommandService.java +++ b/services/comment/application/src/main/java/nettee/reply/service/ReplyCommandService.java @@ -1,5 +1,6 @@ package nettee.reply.service; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import nettee.reply.Reply; import nettee.reply.port.ReplyCommandRepositoryPort; @@ -11,6 +12,7 @@ @Service @RequiredArgsConstructor +@Transactional public class ReplyCommandService implements ReplyCreateUseCase, ReplyUpdateUseCase, ReplyDeleteUseCase { private final ReplyCommandRepositoryPort replyCommandRepositoryPort; diff --git a/services/comment/application/src/main/java/nettee/reply/service/ReplyQueryService.java b/services/comment/application/src/main/java/nettee/reply/service/ReplyQueryService.java index e50f0da..110931a 100644 --- a/services/comment/application/src/main/java/nettee/reply/service/ReplyQueryService.java +++ b/services/comment/application/src/main/java/nettee/reply/service/ReplyQueryService.java @@ -3,7 +3,7 @@ import java.time.Instant; import java.util.List; import lombok.RequiredArgsConstructor; -import nettee.reply.Reply; +import nettee.reply.model.ReplyQueryModels.ReplyDetail; import nettee.reply.port.ReplyQueryRepositoryPort; import org.springframework.stereotype.Service; @@ -13,11 +13,11 @@ public class ReplyQueryService { private final ReplyQueryRepositoryPort replyQueryRepositoryPort; - public List getReplyListByCommentId(Long commentId) { + public List getReplyListByCommentId(Long commentId) { return replyQueryRepositoryPort.findPageByCommentId(commentId, 0, 10); } - public List getReplyListByCommentIdAfter(Long commentId, Instant createdAt) { + public List getReplyListByCommentIdAfter(Long commentId, Instant createdAt) { return replyQueryRepositoryPort.findPageByCommentIdAfter(commentId, createdAt, 10); } From a990533ebb8bd748e42e1059ba827753d36d480f Mon Sep 17 00:00:00 2001 From: seonghooni Date: Mon, 19 May 2025 23:14:48 +0900 Subject: [PATCH 23/25] feat(comment/reply): add command api, query api, dto to web-mvc module --- .../comment/driving/web-mvc/build.gradle.kts | 15 +++++ .../nettee/comment/web/CommentCommandApi.java | 60 +++++++++++++++++++ .../nettee/comment/web/CommentQueryApi.java | 24 ++++++++ .../comment/web/dto/CommentCommandDto.java | 46 ++++++++++++++ .../comment/web/mapper/CommentDtoMapper.java | 12 ++++ .../nettee/reply/web/ReplyCommandApi.java | 60 +++++++++++++++++++ .../java/nettee/reply/web/ReplyQueryApi.java | 30 ++++++++++ .../nettee/reply/web/dto/ReplyCommandDto.java | 46 ++++++++++++++ .../reply/web/mapper/ReplyDtoMapper.java | 12 ++++ .../src/main/resources/comment-web.yml | 3 + 10 files changed, 308 insertions(+) create mode 100644 services/comment/driving/web-mvc/build.gradle.kts create mode 100644 services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentCommandApi.java create mode 100644 services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentQueryApi.java create mode 100644 services/comment/driving/web-mvc/src/main/java/nettee/comment/web/dto/CommentCommandDto.java create mode 100644 services/comment/driving/web-mvc/src/main/java/nettee/comment/web/mapper/CommentDtoMapper.java create mode 100644 services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyCommandApi.java create mode 100644 services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyQueryApi.java create mode 100644 services/comment/driving/web-mvc/src/main/java/nettee/reply/web/dto/ReplyCommandDto.java create mode 100644 services/comment/driving/web-mvc/src/main/java/nettee/reply/web/mapper/ReplyDtoMapper.java create mode 100644 services/comment/driving/web-mvc/src/main/resources/comment-web.yml diff --git a/services/comment/driving/web-mvc/build.gradle.kts b/services/comment/driving/web-mvc/build.gradle.kts new file mode 100644 index 0000000..ceac9d9 --- /dev/null +++ b/services/comment/driving/web-mvc/build.gradle.kts @@ -0,0 +1,15 @@ +val commentApplication: String by project + +dependencies { + api(project(commentApplication)) + + // validation + compileOnly("jakarta.validation:jakarta.validation-api") + compileOnly("jakarta.annotation:jakarta.annotation-api") + + // mapstruct + compileOnly("org.mapstruct:mapstruct:1.6.3") + annotationProcessor("org.mapstruct:mapstruct-processor:1.6.3") + annotationProcessor("org.projectlombok:lombok-mapstruct-binding:0.2.0") +} + diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentCommandApi.java b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentCommandApi.java new file mode 100644 index 0000000..9838c4b --- /dev/null +++ b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentCommandApi.java @@ -0,0 +1,60 @@ +package nettee.comment.web; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import nettee.comment.usecase.CommentCreateUseCase; +import nettee.comment.usecase.CommentDeleteUseCase; +import nettee.comment.usecase.CommentUpdateUseCase; +import nettee.comment.web.dto.CommentCommandDto.CommentCommandResponse; +import nettee.comment.web.dto.CommentCommandDto.CommentCreateCommand; +import nettee.comment.web.dto.CommentCommandDto.CommentUpdateCommand; +import nettee.comment.web.mapper.CommentDtoMapper; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/comments") +@RequiredArgsConstructor +public class CommentCommandApi { + + private final CommentCreateUseCase commentCreateUseCase; + private final CommentUpdateUseCase commentUpdateUseCase; + private final CommentDeleteUseCase commentDeleteUseCase; + private final CommentDtoMapper mapper; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public CommentCommandResponse create( + @RequestBody @Valid CommentCreateCommand command + ) { + var comment = mapper.toDomain(command); + return CommentCommandResponse.builder() + .comment(commentCreateUseCase.createComment(comment)) + .build(); + } + + @PatchMapping("/{id}") + @ResponseStatus(HttpStatus.OK) + public CommentCommandResponse update( + @PathVariable("id") Long id, + @RequestBody @Valid CommentUpdateCommand command + ) { + var comment = mapper.toDomain(command); + return CommentCommandResponse.builder() + .comment(commentUpdateUseCase.updateComment(comment)) + .build(); + } + + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void delete(@PathVariable("id") Long id) { + commentDeleteUseCase.deleteComment(id); + } +} diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentQueryApi.java b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentQueryApi.java new file mode 100644 index 0000000..096cb72 --- /dev/null +++ b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentQueryApi.java @@ -0,0 +1,24 @@ +package nettee.comment.web; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import nettee.comment.model.CommentQueryModels.CommentDetail; +import nettee.comment.service.CommentQueryService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/comments") +@RequiredArgsConstructor +public class CommentQueryApi { + + private final CommentQueryService commentQueryService; + + @GetMapping("/{boardId}") + public List getCommentsByBoardId(@PathVariable("boardId") Long boardId) { + return commentQueryService.getCommentsByBoardId(boardId); + } + +} diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/dto/CommentCommandDto.java b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/dto/CommentCommandDto.java new file mode 100644 index 0000000..5cd3571 --- /dev/null +++ b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/dto/CommentCommandDto.java @@ -0,0 +1,46 @@ +package nettee.comment.web.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import nettee.comment.Comment; +import nettee.comment.type.CommentStatus; + +public class CommentCommandDto { + + private CommentCommandDto() { + + } + + @Builder + public record CommentCreateCommand( + @NotNull(message = "boardId를 입력하십시오") + Long boardId, + @NotBlank(message = "본문을 입력하십시오") + String content, + @NotNull(message = "상태를 입력하십시오") + CommentStatus status + ) { + + } + + @Builder + public record CommentUpdateCommand( + @NotNull(message = "id를 입력하십시오") + Long id, + @NotBlank(message = "본문을 입력하십시오") + String content, + @NotNull(message = "상태를 입력하십시오") + CommentStatus status + ){ + + } + + @Builder + public record CommentCommandResponse( + Comment comment + ){ + + } + +} diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/mapper/CommentDtoMapper.java b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/mapper/CommentDtoMapper.java new file mode 100644 index 0000000..57c3e3b --- /dev/null +++ b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/mapper/CommentDtoMapper.java @@ -0,0 +1,12 @@ +package nettee.comment.web.mapper; + +import nettee.comment.Comment; +import nettee.comment.web.dto.CommentCommandDto.CommentCreateCommand; +import nettee.comment.web.dto.CommentCommandDto.CommentUpdateCommand; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface CommentDtoMapper { + Comment toDomain(CommentCreateCommand command); + Comment toDomain(CommentUpdateCommand command); +} diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyCommandApi.java b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyCommandApi.java new file mode 100644 index 0000000..29c3ec3 --- /dev/null +++ b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyCommandApi.java @@ -0,0 +1,60 @@ +package nettee.reply.web; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import nettee.reply.usecase.ReplyCreateUseCase; +import nettee.reply.usecase.ReplyDeleteUseCase; +import nettee.reply.usecase.ReplyUpdateUseCase; +import nettee.reply.web.dto.ReplyCommandDto.ReplyCommandResponse; +import nettee.reply.web.dto.ReplyCommandDto.ReplyCreateCommand; +import nettee.reply.web.dto.ReplyCommandDto.ReplyUpdateCommand; +import nettee.reply.web.mapper.ReplyDtoMapper; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/replies") +@RequiredArgsConstructor +public class ReplyCommandApi { + + private final ReplyCreateUseCase replyCreateUseCase; + private final ReplyUpdateUseCase replyUpdateUseCase; + private final ReplyDeleteUseCase replyDeleteUseCase; + private final ReplyDtoMapper mapper; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public ReplyCommandResponse create( + @RequestBody @Valid ReplyCreateCommand command + ) { + var reply = mapper.toDomain(command); + return ReplyCommandResponse.builder() + .reply(replyCreateUseCase.createReply(reply)) + .build(); + } + + @PatchMapping("/{id}") + @ResponseStatus(HttpStatus.OK) + public ReplyCommandResponse update( + @PathVariable("id") Long id, + @RequestBody @Valid ReplyUpdateCommand command + ) { + var reply = mapper.toDomain(command); + return ReplyCommandResponse.builder() + .reply(replyUpdateUseCase.updateReply(reply)) + .build(); + } + + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void delete(@PathVariable("id") Long id) { + replyDeleteUseCase.deleteReply(id); + } +} diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyQueryApi.java b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyQueryApi.java new file mode 100644 index 0000000..5c5eec4 --- /dev/null +++ b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyQueryApi.java @@ -0,0 +1,30 @@ +package nettee.reply.web; + +import java.time.Instant; +import java.util.List; +import lombok.RequiredArgsConstructor; +import nettee.reply.model.ReplyQueryModels.ReplyDetail; +import nettee.reply.service.ReplyQueryService; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/replies") +@RequiredArgsConstructor +public class ReplyQueryApi { + + private final ReplyQueryService replyQueryService; + + // '답글 더보기' 요청 + @GetMapping("/{commentId}") + public List getRepliesByCommentIdAfter( + @PathVariable("commentId") Long commentId, + @RequestParam("after") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant createdAt + ){ + return replyQueryService.getReplyListByCommentIdAfter(commentId, createdAt); + } +} diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/dto/ReplyCommandDto.java b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/dto/ReplyCommandDto.java new file mode 100644 index 0000000..69f048b --- /dev/null +++ b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/dto/ReplyCommandDto.java @@ -0,0 +1,46 @@ +package nettee.reply.web.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import nettee.reply.Reply; +import nettee.reply.type.ReplyStatus; + +public class ReplyCommandDto { + + private ReplyCommandDto() { + + } + + @Builder + public record ReplyCreateCommand( + @NotNull(message = "commentId를 입력하십시오") + Long commentId, + @NotBlank(message = "본문을 입력하십시오") + String content, + @NotNull(message = "상태를 입력하십시오") + ReplyStatus status + ){ + + } + + @Builder + public record ReplyUpdateCommand( + @NotNull(message = "id를 입력하십시오") + Long id, + @NotBlank(message = "본문을 입력하십시오") + String content, + @NotNull(message = "상태를 입력하십시오") + ReplyStatus status + ){ + + } + + @Builder + public record ReplyCommandResponse( + Reply reply + ) { + + } + +} diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/mapper/ReplyDtoMapper.java b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/mapper/ReplyDtoMapper.java new file mode 100644 index 0000000..c400914 --- /dev/null +++ b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/mapper/ReplyDtoMapper.java @@ -0,0 +1,12 @@ +package nettee.reply.web.mapper; + +import nettee.reply.Reply; +import nettee.reply.web.dto.ReplyCommandDto.ReplyCreateCommand; +import nettee.reply.web.dto.ReplyCommandDto.ReplyUpdateCommand; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface ReplyDtoMapper { + Reply toDomain(ReplyCreateCommand command); + Reply toDomain(ReplyUpdateCommand command); +} diff --git a/services/comment/driving/web-mvc/src/main/resources/comment-web.yml b/services/comment/driving/web-mvc/src/main/resources/comment-web.yml new file mode 100644 index 0000000..8b7a89c --- /dev/null +++ b/services/comment/driving/web-mvc/src/main/resources/comment-web.yml @@ -0,0 +1,3 @@ +spring: + jackson: + default-property-inclusion: non_null \ No newline at end of file From d0062f71616230dd98a6470ce4f86e951ddcf607 Mon Sep 17 00:00:00 2001 From: seonghooni Date: Mon, 19 May 2025 23:32:06 +0900 Subject: [PATCH 24/25] refactor(comment/reply): modify base-package according to #86 --- .../nettee/comment/{ => domain}/Comment.java | 4 ++-- .../{ => domain}/type/CommentStatus.java | 2 +- .../java/nettee/reply/{ => domain}/Reply.java | 4 ++-- .../reply/{ => domain}/type/ReplyStatus.java | 2 +- .../CommentCommandErrorCode.java | 2 +- .../CommentCommandException.java | 2 +- .../{ => exception}/CommentQueryErrorCode.java | 2 +- .../{ => exception}/CommentQueryException.java | 2 +- .../{ => exception}/ReplyCommandErrorCode.java | 2 +- .../{ => exception}/ReplyCommandException.java | 2 +- .../{ => exception}/ReplyQueryErrorCode.java | 2 +- .../{ => exception}/ReplyQueryException.java | 2 +- .../comment/model/CommentQueryModels.java | 2 +- .../nettee/reply/model/ReplyQueryModels.java | 2 +- .../port/CommentCommandRepositoryPort.java | 6 +++--- .../port/CommentQueryRepositoryPort.java | 3 +-- .../service/CommentCommandService.java | 17 +++++++++-------- .../service/CommentQueryService.java | 7 +++---- .../usecase/CommentCreateUseCase.java | 4 ++-- .../usecase/CommentDeleteUseCase.java | 7 +++++++ .../usecase/CommentUpdateUseCase.java | 4 ++-- .../comment/usecase/CommentDeleteUseCase.java | 7 ------- .../port/ReplyCommandRepositoryPort.java | 6 +++--- .../port/ReplyQueryRepositoryPort.java | 3 +-- .../service/ReplyCommandService.java | 14 +++++++------- .../service/ReplyQueryService.java | 4 ++-- .../usecase/ReplyCreateUseCase.java | 4 ++-- .../usecase/ReplyDeleteUseCase.java | 2 +- .../usecase/ReplyUpdateUseCase.java | 4 ++-- .../{ => driven/rdb}/entity/CommentEntity.java | 7 +++---- .../rdb}/entity/type/CommentEntityStatus.java | 6 +++--- .../type/CommentEntityStatusConverter.java | 2 +- .../rdb}/persistence/CommentCommandAdapter.java | 16 ++++++++-------- .../rdb}/persistence/CommentJpaRepository.java | 4 ++-- .../rdb}/persistence/CommentQueryAdapter.java | 12 +++++------- .../persistence/mapper/CommentEntityMapper.java | 6 +++--- .../{ => driven/rdb}/entity/ReplyEntity.java | 7 +++---- .../rdb}/entity/type/ReplyEntityStatus.java | 6 +++--- .../entity/type/ReplyEntityStatusConverter.java | 2 +- .../rdb}/persistence/ReplyCommandAdapter.java | 16 ++++++++-------- .../rdb}/persistence/ReplyJpaRepository.java | 4 ++-- .../rdb}/persistence/ReplyQueryAdapter.java | 10 +++++----- .../persistence/mapper/ReplyEntityMapper.java | 6 +++--- .../nettee/comment/web/CommentCommandApi.java | 6 +++--- .../nettee/comment/web/CommentQueryApi.java | 2 +- .../comment/web/dto/CommentCommandDto.java | 4 ++-- .../comment/web/mapper/CommentDtoMapper.java | 2 +- .../java/nettee/reply/web/ReplyCommandApi.java | 6 +++--- .../java/nettee/reply/web/ReplyQueryApi.java | 2 +- .../nettee/reply/web/dto/ReplyCommandDto.java | 4 ++-- .../nettee/reply/web/mapper/ReplyDtoMapper.java | 2 +- 51 files changed, 125 insertions(+), 131 deletions(-) rename services/comment/api/domain/src/main/java/nettee/comment/{ => domain}/Comment.java (91%) rename services/comment/api/domain/src/main/java/nettee/comment/{ => domain}/type/CommentStatus.java (89%) rename services/comment/api/domain/src/main/java/nettee/reply/{ => domain}/Reply.java (91%) rename services/comment/api/domain/src/main/java/nettee/reply/{ => domain}/type/ReplyStatus.java (90%) rename services/comment/api/exception/src/main/java/nettee/comment/{ => exception}/CommentCommandErrorCode.java (98%) rename services/comment/api/exception/src/main/java/nettee/comment/{ => exception}/CommentCommandException.java (96%) rename services/comment/api/exception/src/main/java/nettee/comment/{ => exception}/CommentQueryErrorCode.java (98%) rename services/comment/api/exception/src/main/java/nettee/comment/{ => exception}/CommentQueryException.java (96%) rename services/comment/api/exception/src/main/java/nettee/reply/{ => exception}/ReplyCommandErrorCode.java (98%) rename services/comment/api/exception/src/main/java/nettee/reply/{ => exception}/ReplyCommandException.java (97%) rename services/comment/api/exception/src/main/java/nettee/reply/{ => exception}/ReplyQueryErrorCode.java (98%) rename services/comment/api/exception/src/main/java/nettee/reply/{ => exception}/ReplyQueryException.java (96%) rename services/comment/application/src/main/java/nettee/comment/{ => application}/port/CommentCommandRepositoryPort.java (58%) rename services/comment/application/src/main/java/nettee/comment/{ => application}/port/CommentQueryRepositoryPort.java (86%) rename services/comment/application/src/main/java/nettee/comment/{ => application}/service/CommentCommandService.java (62%) rename services/comment/application/src/main/java/nettee/comment/{ => application}/service/CommentQueryService.java (87%) rename services/comment/application/src/main/java/nettee/comment/{ => application}/usecase/CommentCreateUseCase.java (51%) create mode 100644 services/comment/application/src/main/java/nettee/comment/application/usecase/CommentDeleteUseCase.java rename services/comment/application/src/main/java/nettee/comment/{ => application}/usecase/CommentUpdateUseCase.java (51%) delete mode 100644 services/comment/application/src/main/java/nettee/comment/usecase/CommentDeleteUseCase.java rename services/comment/application/src/main/java/nettee/reply/{ => application}/port/ReplyCommandRepositoryPort.java (58%) rename services/comment/application/src/main/java/nettee/reply/{ => application}/port/ReplyQueryRepositoryPort.java (90%) rename services/comment/application/src/main/java/nettee/reply/{ => application}/service/ReplyCommandService.java (66%) rename services/comment/application/src/main/java/nettee/reply/{ => application}/service/ReplyQueryService.java (86%) rename services/comment/application/src/main/java/nettee/reply/{ => application}/usecase/ReplyCreateUseCase.java (50%) rename services/comment/application/src/main/java/nettee/reply/{ => application}/usecase/ReplyDeleteUseCase.java (65%) rename services/comment/application/src/main/java/nettee/reply/{ => application}/usecase/ReplyUpdateUseCase.java (52%) rename services/comment/driven/rdb/src/main/java/nettee/comment/{ => driven/rdb}/entity/CommentEntity.java (89%) rename services/comment/driven/rdb/src/main/java/nettee/comment/{ => driven/rdb}/entity/type/CommentEntityStatus.java (95%) rename services/comment/driven/rdb/src/main/java/nettee/comment/{ => driven/rdb}/entity/type/CommentEntityStatusConverter.java (90%) rename services/comment/driven/rdb/src/main/java/nettee/comment/{ => driven/rdb}/persistence/CommentCommandAdapter.java (75%) rename services/comment/driven/rdb/src/main/java/nettee/comment/{ => driven/rdb}/persistence/CommentJpaRepository.java (59%) rename services/comment/driven/rdb/src/main/java/nettee/comment/{ => driven/rdb}/persistence/CommentQueryAdapter.java (83%) rename services/comment/driven/rdb/src/main/java/nettee/comment/{ => driven/rdb}/persistence/mapper/CommentEntityMapper.java (78%) rename services/comment/driven/rdb/src/main/java/nettee/reply/{ => driven/rdb}/entity/ReplyEntity.java (89%) rename services/comment/driven/rdb/src/main/java/nettee/reply/{ => driven/rdb}/entity/type/ReplyEntityStatus.java (96%) rename services/comment/driven/rdb/src/main/java/nettee/reply/{ => driven/rdb}/entity/type/ReplyEntityStatusConverter.java (91%) rename services/comment/driven/rdb/src/main/java/nettee/reply/{ => driven/rdb}/persistence/ReplyCommandAdapter.java (75%) rename services/comment/driven/rdb/src/main/java/nettee/reply/{ => driven/rdb}/persistence/ReplyJpaRepository.java (60%) rename services/comment/driven/rdb/src/main/java/nettee/reply/{ => driven/rdb}/persistence/ReplyQueryAdapter.java (88%) rename services/comment/driven/rdb/src/main/java/nettee/reply/{ => driven/rdb}/persistence/mapper/ReplyEntityMapper.java (77%) diff --git a/services/comment/api/domain/src/main/java/nettee/comment/Comment.java b/services/comment/api/domain/src/main/java/nettee/comment/domain/Comment.java similarity index 91% rename from services/comment/api/domain/src/main/java/nettee/comment/Comment.java rename to services/comment/api/domain/src/main/java/nettee/comment/domain/Comment.java index 65683ee..50547c3 100644 --- a/services/comment/api/domain/src/main/java/nettee/comment/Comment.java +++ b/services/comment/api/domain/src/main/java/nettee/comment/domain/Comment.java @@ -1,4 +1,4 @@ -package nettee.comment; +package nettee.comment.domain; import java.time.Instant; import java.util.Objects; @@ -6,7 +6,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import nettee.comment.type.CommentStatus; +import nettee.comment.domain.type.CommentStatus; @Getter @Builder diff --git a/services/comment/api/domain/src/main/java/nettee/comment/type/CommentStatus.java b/services/comment/api/domain/src/main/java/nettee/comment/domain/type/CommentStatus.java similarity index 89% rename from services/comment/api/domain/src/main/java/nettee/comment/type/CommentStatus.java rename to services/comment/api/domain/src/main/java/nettee/comment/domain/type/CommentStatus.java index 323397e..e8e64e6 100644 --- a/services/comment/api/domain/src/main/java/nettee/comment/type/CommentStatus.java +++ b/services/comment/api/domain/src/main/java/nettee/comment/domain/type/CommentStatus.java @@ -1,4 +1,4 @@ -package nettee.comment.type; +package nettee.comment.domain.type; import java.util.EnumSet; import java.util.Set; diff --git a/services/comment/api/domain/src/main/java/nettee/reply/Reply.java b/services/comment/api/domain/src/main/java/nettee/reply/domain/Reply.java similarity index 91% rename from services/comment/api/domain/src/main/java/nettee/reply/Reply.java rename to services/comment/api/domain/src/main/java/nettee/reply/domain/Reply.java index 4316089..4830c8e 100644 --- a/services/comment/api/domain/src/main/java/nettee/reply/Reply.java +++ b/services/comment/api/domain/src/main/java/nettee/reply/domain/Reply.java @@ -1,4 +1,4 @@ -package nettee.reply; +package nettee.reply.domain; import java.time.Instant; import java.util.Objects; @@ -6,7 +6,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import nettee.reply.type.ReplyStatus; +import nettee.reply.domain.type.ReplyStatus; @Getter @Builder diff --git a/services/comment/api/domain/src/main/java/nettee/reply/type/ReplyStatus.java b/services/comment/api/domain/src/main/java/nettee/reply/domain/type/ReplyStatus.java similarity index 90% rename from services/comment/api/domain/src/main/java/nettee/reply/type/ReplyStatus.java rename to services/comment/api/domain/src/main/java/nettee/reply/domain/type/ReplyStatus.java index 25b13b5..90e9e9a 100644 --- a/services/comment/api/domain/src/main/java/nettee/reply/type/ReplyStatus.java +++ b/services/comment/api/domain/src/main/java/nettee/reply/domain/type/ReplyStatus.java @@ -1,4 +1,4 @@ -package nettee.reply.type; +package nettee.reply.domain.type; import java.util.EnumSet; import java.util.Set; diff --git a/services/comment/api/exception/src/main/java/nettee/comment/CommentCommandErrorCode.java b/services/comment/api/exception/src/main/java/nettee/comment/exception/CommentCommandErrorCode.java similarity index 98% rename from services/comment/api/exception/src/main/java/nettee/comment/CommentCommandErrorCode.java rename to services/comment/api/exception/src/main/java/nettee/comment/exception/CommentCommandErrorCode.java index d5431f6..5061370 100644 --- a/services/comment/api/exception/src/main/java/nettee/comment/CommentCommandErrorCode.java +++ b/services/comment/api/exception/src/main/java/nettee/comment/exception/CommentCommandErrorCode.java @@ -1,4 +1,4 @@ -package nettee.comment; +package nettee.comment.exception; import java.util.Map; import java.util.function.Supplier; diff --git a/services/comment/api/exception/src/main/java/nettee/comment/CommentCommandException.java b/services/comment/api/exception/src/main/java/nettee/comment/exception/CommentCommandException.java similarity index 96% rename from services/comment/api/exception/src/main/java/nettee/comment/CommentCommandException.java rename to services/comment/api/exception/src/main/java/nettee/comment/exception/CommentCommandException.java index 2d517a4..d6e01ff 100644 --- a/services/comment/api/exception/src/main/java/nettee/comment/CommentCommandException.java +++ b/services/comment/api/exception/src/main/java/nettee/comment/exception/CommentCommandException.java @@ -1,4 +1,4 @@ -package nettee.comment; +package nettee.comment.exception; import java.util.Map; import java.util.function.Supplier; diff --git a/services/comment/api/exception/src/main/java/nettee/comment/CommentQueryErrorCode.java b/services/comment/api/exception/src/main/java/nettee/comment/exception/CommentQueryErrorCode.java similarity index 98% rename from services/comment/api/exception/src/main/java/nettee/comment/CommentQueryErrorCode.java rename to services/comment/api/exception/src/main/java/nettee/comment/exception/CommentQueryErrorCode.java index 100bc06..1529b31 100644 --- a/services/comment/api/exception/src/main/java/nettee/comment/CommentQueryErrorCode.java +++ b/services/comment/api/exception/src/main/java/nettee/comment/exception/CommentQueryErrorCode.java @@ -1,4 +1,4 @@ -package nettee.comment; +package nettee.comment.exception; import java.util.Map; import java.util.function.Supplier; diff --git a/services/comment/api/exception/src/main/java/nettee/comment/CommentQueryException.java b/services/comment/api/exception/src/main/java/nettee/comment/exception/CommentQueryException.java similarity index 96% rename from services/comment/api/exception/src/main/java/nettee/comment/CommentQueryException.java rename to services/comment/api/exception/src/main/java/nettee/comment/exception/CommentQueryException.java index 5d35896..a5455f0 100644 --- a/services/comment/api/exception/src/main/java/nettee/comment/CommentQueryException.java +++ b/services/comment/api/exception/src/main/java/nettee/comment/exception/CommentQueryException.java @@ -1,4 +1,4 @@ -package nettee.comment; +package nettee.comment.exception; import java.util.Map; import java.util.function.Supplier; diff --git a/services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandErrorCode.java b/services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyCommandErrorCode.java similarity index 98% rename from services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandErrorCode.java rename to services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyCommandErrorCode.java index b3f0010..110e1d0 100644 --- a/services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandErrorCode.java +++ b/services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyCommandErrorCode.java @@ -1,4 +1,4 @@ -package nettee.reply; +package nettee.reply.exception; import java.util.Map; import java.util.function.Supplier; diff --git a/services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandException.java b/services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyCommandException.java similarity index 97% rename from services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandException.java rename to services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyCommandException.java index 414f12a..d9c740c 100644 --- a/services/comment/api/exception/src/main/java/nettee/reply/ReplyCommandException.java +++ b/services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyCommandException.java @@ -1,4 +1,4 @@ -package nettee.reply; +package nettee.reply.exception; import java.util.Map; import java.util.function.Supplier; diff --git a/services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryErrorCode.java b/services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyQueryErrorCode.java similarity index 98% rename from services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryErrorCode.java rename to services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyQueryErrorCode.java index 91bd5c9..f8eb5c8 100644 --- a/services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryErrorCode.java +++ b/services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyQueryErrorCode.java @@ -1,4 +1,4 @@ -package nettee.reply; +package nettee.reply.exception; import java.util.Map; import java.util.function.Supplier; diff --git a/services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryException.java b/services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyQueryException.java similarity index 96% rename from services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryException.java rename to services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyQueryException.java index 0f87edf..f2fbdfd 100644 --- a/services/comment/api/exception/src/main/java/nettee/reply/ReplyQueryException.java +++ b/services/comment/api/exception/src/main/java/nettee/reply/exception/ReplyQueryException.java @@ -1,4 +1,4 @@ -package nettee.reply; +package nettee.reply.exception; import java.util.Map; import java.util.function.Supplier; diff --git a/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java b/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java index 03f9500..936cd78 100644 --- a/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java +++ b/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java @@ -4,7 +4,7 @@ import lombok.Builder; import java.time.Instant; -import nettee.comment.type.CommentStatus; +import nettee.comment.domain.type.CommentStatus; import nettee.reply.model.ReplyQueryModels.ReplyDetail; public final class CommentQueryModels { diff --git a/services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java b/services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java index f5f78a1..c0cf3c5 100644 --- a/services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java +++ b/services/comment/api/readmodel/src/main/java/nettee/reply/model/ReplyQueryModels.java @@ -2,7 +2,7 @@ import java.time.Instant; import lombok.Builder; -import nettee.reply.type.ReplyStatus; +import nettee.reply.domain.type.ReplyStatus; public final class ReplyQueryModels { diff --git a/services/comment/application/src/main/java/nettee/comment/port/CommentCommandRepositoryPort.java b/services/comment/application/src/main/java/nettee/comment/application/port/CommentCommandRepositoryPort.java similarity index 58% rename from services/comment/application/src/main/java/nettee/comment/port/CommentCommandRepositoryPort.java rename to services/comment/application/src/main/java/nettee/comment/application/port/CommentCommandRepositoryPort.java index 452305f..f93c888 100644 --- a/services/comment/application/src/main/java/nettee/comment/port/CommentCommandRepositoryPort.java +++ b/services/comment/application/src/main/java/nettee/comment/application/port/CommentCommandRepositoryPort.java @@ -1,7 +1,7 @@ -package nettee.comment.port; +package nettee.comment.application.port; -import nettee.comment.Comment; -import nettee.comment.type.CommentStatus; +import nettee.comment.domain.Comment; +import nettee.comment.domain.type.CommentStatus; public interface CommentCommandRepositoryPort { diff --git a/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java b/services/comment/application/src/main/java/nettee/comment/application/port/CommentQueryRepositoryPort.java similarity index 86% rename from services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java rename to services/comment/application/src/main/java/nettee/comment/application/port/CommentQueryRepositoryPort.java index f1e70f1..39b81da 100644 --- a/services/comment/application/src/main/java/nettee/comment/port/CommentQueryRepositoryPort.java +++ b/services/comment/application/src/main/java/nettee/comment/application/port/CommentQueryRepositoryPort.java @@ -1,9 +1,8 @@ -package nettee.comment.port; +package nettee.comment.application.port; import java.time.Instant; import java.util.List; import java.util.Optional; -import nettee.comment.Comment; import nettee.comment.model.CommentQueryModels.CommentDetail; public interface CommentQueryRepositoryPort { diff --git a/services/comment/application/src/main/java/nettee/comment/service/CommentCommandService.java b/services/comment/application/src/main/java/nettee/comment/application/service/CommentCommandService.java similarity index 62% rename from services/comment/application/src/main/java/nettee/comment/service/CommentCommandService.java rename to services/comment/application/src/main/java/nettee/comment/application/service/CommentCommandService.java index bab875e..aef56ea 100644 --- a/services/comment/application/src/main/java/nettee/comment/service/CommentCommandService.java +++ b/services/comment/application/src/main/java/nettee/comment/application/service/CommentCommandService.java @@ -1,19 +1,20 @@ -package nettee.comment.service; +package nettee.comment.application.service; import lombok.RequiredArgsConstructor; -import nettee.comment.Comment; -import nettee.comment.port.CommentCommandRepositoryPort; -import nettee.comment.type.CommentStatus; -import nettee.comment.usecase.CommentCreateUseCase; -import nettee.comment.usecase.CommentDeleteUseCase; -import nettee.comment.usecase.CommentUpdateUseCase; +import nettee.comment.application.usecase.CommentDeleteUseCase; +import nettee.comment.domain.Comment; +import nettee.comment.application.port.CommentCommandRepositoryPort; +import nettee.comment.domain.type.CommentStatus; +import nettee.comment.application.usecase.CommentCreateUseCase; +import nettee.comment.application.usecase.CommentUpdateUseCase; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor @Transactional -public class CommentCommandService implements CommentCreateUseCase, CommentUpdateUseCase, CommentDeleteUseCase { +public class CommentCommandService implements CommentCreateUseCase, CommentUpdateUseCase, + CommentDeleteUseCase { private final CommentCommandRepositoryPort commentCommandRepositoryPort; diff --git a/services/comment/application/src/main/java/nettee/comment/service/CommentQueryService.java b/services/comment/application/src/main/java/nettee/comment/application/service/CommentQueryService.java similarity index 87% rename from services/comment/application/src/main/java/nettee/comment/service/CommentQueryService.java rename to services/comment/application/src/main/java/nettee/comment/application/service/CommentQueryService.java index f84dff5..5736144 100644 --- a/services/comment/application/src/main/java/nettee/comment/service/CommentQueryService.java +++ b/services/comment/application/src/main/java/nettee/comment/application/service/CommentQueryService.java @@ -1,12 +1,11 @@ -package nettee.comment.service; +package nettee.comment.application.service; import java.util.List; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import nettee.comment.model.CommentQueryModels.CommentDetail; -import nettee.comment.port.CommentQueryRepositoryPort; -import nettee.reply.model.ReplyQueryModels.ReplyDetail; -import nettee.reply.port.ReplyQueryRepositoryPort; +import nettee.comment.application.port.CommentQueryRepositoryPort; +import nettee.reply.application.port.ReplyQueryRepositoryPort; import org.springframework.stereotype.Service; @Service diff --git a/services/comment/application/src/main/java/nettee/comment/usecase/CommentCreateUseCase.java b/services/comment/application/src/main/java/nettee/comment/application/usecase/CommentCreateUseCase.java similarity index 51% rename from services/comment/application/src/main/java/nettee/comment/usecase/CommentCreateUseCase.java rename to services/comment/application/src/main/java/nettee/comment/application/usecase/CommentCreateUseCase.java index da1ef82..248350a 100644 --- a/services/comment/application/src/main/java/nettee/comment/usecase/CommentCreateUseCase.java +++ b/services/comment/application/src/main/java/nettee/comment/application/usecase/CommentCreateUseCase.java @@ -1,6 +1,6 @@ -package nettee.comment.usecase; +package nettee.comment.application.usecase; -import nettee.comment.Comment; +import nettee.comment.domain.Comment; public interface CommentCreateUseCase { Comment createComment(Comment comment); diff --git a/services/comment/application/src/main/java/nettee/comment/application/usecase/CommentDeleteUseCase.java b/services/comment/application/src/main/java/nettee/comment/application/usecase/CommentDeleteUseCase.java new file mode 100644 index 0000000..8607c2b --- /dev/null +++ b/services/comment/application/src/main/java/nettee/comment/application/usecase/CommentDeleteUseCase.java @@ -0,0 +1,7 @@ +package nettee.comment.application.usecase; + +import nettee.comment.domain.Comment; + +public interface CommentDeleteUseCase { + void deleteComment(Long id); +} diff --git a/services/comment/application/src/main/java/nettee/comment/usecase/CommentUpdateUseCase.java b/services/comment/application/src/main/java/nettee/comment/application/usecase/CommentUpdateUseCase.java similarity index 51% rename from services/comment/application/src/main/java/nettee/comment/usecase/CommentUpdateUseCase.java rename to services/comment/application/src/main/java/nettee/comment/application/usecase/CommentUpdateUseCase.java index 39dbd3e..514e467 100644 --- a/services/comment/application/src/main/java/nettee/comment/usecase/CommentUpdateUseCase.java +++ b/services/comment/application/src/main/java/nettee/comment/application/usecase/CommentUpdateUseCase.java @@ -1,6 +1,6 @@ -package nettee.comment.usecase; +package nettee.comment.application.usecase; -import nettee.comment.Comment; +import nettee.comment.domain.Comment; public interface CommentUpdateUseCase { Comment updateComment(Comment comment); diff --git a/services/comment/application/src/main/java/nettee/comment/usecase/CommentDeleteUseCase.java b/services/comment/application/src/main/java/nettee/comment/usecase/CommentDeleteUseCase.java deleted file mode 100644 index 47a9d69..0000000 --- a/services/comment/application/src/main/java/nettee/comment/usecase/CommentDeleteUseCase.java +++ /dev/null @@ -1,7 +0,0 @@ -package nettee.comment.usecase; - -import nettee.comment.Comment; - -public interface CommentDeleteUseCase { - void deleteComment(Long id); -} diff --git a/services/comment/application/src/main/java/nettee/reply/port/ReplyCommandRepositoryPort.java b/services/comment/application/src/main/java/nettee/reply/application/port/ReplyCommandRepositoryPort.java similarity index 58% rename from services/comment/application/src/main/java/nettee/reply/port/ReplyCommandRepositoryPort.java rename to services/comment/application/src/main/java/nettee/reply/application/port/ReplyCommandRepositoryPort.java index 1f10d93..fea7b2e 100644 --- a/services/comment/application/src/main/java/nettee/reply/port/ReplyCommandRepositoryPort.java +++ b/services/comment/application/src/main/java/nettee/reply/application/port/ReplyCommandRepositoryPort.java @@ -1,7 +1,7 @@ -package nettee.reply.port; +package nettee.reply.application.port; -import nettee.reply.Reply; -import nettee.reply.type.ReplyStatus; +import nettee.reply.domain.Reply; +import nettee.reply.domain.type.ReplyStatus; public interface ReplyCommandRepositoryPort { diff --git a/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java b/services/comment/application/src/main/java/nettee/reply/application/port/ReplyQueryRepositoryPort.java similarity index 90% rename from services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java rename to services/comment/application/src/main/java/nettee/reply/application/port/ReplyQueryRepositoryPort.java index d682716..e83c6e7 100644 --- a/services/comment/application/src/main/java/nettee/reply/port/ReplyQueryRepositoryPort.java +++ b/services/comment/application/src/main/java/nettee/reply/application/port/ReplyQueryRepositoryPort.java @@ -1,9 +1,8 @@ -package nettee.reply.port; +package nettee.reply.application.port; import java.time.Instant; import java.util.List; import java.util.Optional; -import nettee.reply.Reply; import nettee.reply.model.ReplyQueryModels.ReplyDetail; public interface ReplyQueryRepositoryPort { diff --git a/services/comment/application/src/main/java/nettee/reply/service/ReplyCommandService.java b/services/comment/application/src/main/java/nettee/reply/application/service/ReplyCommandService.java similarity index 66% rename from services/comment/application/src/main/java/nettee/reply/service/ReplyCommandService.java rename to services/comment/application/src/main/java/nettee/reply/application/service/ReplyCommandService.java index f4dbb1d..e53309f 100644 --- a/services/comment/application/src/main/java/nettee/reply/service/ReplyCommandService.java +++ b/services/comment/application/src/main/java/nettee/reply/application/service/ReplyCommandService.java @@ -1,13 +1,13 @@ -package nettee.reply.service; +package nettee.reply.application.service; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; -import nettee.reply.Reply; -import nettee.reply.port.ReplyCommandRepositoryPort; -import nettee.reply.type.ReplyStatus; -import nettee.reply.usecase.ReplyCreateUseCase; -import nettee.reply.usecase.ReplyDeleteUseCase; -import nettee.reply.usecase.ReplyUpdateUseCase; +import nettee.reply.domain.Reply; +import nettee.reply.application.port.ReplyCommandRepositoryPort; +import nettee.reply.domain.type.ReplyStatus; +import nettee.reply.application.usecase.ReplyCreateUseCase; +import nettee.reply.application.usecase.ReplyDeleteUseCase; +import nettee.reply.application.usecase.ReplyUpdateUseCase; import org.springframework.stereotype.Service; @Service diff --git a/services/comment/application/src/main/java/nettee/reply/service/ReplyQueryService.java b/services/comment/application/src/main/java/nettee/reply/application/service/ReplyQueryService.java similarity index 86% rename from services/comment/application/src/main/java/nettee/reply/service/ReplyQueryService.java rename to services/comment/application/src/main/java/nettee/reply/application/service/ReplyQueryService.java index 110931a..0a37e7b 100644 --- a/services/comment/application/src/main/java/nettee/reply/service/ReplyQueryService.java +++ b/services/comment/application/src/main/java/nettee/reply/application/service/ReplyQueryService.java @@ -1,10 +1,10 @@ -package nettee.reply.service; +package nettee.reply.application.service; import java.time.Instant; import java.util.List; import lombok.RequiredArgsConstructor; import nettee.reply.model.ReplyQueryModels.ReplyDetail; -import nettee.reply.port.ReplyQueryRepositoryPort; +import nettee.reply.application.port.ReplyQueryRepositoryPort; import org.springframework.stereotype.Service; @Service diff --git a/services/comment/application/src/main/java/nettee/reply/usecase/ReplyCreateUseCase.java b/services/comment/application/src/main/java/nettee/reply/application/usecase/ReplyCreateUseCase.java similarity index 50% rename from services/comment/application/src/main/java/nettee/reply/usecase/ReplyCreateUseCase.java rename to services/comment/application/src/main/java/nettee/reply/application/usecase/ReplyCreateUseCase.java index a853677..5eecf38 100644 --- a/services/comment/application/src/main/java/nettee/reply/usecase/ReplyCreateUseCase.java +++ b/services/comment/application/src/main/java/nettee/reply/application/usecase/ReplyCreateUseCase.java @@ -1,6 +1,6 @@ -package nettee.reply.usecase; +package nettee.reply.application.usecase; -import nettee.reply.Reply; +import nettee.reply.domain.Reply; public interface ReplyCreateUseCase { Reply createReply(Reply reply); diff --git a/services/comment/application/src/main/java/nettee/reply/usecase/ReplyDeleteUseCase.java b/services/comment/application/src/main/java/nettee/reply/application/usecase/ReplyDeleteUseCase.java similarity index 65% rename from services/comment/application/src/main/java/nettee/reply/usecase/ReplyDeleteUseCase.java rename to services/comment/application/src/main/java/nettee/reply/application/usecase/ReplyDeleteUseCase.java index ddfbd74..8cb3e8a 100644 --- a/services/comment/application/src/main/java/nettee/reply/usecase/ReplyDeleteUseCase.java +++ b/services/comment/application/src/main/java/nettee/reply/application/usecase/ReplyDeleteUseCase.java @@ -1,4 +1,4 @@ -package nettee.reply.usecase; +package nettee.reply.application.usecase; public interface ReplyDeleteUseCase { public void deleteReply(Long id); diff --git a/services/comment/application/src/main/java/nettee/reply/usecase/ReplyUpdateUseCase.java b/services/comment/application/src/main/java/nettee/reply/application/usecase/ReplyUpdateUseCase.java similarity index 52% rename from services/comment/application/src/main/java/nettee/reply/usecase/ReplyUpdateUseCase.java rename to services/comment/application/src/main/java/nettee/reply/application/usecase/ReplyUpdateUseCase.java index 6d698c1..a70ff5e 100644 --- a/services/comment/application/src/main/java/nettee/reply/usecase/ReplyUpdateUseCase.java +++ b/services/comment/application/src/main/java/nettee/reply/application/usecase/ReplyUpdateUseCase.java @@ -1,6 +1,6 @@ -package nettee.reply.usecase; +package nettee.reply.application.usecase; -import nettee.reply.Reply; +import nettee.reply.domain.Reply; public interface ReplyUpdateUseCase { public Reply updateReply(Reply reply); diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/entity/CommentEntity.java similarity index 89% rename from services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java rename to services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/entity/CommentEntity.java index 9a3c02e..eb7f24a 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/CommentEntity.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/entity/CommentEntity.java @@ -1,4 +1,4 @@ -package nettee.comment.entity; +package nettee.comment.driven.rdb.entity; import jakarta.persistence.Column; import jakarta.persistence.Convert; @@ -6,10 +6,9 @@ import java.util.Objects; import lombok.AccessLevel; import lombok.Builder; -import lombok.Getter; import lombok.NoArgsConstructor; -import nettee.comment.entity.type.CommentEntityStatus; -import nettee.comment.entity.type.CommentEntityStatusConverter; +import nettee.comment.driven.rdb.entity.type.CommentEntityStatus; +import nettee.comment.driven.rdb.entity.type.CommentEntityStatusConverter; import nettee.jpa.support.LongBaseTimeEntity; import org.hibernate.annotations.DynamicUpdate; diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatus.java b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/entity/type/CommentEntityStatus.java similarity index 95% rename from services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatus.java rename to services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/entity/type/CommentEntityStatus.java index a52ef89..77ca59c 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatus.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/entity/type/CommentEntityStatus.java @@ -1,12 +1,12 @@ -package nettee.comment.entity.type; +package nettee.comment.driven.rdb.entity.type; -import nettee.comment.type.CommentStatus; +import nettee.comment.domain.type.CommentStatus; import java.util.Arrays; import java.util.Set; import java.util.stream.Collectors; -import static nettee.comment.CommentCommandErrorCode.DEFAULT; +import static nettee.comment.exception.CommentCommandErrorCode.DEFAULT; public enum CommentEntityStatus { REMOVED( diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatusConverter.java b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/entity/type/CommentEntityStatusConverter.java similarity index 90% rename from services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatusConverter.java rename to services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/entity/type/CommentEntityStatusConverter.java index fb47e66..1455550 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/entity/type/CommentEntityStatusConverter.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/entity/type/CommentEntityStatusConverter.java @@ -1,4 +1,4 @@ -package nettee.comment.entity.type; +package nettee.comment.driven.rdb.entity.type; import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentCommandAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/CommentCommandAdapter.java similarity index 75% rename from services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentCommandAdapter.java rename to services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/CommentCommandAdapter.java index bd1e1dd..3ef58d9 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentCommandAdapter.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/CommentCommandAdapter.java @@ -1,16 +1,16 @@ -package nettee.comment.persistence; +package nettee.comment.driven.rdb.persistence; import lombok.RequiredArgsConstructor; -import nettee.comment.Comment; -import nettee.comment.entity.type.CommentEntityStatus; -import nettee.comment.persistence.mapper.CommentEntityMapper; -import nettee.comment.port.CommentCommandRepositoryPort; -import nettee.comment.type.CommentStatus; +import nettee.comment.domain.Comment; +import nettee.comment.driven.rdb.entity.type.CommentEntityStatus; +import nettee.comment.driven.rdb.persistence.mapper.CommentEntityMapper; +import nettee.comment.application.port.CommentCommandRepositoryPort; +import nettee.comment.domain.type.CommentStatus; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Repository; -import static nettee.comment.CommentCommandErrorCode.COMMENT_NOT_FOUND; -import static nettee.comment.CommentCommandErrorCode.DEFAULT; +import static nettee.comment.exception.CommentCommandErrorCode.COMMENT_NOT_FOUND; +import static nettee.comment.exception.CommentCommandErrorCode.DEFAULT; @Repository @RequiredArgsConstructor diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentJpaRepository.java b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/CommentJpaRepository.java similarity index 59% rename from services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentJpaRepository.java rename to services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/CommentJpaRepository.java index 12232d6..25937f3 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentJpaRepository.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/CommentJpaRepository.java @@ -1,6 +1,6 @@ -package nettee.comment.persistence; +package nettee.comment.driven.rdb.persistence; -import nettee.comment.entity.CommentEntity; +import nettee.comment.driven.rdb.entity.CommentEntity; import org.springframework.data.jpa.repository.JpaRepository; public interface CommentJpaRepository extends JpaRepository { diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/CommentQueryAdapter.java similarity index 83% rename from services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java rename to services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/CommentQueryAdapter.java index 7b46573..3ce159f 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/CommentQueryAdapter.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/CommentQueryAdapter.java @@ -1,18 +1,16 @@ -package nettee.comment.persistence; +package nettee.comment.driven.rdb.persistence; -import java.time.Instant; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import nettee.comment.Comment; -import nettee.comment.entity.CommentEntity; +import nettee.comment.driven.rdb.persistence.mapper.CommentEntityMapper; +import nettee.comment.driven.rdb.entity.CommentEntity; import nettee.comment.model.CommentQueryModels.CommentDetail; -import nettee.comment.persistence.mapper.CommentEntityMapper; -import nettee.comment.port.CommentQueryRepositoryPort; +import nettee.comment.application.port.CommentQueryRepositoryPort; import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; import org.springframework.stereotype.Repository; -import static nettee.comment.entity.QCommentEntity.commentEntity; +import static nettee.comment.driven.rdb.entity.QCommentEntity.commentEntity; @Repository public class CommentQueryAdapter extends QuerydslRepositorySupport implements CommentQueryRepositoryPort { diff --git a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/mapper/CommentEntityMapper.java similarity index 78% rename from services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java rename to services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/mapper/CommentEntityMapper.java index 8e06d20..1fb613e 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/comment/persistence/mapper/CommentEntityMapper.java +++ b/services/comment/driven/rdb/src/main/java/nettee/comment/driven/rdb/persistence/mapper/CommentEntityMapper.java @@ -1,8 +1,8 @@ -package nettee.comment.persistence.mapper; +package nettee.comment.driven.rdb.persistence.mapper; import java.util.Optional; -import nettee.comment.Comment; -import nettee.comment.entity.CommentEntity; +import nettee.comment.domain.Comment; +import nettee.comment.driven.rdb.entity.CommentEntity; import nettee.comment.model.CommentQueryModels.CommentDetail; import org.mapstruct.Mapper; diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/entity/ReplyEntity.java similarity index 89% rename from services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java rename to services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/entity/ReplyEntity.java index ec355c0..b9b29c5 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/ReplyEntity.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/entity/ReplyEntity.java @@ -1,4 +1,4 @@ -package nettee.reply.entity; +package nettee.reply.driven.rdb.entity; import jakarta.persistence.Column; import jakarta.persistence.Convert; @@ -6,11 +6,10 @@ import java.util.Objects; import lombok.AccessLevel; import lombok.Builder; -import lombok.Getter; import lombok.NoArgsConstructor; import nettee.jpa.support.LongBaseTimeEntity; -import nettee.reply.entity.type.ReplyEntityStatus; -import nettee.reply.entity.type.ReplyEntityStatusConverter; +import nettee.reply.driven.rdb.entity.type.ReplyEntityStatus; +import nettee.reply.driven.rdb.entity.type.ReplyEntityStatusConverter; import org.hibernate.annotations.DynamicUpdate; @DynamicUpdate diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatus.java b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/entity/type/ReplyEntityStatus.java similarity index 96% rename from services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatus.java rename to services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/entity/type/ReplyEntityStatus.java index 7ba94dc..a752b7e 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatus.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/entity/type/ReplyEntityStatus.java @@ -1,11 +1,11 @@ -package nettee.reply.entity.type; +package nettee.reply.driven.rdb.entity.type; -import static nettee.reply.ReplyCommandErrorCode.DEFAULT; +import static nettee.reply.exception.ReplyCommandErrorCode.DEFAULT; import java.util.Arrays; import java.util.Set; import java.util.stream.Collectors; -import nettee.reply.type.ReplyStatus; +import nettee.reply.domain.type.ReplyStatus; public enum ReplyEntityStatus { REMOVED( diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatusConverter.java b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/entity/type/ReplyEntityStatusConverter.java similarity index 91% rename from services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatusConverter.java rename to services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/entity/type/ReplyEntityStatusConverter.java index a685e77..9fe6b15 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/entity/type/ReplyEntityStatusConverter.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/entity/type/ReplyEntityStatusConverter.java @@ -1,4 +1,4 @@ -package nettee.reply.entity.type; +package nettee.reply.driven.rdb.entity.type; import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyCommandAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/ReplyCommandAdapter.java similarity index 75% rename from services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyCommandAdapter.java rename to services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/ReplyCommandAdapter.java index d2e112a..bf0acb7 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyCommandAdapter.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/ReplyCommandAdapter.java @@ -1,16 +1,16 @@ -package nettee.reply.persistence; +package nettee.reply.driven.rdb.persistence; import lombok.RequiredArgsConstructor; -import nettee.reply.Reply; -import nettee.reply.entity.type.ReplyEntityStatus; -import nettee.reply.persistence.mapper.ReplyEntityMapper; -import nettee.reply.port.ReplyCommandRepositoryPort; -import nettee.reply.type.ReplyStatus; +import nettee.reply.domain.Reply; +import nettee.reply.driven.rdb.entity.type.ReplyEntityStatus; +import nettee.reply.driven.rdb.persistence.mapper.ReplyEntityMapper; +import nettee.reply.application.port.ReplyCommandRepositoryPort; +import nettee.reply.domain.type.ReplyStatus; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Repository; -import static nettee.reply.ReplyCommandErrorCode.DEFAULT; -import static nettee.reply.ReplyCommandErrorCode.REPLY_NOT_FOUND; +import static nettee.reply.exception.ReplyCommandErrorCode.DEFAULT; +import static nettee.reply.exception.ReplyCommandErrorCode.REPLY_NOT_FOUND; @Repository @RequiredArgsConstructor diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyJpaRepository.java b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/ReplyJpaRepository.java similarity index 60% rename from services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyJpaRepository.java rename to services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/ReplyJpaRepository.java index 6f4ffe8..c3d0054 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyJpaRepository.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/ReplyJpaRepository.java @@ -1,6 +1,6 @@ -package nettee.reply.persistence; +package nettee.reply.driven.rdb.persistence; -import nettee.reply.entity.ReplyEntity; +import nettee.reply.driven.rdb.entity.ReplyEntity; import org.springframework.data.jpa.repository.JpaRepository; public interface ReplyJpaRepository extends JpaRepository { diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/ReplyQueryAdapter.java similarity index 88% rename from services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java rename to services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/ReplyQueryAdapter.java index 3070062..3a7da28 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/ReplyQueryAdapter.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/ReplyQueryAdapter.java @@ -1,17 +1,17 @@ -package nettee.reply.persistence; +package nettee.reply.driven.rdb.persistence; import java.time.Instant; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import nettee.reply.entity.ReplyEntity; +import nettee.reply.driven.rdb.persistence.mapper.ReplyEntityMapper; +import nettee.reply.driven.rdb.entity.ReplyEntity; import nettee.reply.model.ReplyQueryModels.ReplyDetail; -import nettee.reply.persistence.mapper.ReplyEntityMapper; -import nettee.reply.port.ReplyQueryRepositoryPort; +import nettee.reply.application.port.ReplyQueryRepositoryPort; import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; import org.springframework.stereotype.Repository; -import static nettee.reply.entity.QReplyEntity.replyEntity; +import static nettee.reply.driven.rdb.entity.QReplyEntity.replyEntity; @Repository public class ReplyQueryAdapter extends QuerydslRepositorySupport implements ReplyQueryRepositoryPort { diff --git a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/mapper/ReplyEntityMapper.java similarity index 77% rename from services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java rename to services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/mapper/ReplyEntityMapper.java index 30ecb4b..c82ec04 100644 --- a/services/comment/driven/rdb/src/main/java/nettee/reply/persistence/mapper/ReplyEntityMapper.java +++ b/services/comment/driven/rdb/src/main/java/nettee/reply/driven/rdb/persistence/mapper/ReplyEntityMapper.java @@ -1,8 +1,8 @@ -package nettee.reply.persistence.mapper; +package nettee.reply.driven.rdb.persistence.mapper; import java.util.Optional; -import nettee.reply.Reply; -import nettee.reply.entity.ReplyEntity; +import nettee.reply.domain.Reply; +import nettee.reply.driven.rdb.entity.ReplyEntity; import nettee.reply.model.ReplyQueryModels.ReplyDetail; import org.mapstruct.Mapper; diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentCommandApi.java b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentCommandApi.java index 9838c4b..abb7e09 100644 --- a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentCommandApi.java +++ b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentCommandApi.java @@ -2,9 +2,9 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import nettee.comment.usecase.CommentCreateUseCase; -import nettee.comment.usecase.CommentDeleteUseCase; -import nettee.comment.usecase.CommentUpdateUseCase; +import nettee.comment.application.usecase.CommentCreateUseCase; +import nettee.comment.application.usecase.CommentDeleteUseCase; +import nettee.comment.application.usecase.CommentUpdateUseCase; import nettee.comment.web.dto.CommentCommandDto.CommentCommandResponse; import nettee.comment.web.dto.CommentCommandDto.CommentCreateCommand; import nettee.comment.web.dto.CommentCommandDto.CommentUpdateCommand; diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentQueryApi.java b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentQueryApi.java index 096cb72..cacd224 100644 --- a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentQueryApi.java +++ b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/CommentQueryApi.java @@ -3,7 +3,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; import nettee.comment.model.CommentQueryModels.CommentDetail; -import nettee.comment.service.CommentQueryService; +import nettee.comment.application.service.CommentQueryService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/dto/CommentCommandDto.java b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/dto/CommentCommandDto.java index 5cd3571..e105b88 100644 --- a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/dto/CommentCommandDto.java +++ b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/dto/CommentCommandDto.java @@ -3,8 +3,8 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Builder; -import nettee.comment.Comment; -import nettee.comment.type.CommentStatus; +import nettee.comment.domain.Comment; +import nettee.comment.domain.type.CommentStatus; public class CommentCommandDto { diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/mapper/CommentDtoMapper.java b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/mapper/CommentDtoMapper.java index 57c3e3b..cf7e1cf 100644 --- a/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/mapper/CommentDtoMapper.java +++ b/services/comment/driving/web-mvc/src/main/java/nettee/comment/web/mapper/CommentDtoMapper.java @@ -1,6 +1,6 @@ package nettee.comment.web.mapper; -import nettee.comment.Comment; +import nettee.comment.domain.Comment; import nettee.comment.web.dto.CommentCommandDto.CommentCreateCommand; import nettee.comment.web.dto.CommentCommandDto.CommentUpdateCommand; import org.mapstruct.Mapper; diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyCommandApi.java b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyCommandApi.java index 29c3ec3..e907af7 100644 --- a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyCommandApi.java +++ b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyCommandApi.java @@ -2,9 +2,9 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import nettee.reply.usecase.ReplyCreateUseCase; -import nettee.reply.usecase.ReplyDeleteUseCase; -import nettee.reply.usecase.ReplyUpdateUseCase; +import nettee.reply.application.usecase.ReplyCreateUseCase; +import nettee.reply.application.usecase.ReplyDeleteUseCase; +import nettee.reply.application.usecase.ReplyUpdateUseCase; import nettee.reply.web.dto.ReplyCommandDto.ReplyCommandResponse; import nettee.reply.web.dto.ReplyCommandDto.ReplyCreateCommand; import nettee.reply.web.dto.ReplyCommandDto.ReplyUpdateCommand; diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyQueryApi.java b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyQueryApi.java index 5c5eec4..8d9f2a7 100644 --- a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyQueryApi.java +++ b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/ReplyQueryApi.java @@ -4,7 +4,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; import nettee.reply.model.ReplyQueryModels.ReplyDetail; -import nettee.reply.service.ReplyQueryService; +import nettee.reply.application.service.ReplyQueryService; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/dto/ReplyCommandDto.java b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/dto/ReplyCommandDto.java index 69f048b..0b8e246 100644 --- a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/dto/ReplyCommandDto.java +++ b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/dto/ReplyCommandDto.java @@ -3,8 +3,8 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Builder; -import nettee.reply.Reply; -import nettee.reply.type.ReplyStatus; +import nettee.reply.domain.Reply; +import nettee.reply.domain.type.ReplyStatus; public class ReplyCommandDto { diff --git a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/mapper/ReplyDtoMapper.java b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/mapper/ReplyDtoMapper.java index c400914..791cef8 100644 --- a/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/mapper/ReplyDtoMapper.java +++ b/services/comment/driving/web-mvc/src/main/java/nettee/reply/web/mapper/ReplyDtoMapper.java @@ -1,6 +1,6 @@ package nettee.reply.web.mapper; -import nettee.reply.Reply; +import nettee.reply.domain.Reply; import nettee.reply.web.dto.ReplyCommandDto.ReplyCreateCommand; import nettee.reply.web.dto.ReplyCommandDto.ReplyUpdateCommand; import org.mapstruct.Mapper; From b007832404bd617db39cff7343897539060f0eaf Mon Sep 17 00:00:00 2001 From: seonghooni Date: Mon, 19 May 2025 23:49:02 +0900 Subject: [PATCH 25/25] refactor(comment/reply): add boardId field to CommentDetail --- .../src/main/java/nettee/comment/model/CommentQueryModels.java | 1 + .../nettee/comment/application/service/CommentQueryService.java | 1 + 2 files changed, 2 insertions(+) diff --git a/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java b/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java index 936cd78..d6d89b1 100644 --- a/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java +++ b/services/comment/api/readmodel/src/main/java/nettee/comment/model/CommentQueryModels.java @@ -15,6 +15,7 @@ private CommentQueryModels() { @Builder public record CommentDetail( Long id, + Long boardId, String content, CommentStatus status, Instant createdAt, diff --git a/services/comment/application/src/main/java/nettee/comment/application/service/CommentQueryService.java b/services/comment/application/src/main/java/nettee/comment/application/service/CommentQueryService.java index 5736144..c7b5f18 100644 --- a/services/comment/application/src/main/java/nettee/comment/application/service/CommentQueryService.java +++ b/services/comment/application/src/main/java/nettee/comment/application/service/CommentQueryService.java @@ -26,6 +26,7 @@ public List getCommentsByBoardId(Long boardId) { return CommentDetail.builder() .id(comment.id()) + .boardId(comment.boardId()) .content(comment.content()) .status(comment.status()) .createdAt(comment.createdAt())