diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8bef5f2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,234 @@
+# π FlipNote β Reaction Service
+
+**FlipNote μλΉμ€μ λ°μ(μ’μμΒ·λΆλ§ν¬) λλ©μΈ λ°±μλ λ ν¬μ§ν 리μ
λλ€.**
+
+
+
+
+
+
+
+---
+
+## π λͺ©μ°¨
+
+- [μμνκΈ°](#μμνκΈ°)
+- [νκ²½ λ³μ](#νκ²½-λ³μ)
+- [μ€ν λ° λ°°ν¬](#μ€ν-λ°-λ°°ν¬)
+- [νλ‘μ νΈ κ΅¬μ‘°](#νλ‘μ νΈ-ꡬ쑰)
+
+---
+
+
+
+## π μμνκΈ°
+
+### μ¬μ μꡬμ¬ν
+
+- **Java** 21 μ΄μ
+- **Gradle** 8 μ΄μ
+- **MySQL** 8 μ΄μ
+- **RabbitMQ** 3 μ΄μ
+
+### μ€μΉ
+
+```bash
+# μμ‘΄μ± μ€μΉ λ° λΉλ
+./gradlew build -x test
+```
+
+---
+
+
+
+## π νκ²½ λ³μ
+
+`application.yml`μμ μ°Έμ‘°νλ νκ²½ λ³μ λͺ©λ‘μ
λλ€. λ‘컬 μ€ν μ `.env` λλ IDE μ€ν ꡬμ±μ μλ λ³μλ₯Ό μ€μ ν©λλ€.
+
+```text
+# βββ Database βββββββββββββββββββββββββββββββββββββββββββ
+DB_URL=jdbc:mysql://localhost:3306/flipnote_reaction
+DB_USERNAME=
+DB_PASSWORD=
+# create | create-drop | update | validate | none
+DDL_AUTO=validate
+
+# βββ RabbitMQ βββββββββββββββββββββββββββββββββββββββββββ
+RABBITMQ_HOST=localhost
+RABBITMQ_PORT=5672
+RABBITMQ_USERNAME=guest
+RABBITMQ_PASSWORD=guest
+
+# βββ gRPC βββββββββββββββββββββββββββββββββββββββββββββββ
+# Reaction μλΉμ€ gRPC μλ² ν¬νΈ (κΈ°λ³Έκ° 9093)
+GRPC_SERVER_PORT=9093
+# CardSet μλΉμ€ gRPC ν΄λΌμ΄μΈνΈ μ£Όμ (κΈ°λ³Έκ° static://cardset-service:9095)
+GRPC_CARDSET_ADDRESS=static://localhost:9095
+```
+
+> **β οΈ μ£Όμ**: νκ²½ λ³μ νμΌμ μ λ gitμ 컀λ°νμ§ λ§μΈμ. `.gitignore`μ ν¬ν¨λμ΄ μλμ§ λ°λμ νμΈνμΈμ.
+
+---
+
+
+
+## π₯οΈ μ€ν λ° λ°°ν¬
+
+### λ‘컬 κ°λ° μλ² μ€ν
+
+```bash
+./gradlew bootRun
+```
+
+κΈ°λ³Έμ μΌλ‘ `http://localhost:8083`μμ μ€νλ©λλ€.
+gRPC μλ²λ κΈ°λ³Έμ μΌλ‘ `9093` ν¬νΈμμ μ€νλ©λλ€.
+
+### νλ‘λμ
λΉλ
+
+```bash
+./gradlew bootJar
+```
+
+`build/libs/reaction-0.0.1-SNAPSHOT.jar` νμΌμ΄ μμ±λ©λλ€.
+
+### ν
μ€νΈ μ€ν
+
+```bash
+./gradlew test
+```
+
+### Docker μ΄λ―Έμ§ λΉλ λ° μ€ν
+
+```bash
+# μ΄λ―Έμ§ λΉλ
+docker build -t flipnote-reaction .
+
+# 컨ν
μ΄λ μ€ν
+docker run -p 8083:8083 -p 9093:9093 \
+ -e DB_URL=... \
+ -e RABBITMQ_HOST=... \
+ flipnote-reaction
+```
+
+### λ°°ν¬ (GitHub Actions)
+
+`main` λΈλμΉμ push μ GitHub Actionsκ° μλμΌλ‘ μλ κ³Όμ μ μ€νν©λλ€.
+
+**CI** (`push` / `pull_request` β `main`)
+1. JDK 21 μ€μΉ
+2. `./gradlew build -x test` β λΉλ κ²μ¦
+3. `./gradlew test` β ν
μ€νΈ μ€ν
+4. Dependency-Check β μ·¨μ½μ λΆμ 리ν¬νΈ μμ±
+
+**CD** (`push` β `main`)
+1. GitHub Container Registry(GHCR) λ‘κ·ΈμΈ
+2. Docker μ΄λ―Έμ§ λΉλ
+3. `ghcr.io/dungbik/flipnote-reaction` μ΄λ―Έμ§ Push
+
+> λ°°ν¬μ νμν μν¬λ¦Ώ(`ORG_PAT`)μ GitHub Repository β Settings β Secrets and variables β Actionsμ λ±λ‘ν΄μΌ ν©λλ€.
+
+---
+
+
+
+## π νλ‘μ νΈ κ΅¬μ‘°
+
+- κ°λ΅ν λ²μ
+
+ ```text
+ src/main/java/flipnote/reaction/
+ βββ domain/ # λλ©μΈ λ μ΄μ΄ (μν°ν°, λ ν¬μ§ν 리, λλ©μΈ μ΄λ²€νΈ, μλ¬μ½λ)
+ βββ application/ # μ ν리μΌμ΄μ
λ μ΄μ΄ (μλΉμ€, μ‘°ν μ»΄ν¬λνΈ, Result κ°μ²΄)
+ βββ infrastructure/ # μΈνλΌ λ μ΄μ΄ (μμμ±, λ©μμ§, gRPC ν΄λΌμ΄μΈνΈ)
+ βββ interfaces/ # μΈν°νμ΄μ€ λ μ΄μ΄ (HTTP, gRPC μ§μ
μ )
+ ```
+
+```text
+FlipNote-Reaction/
+βββ src/
+β βββ main/
+β β βββ java/flipnote/reaction/
+β β β βββ ReactionApplication.java
+β β β β
+β β β βββ domain/ # λλ©μΈ λ μ΄μ΄
+β β β β βββ common/ # λλ©μΈ 곡ν΅
+β β β β β βββ ErrorCode.java
+β β β β β βββ BizException.java
+β β β β β βββ CommonErrorCode.java
+β β β β β βββ BaseEntity.java
+β β β β βββ bookmark/ # λΆλ§ν¬ λλ©μΈ
+β β β β β βββ Bookmark.java
+β β β β β βββ BookmarkTargetType.java
+β β β β β βββ BookmarkRepository.java
+β β β β β βββ BookmarkErrorCode.java
+β β β β β βββ event/ # λΆλ§ν¬ λλ©μΈ μ΄λ²€νΈ
+β β β β β βββ BookmarkAddedEvent.java
+β β β β β βββ BookmarkRemovedEvent.java
+β β β β βββ like/ # μ’μμ λλ©μΈ
+β β β β βββ Like.java
+β β β β βββ LikeTargetType.java
+β β β β βββ LikeRepository.java
+β β β β βββ LikeErrorCode.java
+β β β β βββ event/ # μ’μμ λλ©μΈ μ΄λ²€νΈ
+β β β β βββ LikeAddedEvent.java
+β β β β βββ LikeRemovedEvent.java
+β β β β
+β β β βββ application/ # μ ν리μΌμ΄μ
λ μ΄μ΄
+β β β β βββ common/
+β β β β β βββ CardSetSummaryResult.java # 곡μ Result κ°μ²΄
+β β β β βββ bookmark/
+β β β β β βββ BookmarkService.java
+β β β β β βββ BookmarkReader.java
+β β β β β βββ BookmarkResult.java # μλΉμ€ λ°ν Result
+β β β β βββ like/
+β β β β βββ LikeService.java
+β β β β βββ LikeReader.java
+β β β β βββ LikeResult.java # μλΉμ€ λ°ν Result
+β β β β
+β β β βββ infrastructure/ # μΈνλΌ λ μ΄μ΄
+β β β β βββ grpc/ # gRPC ν΄λΌμ΄μΈνΈ
+β β β β β βββ GrpcConfig.java
+β β β β β βββ CardSetGrpcClient.java
+β β β β βββ messaging/ # λ©μμ§ λ°ν + μ€μ
+β β β β β βββ RabbitMqConfig.java
+β β β β β βββ ReactionMessage.java
+β β β β β βββ ReactionEventPublisher.java
+β β β β β βββ ReactionEventListener.java # @TransactionalEventListener
+β β β β βββ persistence/ # μμμ± μ΄λν° + JPA μ€μ
+β β β β βββ AuditingConfig.java
+β β β β βββ BookmarkRepositoryAdapter.java
+β β β β βββ SpringDataBookmarkRepository.java
+β β β β βββ LikeRepositoryAdapter.java
+β β β β βββ SpringDataLikeRepository.java
+β β β β
+β β β βββ interfaces/ # μΈν°νμ΄μ€ λ μ΄μ΄
+β β β βββ http/ # HTTP μ§μ
μ
+β β β β βββ BookmarkController.java
+β β β β βββ LikeController.java
+β β β β βββ common/ # κ³΅ν΅ μλ΅, μμΈ μ²λ¦¬
+β β β β β βββ ApiResponse.java
+β β β β β βββ ApiResponseAdvice.java
+β β β β β βββ GlobalExceptionHandler.java
+β β β β β βββ HeaderConstants.java
+β β β β β βββ IdResponse.java
+β β β β β βββ PagingRequest.java
+β β β β β βββ PagingResponse.java
+β β β β βββ dto/request/
+β β β β βββ BookmarkSearchRequest.java
+β β β β βββ BookmarkTargetTypeRequest.java
+β β β β βββ LikeSearchRequest.java
+β β β β βββ LikeTargetTypeRequest.java
+β β β βββ grpc/ # gRPC μλ² μ§μ
μ
+β β β βββ ReactionGrpcService.java
+β β β βββ GrpcExceptionHandlerImpl.java
+β β β
+β β βββ resources/
+β β βββ application.yml
+β β
+β βββ test/
+β βββ java/flipnote/reaction/
+β
+βββ Dockerfile
+βββ build.gradle.kts
+βββ settings.gradle.kts
+```
diff --git a/build.gradle.kts b/build.gradle.kts
index 3a4f7ed..cdedd82 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,9 +2,9 @@ import com.google.protobuf.gradle.id
plugins {
java
- id("org.springframework.boot") version "3.5.10"
+ id("org.springframework.boot") version "4.0.2"
id("io.spring.dependency-management") version "1.1.7"
- id("com.google.protobuf") version "0.9.4"
+ id("com.google.protobuf") version "0.9.5"
}
group = "flipnote"
@@ -17,18 +17,11 @@ java {
}
}
-configurations {
- compileOnly {
- extendsFrom(configurations.annotationProcessor.get())
- }
-}
-
repositories {
mavenCentral()
}
-val grpcVersion = "1.68.0"
-val protocVersion = "4.28.2"
+extra["springGrpcVersion"] = "1.0.2"
dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
@@ -38,33 +31,39 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-amqp")
// gRPC
- implementation("io.grpc:grpc-netty-shaded:$grpcVersion")
- implementation("io.grpc:grpc-protobuf:$grpcVersion")
- implementation("io.grpc:grpc-stub:$grpcVersion")
- implementation("com.google.protobuf:protobuf-java:$protocVersion")
- implementation("javax.annotation:javax.annotation-api:1.3.2")
+ implementation("io.grpc:grpc-services")
+ implementation("org.springframework.grpc:spring-grpc-spring-boot-starter")
compileOnly("org.projectlombok:lombok")
runtimeOnly("com.mysql:mysql-connector-j")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
+ testImplementation("org.springframework.grpc:spring-grpc-test")
testRuntimeOnly("com.h2database:h2")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
+dependencyManagement {
+ imports {
+ mavenBom("org.springframework.grpc:spring-grpc-dependencies:${property("springGrpcVersion")}")
+ }
+}
+
protobuf {
protoc {
- artifact = "com.google.protobuf:protoc:$protocVersion"
+ artifact = "com.google.protobuf:protoc"
}
plugins {
id("grpc") {
- artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion"
+ artifact = "io.grpc:protoc-gen-grpc-java"
}
}
generateProtoTasks {
- all().forEach { task ->
- task.plugins {
- id("grpc")
+ all().forEach {
+ it.plugins {
+ id("grpc") {
+ option("@generated=omit")
+ }
}
}
}
diff --git a/src/main/java/flipnote/reaction/bookmark/service/BookmarkReader.java b/src/main/java/flipnote/reaction/application/bookmark/BookmarkReader.java
similarity index 56%
rename from src/main/java/flipnote/reaction/bookmark/service/BookmarkReader.java
rename to src/main/java/flipnote/reaction/application/bookmark/BookmarkReader.java
index aa74c36..6c401ac 100644
--- a/src/main/java/flipnote/reaction/bookmark/service/BookmarkReader.java
+++ b/src/main/java/flipnote/reaction/application/bookmark/BookmarkReader.java
@@ -1,17 +1,16 @@
-package flipnote.reaction.bookmark.service;
+package flipnote.reaction.application.bookmark;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.stereotype.Component;
-import flipnote.reaction.bookmark.entity.Bookmark;
-import flipnote.reaction.bookmark.entity.BookmarkTargetType;
-import flipnote.reaction.bookmark.exception.BookmarkErrorCode;
-import flipnote.reaction.bookmark.repository.BookmarkRepository;
-import flipnote.reaction.common.exception.BizException;
+import flipnote.reaction.domain.bookmark.Bookmark;
+import flipnote.reaction.domain.bookmark.BookmarkErrorCode;
+import flipnote.reaction.domain.bookmark.BookmarkRepository;
+import flipnote.reaction.domain.bookmark.BookmarkTargetType;
+import flipnote.reaction.domain.common.BizException;
import lombok.RequiredArgsConstructor;
@Component
@@ -29,13 +28,11 @@ public boolean isBookmarked(BookmarkTargetType targetType, Long targetId, Long u
return bookmarkRepository.existsByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId);
}
- public Map areBookmarked(BookmarkTargetType targetType, List targetIds, Long userId) {
- Set bookmarkedIds = bookmarkRepository.findByTargetTypeAndTargetIdInAndUserId(targetType, targetIds, userId)
- .stream()
- .map(Bookmark::getTargetId)
- .collect(Collectors.toSet());
-
+ public Map areBookmarked(BookmarkTargetType targetType, List targetIds, long userId) {
return targetIds.stream()
- .collect(Collectors.toMap(id -> id, bookmarkedIds::contains));
+ .collect(Collectors.toMap(
+ targetId -> targetId,
+ targetId -> bookmarkRepository.existsByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId)
+ ));
}
}
diff --git a/src/main/java/flipnote/reaction/application/bookmark/BookmarkResult.java b/src/main/java/flipnote/reaction/application/bookmark/BookmarkResult.java
new file mode 100644
index 0000000..bbd9551
--- /dev/null
+++ b/src/main/java/flipnote/reaction/application/bookmark/BookmarkResult.java
@@ -0,0 +1,22 @@
+package flipnote.reaction.application.bookmark;
+
+import java.time.LocalDateTime;
+
+import flipnote.reaction.application.common.CardSetSummaryResult;
+import flipnote.reaction.domain.bookmark.Bookmark;
+
+public record BookmarkResult(
+ String targetType,
+ Long targetId,
+ LocalDateTime bookmarkedAt,
+ CardSetSummaryResult cardSet
+) {
+ public static BookmarkResult from(Bookmark bookmark, CardSetSummaryResult cardSet) {
+ return new BookmarkResult(
+ bookmark.getTargetType().name(),
+ bookmark.getTargetId(),
+ bookmark.getCreatedAt(),
+ cardSet
+ );
+ }
+}
diff --git a/src/main/java/flipnote/reaction/application/bookmark/BookmarkService.java b/src/main/java/flipnote/reaction/application/bookmark/BookmarkService.java
new file mode 100644
index 0000000..dfa0bf4
--- /dev/null
+++ b/src/main/java/flipnote/reaction/application/bookmark/BookmarkService.java
@@ -0,0 +1,98 @@
+package flipnote.reaction.application.bookmark;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import flipnote.reaction.application.common.CardSetSummaryResult;
+import flipnote.reaction.domain.bookmark.Bookmark;
+import flipnote.reaction.domain.bookmark.BookmarkErrorCode;
+import flipnote.reaction.domain.bookmark.BookmarkRepository;
+import flipnote.reaction.domain.bookmark.BookmarkTargetType;
+import flipnote.reaction.domain.bookmark.event.BookmarkAddedEvent;
+import flipnote.reaction.domain.bookmark.event.BookmarkRemovedEvent;
+import flipnote.reaction.domain.common.BizException;
+import flipnote.reaction.domain.common.CommonErrorCode;
+import flipnote.reaction.infrastructure.grpc.CardSetGrpcClient;
+import flipnote.reaction.interfaces.http.dto.request.BookmarkSearchRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class BookmarkService {
+
+ private final BookmarkRepository bookmarkRepository;
+ private final BookmarkReader bookmarkReader;
+ private final ApplicationEventPublisher eventPublisher;
+ private final CardSetGrpcClient cardSetGrpcClient;
+
+ @Transactional(noRollbackFor = DataIntegrityViolationException.class)
+ public Long addBookmark(BookmarkTargetType targetType, Long targetId, Long userId) {
+ if (!cardSetGrpcClient.isCardSetViewable(targetId, userId)) {
+ throw new BizException(CommonErrorCode.TARGET_NOT_VIEWABLE);
+ }
+
+ if (bookmarkReader.isBookmarked(targetType, targetId, userId)) {
+ throw new BizException(BookmarkErrorCode.ALREADY_BOOKMARKED);
+ }
+
+ Bookmark bookmark = Bookmark.builder()
+ .targetType(targetType)
+ .targetId(targetId)
+ .userId(userId)
+ .build();
+
+ try {
+ bookmarkRepository.save(bookmark);
+ } catch (DataIntegrityViolationException e) {
+ throw new BizException(BookmarkErrorCode.ALREADY_BOOKMARKED);
+ }
+
+ eventPublisher.publishEvent(new BookmarkAddedEvent(targetType.name(), targetId, userId));
+
+ return bookmark.getId();
+ }
+
+ @Transactional
+ public void removeBookmark(BookmarkTargetType targetType, Long targetId, Long userId) {
+ Bookmark bookmark = bookmarkReader.findByTargetAndUserId(targetType, targetId, userId);
+ bookmarkRepository.delete(bookmark);
+
+ eventPublisher.publishEvent(new BookmarkRemovedEvent(targetType.name(), targetId, userId));
+ }
+
+ public Page getBookmarks(BookmarkTargetType targetType, Long userId,
+ BookmarkSearchRequest request) {
+ Page bookmarkPage = bookmarkRepository.findByTargetTypeAndUserId(
+ targetType, userId, request.getPageRequest()
+ );
+
+ List targetIds = bookmarkPage.getContent().stream()
+ .map(Bookmark::getTargetId)
+ .toList();
+
+ Map cardSetMap = fetchCardSets(targetIds, userId);
+
+ return bookmarkPage.map(b -> BookmarkResult.from(b, cardSetMap.get(b.getTargetId())));
+ }
+
+ private Map fetchCardSets(List targetIds, Long userId) {
+ if (targetIds.isEmpty()) {
+ return Map.of();
+ }
+ try {
+ return cardSetGrpcClient.getCardSetsByIds(targetIds, userId);
+ } catch (BizException e) {
+ log.warn("CardSet μ‘°ν μ€ν¨ β cardSet μμ΄ λ°νν©λλ€: {}", e.getMessage());
+ return Map.of();
+ }
+ }
+}
diff --git a/src/main/java/flipnote/reaction/application/common/CardSetSummaryResult.java b/src/main/java/flipnote/reaction/application/common/CardSetSummaryResult.java
new file mode 100644
index 0000000..42ff7d9
--- /dev/null
+++ b/src/main/java/flipnote/reaction/application/common/CardSetSummaryResult.java
@@ -0,0 +1,13 @@
+package flipnote.reaction.application.common;
+
+public record CardSetSummaryResult(
+ Long id,
+ String name,
+ Long groupId,
+ String visibility,
+ String category,
+ String hashtag,
+ Long imageRefId,
+ Long cardCount
+) {
+}
diff --git a/src/main/java/flipnote/reaction/like/service/LikeReader.java b/src/main/java/flipnote/reaction/application/like/LikeReader.java
similarity index 57%
rename from src/main/java/flipnote/reaction/like/service/LikeReader.java
rename to src/main/java/flipnote/reaction/application/like/LikeReader.java
index fc8c31b..e0501db 100644
--- a/src/main/java/flipnote/reaction/like/service/LikeReader.java
+++ b/src/main/java/flipnote/reaction/application/like/LikeReader.java
@@ -1,17 +1,16 @@
-package flipnote.reaction.like.service;
+package flipnote.reaction.application.like;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.stereotype.Component;
-import flipnote.reaction.common.exception.BizException;
-import flipnote.reaction.like.entity.Like;
-import flipnote.reaction.like.entity.LikeTargetType;
-import flipnote.reaction.like.exception.LikeErrorCode;
-import flipnote.reaction.like.repository.LikeRepository;
+import flipnote.reaction.domain.common.BizException;
+import flipnote.reaction.domain.like.Like;
+import flipnote.reaction.domain.like.LikeErrorCode;
+import flipnote.reaction.domain.like.LikeRepository;
+import flipnote.reaction.domain.like.LikeTargetType;
import lombok.RequiredArgsConstructor;
@Component
@@ -29,13 +28,11 @@ public boolean isLiked(LikeTargetType targetType, Long targetId, Long userId) {
return likeRepository.existsByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId);
}
- public Map areLiked(LikeTargetType targetType, List targetIds, Long userId) {
- Set likedIds = likeRepository.findByTargetTypeAndTargetIdInAndUserId(targetType, targetIds, userId)
- .stream()
- .map(Like::getTargetId)
- .collect(Collectors.toSet());
-
+ public Map areLiked(LikeTargetType targetType, List targetIds, long userId) {
return targetIds.stream()
- .collect(Collectors.toMap(id -> id, likedIds::contains));
+ .collect(Collectors.toMap(
+ targetId -> targetId,
+ targetId -> likeRepository.existsByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId)
+ ));
}
}
diff --git a/src/main/java/flipnote/reaction/application/like/LikeResult.java b/src/main/java/flipnote/reaction/application/like/LikeResult.java
new file mode 100644
index 0000000..d0fc708
--- /dev/null
+++ b/src/main/java/flipnote/reaction/application/like/LikeResult.java
@@ -0,0 +1,22 @@
+package flipnote.reaction.application.like;
+
+import java.time.LocalDateTime;
+
+import flipnote.reaction.application.common.CardSetSummaryResult;
+import flipnote.reaction.domain.like.Like;
+
+public record LikeResult(
+ String targetType,
+ Long targetId,
+ LocalDateTime likedAt,
+ CardSetSummaryResult cardSet
+) {
+ public static LikeResult from(Like like, CardSetSummaryResult cardSet) {
+ return new LikeResult(
+ like.getTargetType().name(),
+ like.getTargetId(),
+ like.getCreatedAt(),
+ cardSet
+ );
+ }
+}
diff --git a/src/main/java/flipnote/reaction/application/like/LikeService.java b/src/main/java/flipnote/reaction/application/like/LikeService.java
new file mode 100644
index 0000000..df94047
--- /dev/null
+++ b/src/main/java/flipnote/reaction/application/like/LikeService.java
@@ -0,0 +1,97 @@
+package flipnote.reaction.application.like;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import flipnote.reaction.application.common.CardSetSummaryResult;
+import flipnote.reaction.domain.common.BizException;
+import flipnote.reaction.domain.common.CommonErrorCode;
+import flipnote.reaction.domain.like.Like;
+import flipnote.reaction.domain.like.LikeErrorCode;
+import flipnote.reaction.domain.like.LikeRepository;
+import flipnote.reaction.domain.like.LikeTargetType;
+import flipnote.reaction.domain.like.event.LikeAddedEvent;
+import flipnote.reaction.domain.like.event.LikeRemovedEvent;
+import flipnote.reaction.infrastructure.grpc.CardSetGrpcClient;
+import flipnote.reaction.interfaces.http.dto.request.LikeSearchRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class LikeService {
+
+ private final LikeRepository likeRepository;
+ private final LikeReader likeReader;
+ private final ApplicationEventPublisher eventPublisher;
+ private final CardSetGrpcClient cardSetGrpcClient;
+
+ @Transactional(noRollbackFor = DataIntegrityViolationException.class)
+ public Long addLike(LikeTargetType targetType, Long targetId, Long userId) {
+ if (!cardSetGrpcClient.isCardSetViewable(targetId, userId)) {
+ throw new BizException(CommonErrorCode.TARGET_NOT_VIEWABLE);
+ }
+
+ if (likeReader.isLiked(targetType, targetId, userId)) {
+ throw new BizException(LikeErrorCode.ALREADY_LIKED);
+ }
+
+ Like like = Like.builder()
+ .targetType(targetType)
+ .targetId(targetId)
+ .userId(userId)
+ .build();
+
+ try {
+ likeRepository.save(like);
+ } catch (DataIntegrityViolationException e) {
+ throw new BizException(LikeErrorCode.ALREADY_LIKED);
+ }
+
+ eventPublisher.publishEvent(new LikeAddedEvent(targetType.name(), targetId, userId));
+
+ return like.getId();
+ }
+
+ @Transactional
+ public void removeLike(LikeTargetType targetType, Long targetId, Long userId) {
+ Like like = likeReader.findByTargetAndUserId(targetType, targetId, userId);
+ likeRepository.delete(like);
+
+ eventPublisher.publishEvent(new LikeRemovedEvent(targetType.name(), targetId, userId));
+ }
+
+ public Page getLikes(LikeTargetType targetType, Long userId, LikeSearchRequest request) {
+ Page likePage = likeRepository.findByTargetTypeAndUserId(
+ targetType, userId, request.getPageRequest()
+ );
+
+ List targetIds = likePage.getContent().stream()
+ .map(Like::getTargetId)
+ .toList();
+
+ Map cardSetMap = fetchCardSets(targetIds, userId);
+
+ return likePage.map(l -> LikeResult.from(l, cardSetMap.get(l.getTargetId())));
+ }
+
+ private Map fetchCardSets(List targetIds, Long userId) {
+ if (targetIds.isEmpty()) {
+ return Map.of();
+ }
+ try {
+ return cardSetGrpcClient.getCardSetsByIds(targetIds, userId);
+ } catch (BizException e) {
+ log.warn("CardSet μ‘°ν μ€ν¨ β cardSet μμ΄ λ°νν©λλ€: {}", e.getMessage());
+ return Map.of();
+ }
+ }
+}
diff --git a/src/main/java/flipnote/reaction/bookmark/model/response/BookmarkResponse.java b/src/main/java/flipnote/reaction/bookmark/model/response/BookmarkResponse.java
deleted file mode 100644
index 5034076..0000000
--- a/src/main/java/flipnote/reaction/bookmark/model/response/BookmarkResponse.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package flipnote.reaction.bookmark.model.response;
-
-import java.time.LocalDateTime;
-
-import cardset.Cardset.CardSetSummary;
-import flipnote.reaction.bookmark.entity.Bookmark;
-
-public record BookmarkResponse(
- BookmarkTargetResponse target,
- LocalDateTime bookmarkedAt
-) {
- public static BookmarkResponse from(Bookmark bookmark, CardSetSummary summary) {
- return new BookmarkResponse(
- new BookmarkTargetResponse(
- bookmark.getTargetType().name(),
- bookmark.getTargetId(),
- summary != null ? summary.getGroupId() : null,
- summary != null ? summary.getName() : null
- ),
- bookmark.getCreatedAt()
- );
- }
-}
diff --git a/src/main/java/flipnote/reaction/bookmark/model/response/BookmarkTargetResponse.java b/src/main/java/flipnote/reaction/bookmark/model/response/BookmarkTargetResponse.java
deleted file mode 100644
index b62838f..0000000
--- a/src/main/java/flipnote/reaction/bookmark/model/response/BookmarkTargetResponse.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package flipnote.reaction.bookmark.model.response;
-
-public record BookmarkTargetResponse(
- String type,
- Long id,
- Long groupId,
- String name
-) {
-}
diff --git a/src/main/java/flipnote/reaction/bookmark/service/BookmarkService.java b/src/main/java/flipnote/reaction/bookmark/service/BookmarkService.java
deleted file mode 100644
index ca7f7f7..0000000
--- a/src/main/java/flipnote/reaction/bookmark/service/BookmarkService.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package flipnote.reaction.bookmark.service;
-
-import java.util.List;
-import java.util.Map;
-
-import org.springframework.dao.DataIntegrityViolationException;
-import org.springframework.data.domain.Page;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import cardset.Cardset;
-import cardset.Cardset.CardSetSummary;
-import flipnote.reaction.bookmark.entity.Bookmark;
-import flipnote.reaction.bookmark.entity.BookmarkTargetType;
-import flipnote.reaction.bookmark.exception.BookmarkErrorCode;
-import flipnote.reaction.bookmark.model.request.BookmarkSearchRequest;
-import flipnote.reaction.bookmark.model.response.BookmarkResponse;
-import flipnote.reaction.bookmark.repository.BookmarkRepository;
-import flipnote.reaction.common.config.RabbitMqConfig;
-import flipnote.reaction.common.event.ReactionEventPublisher;
-import flipnote.reaction.common.exception.BizException;
-import flipnote.reaction.common.exception.CommonErrorCode;
-import flipnote.reaction.common.grpc.CardSetGrpcClient;
-import flipnote.reaction.common.model.response.IdResponse;
-import flipnote.reaction.common.model.response.PagingResponse;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-@Transactional(readOnly = true)
-public class BookmarkService {
-
- private final BookmarkRepository bookmarkRepository;
- private final BookmarkReader bookmarkReader;
- private final ReactionEventPublisher eventPublisher;
- private final CardSetGrpcClient cardSetGrpcClient;
-
- @Transactional
- public IdResponse addBookmark(BookmarkTargetType targetType, Long targetId, Long userId) {
- if (targetType == BookmarkTargetType.CARD_SET) {
- if (!cardSetGrpcClient.isCardSetViewable(targetId, userId)) {
- throw new BizException(CommonErrorCode.TARGET_NOT_VIEWABLE);
- }
- }
-
- if (bookmarkReader.isBookmarked(targetType, targetId, userId)) {
- throw new BizException(BookmarkErrorCode.ALREADY_BOOKMARKED);
- }
-
- Bookmark bookmark = Bookmark.builder()
- .targetType(targetType)
- .targetId(targetId)
- .userId(userId)
- .build();
-
- try {
- bookmarkRepository.save(bookmark);
- } catch (DataIntegrityViolationException e) {
- throw new BizException(BookmarkErrorCode.ALREADY_BOOKMARKED);
- }
-
- eventPublisher.publish(RabbitMqConfig.ROUTING_KEY_BOOKMARK_ADDED, "BOOKMARK_ADDED",
- targetType.name(), targetId, userId);
-
- return IdResponse.from(bookmark.getId());
- }
-
- @Transactional
- public void removeBookmark(BookmarkTargetType targetType, Long targetId, Long userId) {
- Bookmark bookmark = bookmarkReader.findByTargetAndUserId(targetType, targetId, userId);
- bookmarkRepository.delete(bookmark);
-
- eventPublisher.publish(RabbitMqConfig.ROUTING_KEY_BOOKMARK_REMOVED, "BOOKMARK_REMOVED",
- targetType.name(), targetId, userId);
- }
-
- public PagingResponse getBookmarks(BookmarkTargetType targetType, Long userId,
- BookmarkSearchRequest request) {
- Page bookmarkPage = bookmarkRepository.findByTargetTypeAndUserId(
- targetType, userId, request.getPageRequest()
- );
-
- List targetIds = bookmarkPage.getContent().stream()
- .map(Bookmark::getTargetId)
- .toList();
-
- Map summaryMap = targetIds.isEmpty()
- ? Map.of()
- : cardSetGrpcClient.getCardSetsByIds(targetIds, userId);
-
- Page responsePage = bookmarkPage.map(
- bookmark -> BookmarkResponse.from(bookmark, summaryMap.get(bookmark.getTargetId()))
- );
- return PagingResponse.from(responsePage);
- }
-}
diff --git a/src/main/java/flipnote/reaction/common/event/ReactionEventPublisher.java b/src/main/java/flipnote/reaction/common/event/ReactionEventPublisher.java
deleted file mode 100644
index 92729c0..0000000
--- a/src/main/java/flipnote/reaction/common/event/ReactionEventPublisher.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package flipnote.reaction.common.event;
-
-import org.springframework.amqp.rabbit.core.RabbitTemplate;
-import org.springframework.stereotype.Component;
-
-import flipnote.reaction.common.config.RabbitMqConfig;
-import flipnote.reaction.common.model.event.ReactionMessage;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-@Component
-@RequiredArgsConstructor
-public class ReactionEventPublisher {
-
- private final RabbitTemplate rabbitTemplate;
-
- public void publish(String routingKey, String eventType,
- String targetType, Long targetId, Long userId) {
- try {
- rabbitTemplate.convertAndSend(
- RabbitMqConfig.EXCHANGE,
- routingKey,
- new ReactionMessage(eventType, targetType, targetId, userId)
- );
- } catch (Exception e) {
- log.error("μ΄λ²€νΈ λ°ν μ€ν¨: eventType={}, targetType={}, targetId={}, userId={}",
- eventType, targetType, targetId, userId, e);
- }
- }
-}
diff --git a/src/main/java/flipnote/reaction/common/grpc/GrpcConfig.java b/src/main/java/flipnote/reaction/common/grpc/GrpcConfig.java
deleted file mode 100644
index d131067..0000000
--- a/src/main/java/flipnote/reaction/common/grpc/GrpcConfig.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package flipnote.reaction.common.grpc;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import io.grpc.ManagedChannel;
-import io.grpc.ManagedChannelBuilder;
-
-@Configuration
-public class GrpcConfig {
-
- @Bean
- public ManagedChannel cardSetChannel(
- @Value("${grpc.cardset.host}") String host,
- @Value("${grpc.cardset.port}") int port
- ) {
- return ManagedChannelBuilder.forAddress(host, port)
- .usePlaintext()
- .build();
- }
-}
diff --git a/src/main/java/flipnote/reaction/common/grpc/GrpcServerConfig.java b/src/main/java/flipnote/reaction/common/grpc/GrpcServerConfig.java
deleted file mode 100644
index 9fd49cc..0000000
--- a/src/main/java/flipnote/reaction/common/grpc/GrpcServerConfig.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package flipnote.reaction.common.grpc;
-
-import java.io.IOException;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.SmartLifecycle;
-import org.springframework.stereotype.Component;
-
-import io.grpc.Server;
-import io.grpc.ServerBuilder;
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-@Component
-public class GrpcServerConfig implements SmartLifecycle {
-
- private final Server server;
- private boolean running;
-
- public GrpcServerConfig(
- @Value("${grpc.server.port}") int port,
- ReactionGrpcService reactionGrpcService
- ) {
- this.server = ServerBuilder.forPort(port)
- .addService(reactionGrpcService)
- .build();
- }
-
- @Override
- public void start() {
- try {
- server.start();
- running = true;
- log.info("gRPC server started on port {}", server.getPort());
- } catch (IOException e) {
- throw new RuntimeException("Failed to start gRPC server", e);
- }
- }
-
- @Override
- public void stop() {
- if (server != null) {
- server.shutdown();
- running = false;
- log.info("gRPC server stopped");
- }
- }
-
- @Override
- public boolean isRunning() {
- return running;
- }
-}
diff --git a/src/main/java/flipnote/reaction/bookmark/entity/Bookmark.java b/src/main/java/flipnote/reaction/domain/bookmark/Bookmark.java
similarity index 93%
rename from src/main/java/flipnote/reaction/bookmark/entity/Bookmark.java
rename to src/main/java/flipnote/reaction/domain/bookmark/Bookmark.java
index cb6f125..eee6a1e 100644
--- a/src/main/java/flipnote/reaction/bookmark/entity/Bookmark.java
+++ b/src/main/java/flipnote/reaction/domain/bookmark/Bookmark.java
@@ -1,6 +1,6 @@
-package flipnote.reaction.bookmark.entity;
+package flipnote.reaction.domain.bookmark;
-import flipnote.reaction.common.entity.BaseEntity;
+import flipnote.reaction.domain.common.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
diff --git a/src/main/java/flipnote/reaction/bookmark/exception/BookmarkErrorCode.java b/src/main/java/flipnote/reaction/domain/bookmark/BookmarkErrorCode.java
similarity index 83%
rename from src/main/java/flipnote/reaction/bookmark/exception/BookmarkErrorCode.java
rename to src/main/java/flipnote/reaction/domain/bookmark/BookmarkErrorCode.java
index e9eb2e3..f9a0d81 100644
--- a/src/main/java/flipnote/reaction/bookmark/exception/BookmarkErrorCode.java
+++ b/src/main/java/flipnote/reaction/domain/bookmark/BookmarkErrorCode.java
@@ -1,6 +1,6 @@
-package flipnote.reaction.bookmark.exception;
+package flipnote.reaction.domain.bookmark;
-import flipnote.reaction.common.exception.ErrorCode;
+import flipnote.reaction.domain.common.ErrorCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
diff --git a/src/main/java/flipnote/reaction/domain/bookmark/BookmarkRepository.java b/src/main/java/flipnote/reaction/domain/bookmark/BookmarkRepository.java
new file mode 100644
index 0000000..ee3769d
--- /dev/null
+++ b/src/main/java/flipnote/reaction/domain/bookmark/BookmarkRepository.java
@@ -0,0 +1,19 @@
+package flipnote.reaction.domain.bookmark;
+
+import java.util.Optional;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+public interface BookmarkRepository {
+
+ boolean existsByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId);
+
+ Optional findByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId);
+
+ Page findByTargetTypeAndUserId(BookmarkTargetType targetType, Long userId, Pageable pageable);
+
+ Bookmark save(Bookmark bookmark);
+
+ void delete(Bookmark bookmark);
+}
diff --git a/src/main/java/flipnote/reaction/bookmark/entity/BookmarkTargetType.java b/src/main/java/flipnote/reaction/domain/bookmark/BookmarkTargetType.java
similarity index 51%
rename from src/main/java/flipnote/reaction/bookmark/entity/BookmarkTargetType.java
rename to src/main/java/flipnote/reaction/domain/bookmark/BookmarkTargetType.java
index 1dfecab..60b01f9 100644
--- a/src/main/java/flipnote/reaction/bookmark/entity/BookmarkTargetType.java
+++ b/src/main/java/flipnote/reaction/domain/bookmark/BookmarkTargetType.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.bookmark.entity;
+package flipnote.reaction.domain.bookmark;
public enum BookmarkTargetType {
CARD_SET
diff --git a/src/main/java/flipnote/reaction/domain/bookmark/event/BookmarkAddedEvent.java b/src/main/java/flipnote/reaction/domain/bookmark/event/BookmarkAddedEvent.java
new file mode 100644
index 0000000..87421e2
--- /dev/null
+++ b/src/main/java/flipnote/reaction/domain/bookmark/event/BookmarkAddedEvent.java
@@ -0,0 +1,8 @@
+package flipnote.reaction.domain.bookmark.event;
+
+public record BookmarkAddedEvent(
+ String targetType,
+ Long targetId,
+ Long userId
+) {
+}
diff --git a/src/main/java/flipnote/reaction/domain/bookmark/event/BookmarkRemovedEvent.java b/src/main/java/flipnote/reaction/domain/bookmark/event/BookmarkRemovedEvent.java
new file mode 100644
index 0000000..34915b4
--- /dev/null
+++ b/src/main/java/flipnote/reaction/domain/bookmark/event/BookmarkRemovedEvent.java
@@ -0,0 +1,8 @@
+package flipnote.reaction.domain.bookmark.event;
+
+public record BookmarkRemovedEvent(
+ String targetType,
+ Long targetId,
+ Long userId
+) {
+}
diff --git a/src/main/java/flipnote/reaction/common/entity/BaseEntity.java b/src/main/java/flipnote/reaction/domain/common/BaseEntity.java
similarity index 93%
rename from src/main/java/flipnote/reaction/common/entity/BaseEntity.java
rename to src/main/java/flipnote/reaction/domain/common/BaseEntity.java
index 094ff68..07b0bc6 100644
--- a/src/main/java/flipnote/reaction/common/entity/BaseEntity.java
+++ b/src/main/java/flipnote/reaction/domain/common/BaseEntity.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.entity;
+package flipnote.reaction.domain.common;
import java.time.LocalDateTime;
diff --git a/src/main/java/flipnote/reaction/common/exception/BizException.java b/src/main/java/flipnote/reaction/domain/common/BizException.java
similarity index 79%
rename from src/main/java/flipnote/reaction/common/exception/BizException.java
rename to src/main/java/flipnote/reaction/domain/common/BizException.java
index db85b00..7f34bc2 100644
--- a/src/main/java/flipnote/reaction/common/exception/BizException.java
+++ b/src/main/java/flipnote/reaction/domain/common/BizException.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.exception;
+package flipnote.reaction.domain.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
diff --git a/src/main/java/flipnote/reaction/common/exception/CommonErrorCode.java b/src/main/java/flipnote/reaction/domain/common/CommonErrorCode.java
similarity index 92%
rename from src/main/java/flipnote/reaction/common/exception/CommonErrorCode.java
rename to src/main/java/flipnote/reaction/domain/common/CommonErrorCode.java
index 349b6f4..c3e21ff 100644
--- a/src/main/java/flipnote/reaction/common/exception/CommonErrorCode.java
+++ b/src/main/java/flipnote/reaction/domain/common/CommonErrorCode.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.exception;
+package flipnote.reaction.domain.common;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
diff --git a/src/main/java/flipnote/reaction/common/exception/ErrorCode.java b/src/main/java/flipnote/reaction/domain/common/ErrorCode.java
similarity index 67%
rename from src/main/java/flipnote/reaction/common/exception/ErrorCode.java
rename to src/main/java/flipnote/reaction/domain/common/ErrorCode.java
index 5841bc8..5bde461 100644
--- a/src/main/java/flipnote/reaction/common/exception/ErrorCode.java
+++ b/src/main/java/flipnote/reaction/domain/common/ErrorCode.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.exception;
+package flipnote.reaction.domain.common;
public interface ErrorCode {
int getStatus();
diff --git a/src/main/java/flipnote/reaction/like/entity/Like.java b/src/main/java/flipnote/reaction/domain/like/Like.java
similarity index 93%
rename from src/main/java/flipnote/reaction/like/entity/Like.java
rename to src/main/java/flipnote/reaction/domain/like/Like.java
index 446662c..3863da9 100644
--- a/src/main/java/flipnote/reaction/like/entity/Like.java
+++ b/src/main/java/flipnote/reaction/domain/like/Like.java
@@ -1,6 +1,6 @@
-package flipnote.reaction.like.entity;
+package flipnote.reaction.domain.like;
-import flipnote.reaction.common.entity.BaseEntity;
+import flipnote.reaction.domain.common.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
diff --git a/src/main/java/flipnote/reaction/like/exception/LikeErrorCode.java b/src/main/java/flipnote/reaction/domain/like/LikeErrorCode.java
similarity index 83%
rename from src/main/java/flipnote/reaction/like/exception/LikeErrorCode.java
rename to src/main/java/flipnote/reaction/domain/like/LikeErrorCode.java
index d69903b..f7733f3 100644
--- a/src/main/java/flipnote/reaction/like/exception/LikeErrorCode.java
+++ b/src/main/java/flipnote/reaction/domain/like/LikeErrorCode.java
@@ -1,6 +1,6 @@
-package flipnote.reaction.like.exception;
+package flipnote.reaction.domain.like;
-import flipnote.reaction.common.exception.ErrorCode;
+import flipnote.reaction.domain.common.ErrorCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
diff --git a/src/main/java/flipnote/reaction/domain/like/LikeRepository.java b/src/main/java/flipnote/reaction/domain/like/LikeRepository.java
new file mode 100644
index 0000000..8d32323
--- /dev/null
+++ b/src/main/java/flipnote/reaction/domain/like/LikeRepository.java
@@ -0,0 +1,19 @@
+package flipnote.reaction.domain.like;
+
+import java.util.Optional;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+public interface LikeRepository {
+
+ boolean existsByTargetTypeAndTargetIdAndUserId(LikeTargetType targetType, Long targetId, Long userId);
+
+ Optional findByTargetTypeAndTargetIdAndUserId(LikeTargetType targetType, Long targetId, Long userId);
+
+ Page findByTargetTypeAndUserId(LikeTargetType targetType, Long userId, Pageable pageable);
+
+ Like save(Like like);
+
+ void delete(Like like);
+}
diff --git a/src/main/java/flipnote/reaction/like/entity/LikeTargetType.java b/src/main/java/flipnote/reaction/domain/like/LikeTargetType.java
similarity index 51%
rename from src/main/java/flipnote/reaction/like/entity/LikeTargetType.java
rename to src/main/java/flipnote/reaction/domain/like/LikeTargetType.java
index b23ce70..45e81ba 100644
--- a/src/main/java/flipnote/reaction/like/entity/LikeTargetType.java
+++ b/src/main/java/flipnote/reaction/domain/like/LikeTargetType.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.like.entity;
+package flipnote.reaction.domain.like;
public enum LikeTargetType {
CARD_SET
diff --git a/src/main/java/flipnote/reaction/domain/like/event/LikeAddedEvent.java b/src/main/java/flipnote/reaction/domain/like/event/LikeAddedEvent.java
new file mode 100644
index 0000000..01e0876
--- /dev/null
+++ b/src/main/java/flipnote/reaction/domain/like/event/LikeAddedEvent.java
@@ -0,0 +1,8 @@
+package flipnote.reaction.domain.like.event;
+
+public record LikeAddedEvent(
+ String targetType,
+ Long targetId,
+ Long userId
+) {
+}
diff --git a/src/main/java/flipnote/reaction/domain/like/event/LikeRemovedEvent.java b/src/main/java/flipnote/reaction/domain/like/event/LikeRemovedEvent.java
new file mode 100644
index 0000000..2ac8e54
--- /dev/null
+++ b/src/main/java/flipnote/reaction/domain/like/event/LikeRemovedEvent.java
@@ -0,0 +1,8 @@
+package flipnote.reaction.domain.like.event;
+
+public record LikeRemovedEvent(
+ String targetType,
+ Long targetId,
+ Long userId
+) {
+}
diff --git a/src/main/java/flipnote/reaction/common/grpc/CardSetGrpcClient.java b/src/main/java/flipnote/reaction/infrastructure/grpc/CardSetGrpcClient.java
similarity index 67%
rename from src/main/java/flipnote/reaction/common/grpc/CardSetGrpcClient.java
rename to src/main/java/flipnote/reaction/infrastructure/grpc/CardSetGrpcClient.java
index ab5104c..3749a79 100644
--- a/src/main/java/flipnote/reaction/common/grpc/CardSetGrpcClient.java
+++ b/src/main/java/flipnote/reaction/infrastructure/grpc/CardSetGrpcClient.java
@@ -1,21 +1,19 @@
-package flipnote.reaction.common.grpc;
+package flipnote.reaction.infrastructure.grpc;
import java.util.List;
import java.util.Map;
-import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.stereotype.Component;
-import cardset.Cardset.CardSetSummary;
import cardset.Cardset.GetCardSetsByIdsRequest;
import cardset.Cardset.GetCardSetsByIdsResponse;
import cardset.Cardset.IsCardSetViewableRequest;
import cardset.Cardset.IsCardSetViewableResponse;
import cardset.CardsetServiceGrpc;
-import flipnote.reaction.common.exception.BizException;
-import flipnote.reaction.common.exception.CommonErrorCode;
-import io.grpc.ManagedChannel;
+import flipnote.reaction.application.common.CardSetSummaryResult;
+import flipnote.reaction.domain.common.BizException;
+import flipnote.reaction.domain.common.CommonErrorCode;
import io.grpc.StatusRuntimeException;
import lombok.extern.slf4j.Slf4j;
@@ -25,15 +23,15 @@ public class CardSetGrpcClient {
private final CardsetServiceGrpc.CardsetServiceBlockingStub stub;
- public CardSetGrpcClient(ManagedChannel cardSetChannel) {
- this.stub = CardsetServiceGrpc.newBlockingStub(cardSetChannel);
+ public CardSetGrpcClient(CardsetServiceGrpc.CardsetServiceBlockingStub cardSetStub) {
+ this.stub = cardSetStub;
}
public boolean isCardSetViewable(Long cardSetId, Long userId) {
try {
IsCardSetViewableRequest request = IsCardSetViewableRequest.newBuilder()
- .setCardSetId(cardSetId.intValue())
- .setUserId(userId.intValue())
+ .setCardSetId(cardSetId)
+ .setUserId(userId)
.build();
IsCardSetViewableResponse response = stub.isCardSetViewable(request);
@@ -44,18 +42,27 @@ public boolean isCardSetViewable(Long cardSetId, Long userId) {
}
}
- public Map getCardSetsByIds(List cardSetIds, Long userId) {
+ public Map getCardSetsByIds(List cardSetIds, Long userId) {
try {
GetCardSetsByIdsRequest request = GetCardSetsByIdsRequest.newBuilder()
.addAllCardSetIds(cardSetIds)
- .setUserId(userId.intValue())
+ .setUserId(userId)
.build();
GetCardSetsByIdsResponse response = stub.getCardSetsByIds(request);
return response.getCardSetsList().stream()
.collect(Collectors.toMap(
cs -> (long) cs.getId(),
- Function.identity()
+ cs -> new CardSetSummaryResult(
+ (long) cs.getId(),
+ cs.getName(),
+ (long) cs.getGroupId(),
+ cs.getVisibility(),
+ cs.getCategory(),
+ cs.getHashtag(),
+ (long) cs.getImageRefId(),
+ (long) cs.getCardCount()
+ )
));
} catch (StatusRuntimeException e) {
log.error("gRPC call failed: GetCardSetsByIds, cardSetIds={}, userId={}", cardSetIds, userId, e);
diff --git a/src/main/java/flipnote/reaction/infrastructure/grpc/GrpcConfig.java b/src/main/java/flipnote/reaction/infrastructure/grpc/GrpcConfig.java
new file mode 100644
index 0000000..643448a
--- /dev/null
+++ b/src/main/java/flipnote/reaction/infrastructure/grpc/GrpcConfig.java
@@ -0,0 +1,16 @@
+package flipnote.reaction.infrastructure.grpc;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.grpc.client.GrpcChannelFactory;
+
+import cardset.CardsetServiceGrpc;
+
+@Configuration
+public class GrpcConfig {
+
+ @Bean
+ public CardsetServiceGrpc.CardsetServiceBlockingStub cardSetStub(GrpcChannelFactory grpcChannelFactory) {
+ return CardsetServiceGrpc.newBlockingStub(grpcChannelFactory.createChannel("cardset"));
+ }
+}
diff --git a/src/main/java/flipnote/reaction/common/config/RabbitMqConfig.java b/src/main/java/flipnote/reaction/infrastructure/messaging/RabbitMqConfig.java
similarity index 82%
rename from src/main/java/flipnote/reaction/common/config/RabbitMqConfig.java
rename to src/main/java/flipnote/reaction/infrastructure/messaging/RabbitMqConfig.java
index 563ee22..6c8cf81 100644
--- a/src/main/java/flipnote/reaction/common/config/RabbitMqConfig.java
+++ b/src/main/java/flipnote/reaction/infrastructure/messaging/RabbitMqConfig.java
@@ -1,7 +1,7 @@
-package flipnote.reaction.common.config;
+package flipnote.reaction.infrastructure.messaging;
import org.springframework.amqp.core.TopicExchange;
-import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+import org.springframework.amqp.support.converter.JacksonJsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -23,6 +23,6 @@ public TopicExchange reactionExchange() {
@Bean
public MessageConverter jackson2JsonMessageConverter() {
- return new Jackson2JsonMessageConverter();
+ return new JacksonJsonMessageConverter();
}
}
diff --git a/src/main/java/flipnote/reaction/infrastructure/messaging/ReactionEventListener.java b/src/main/java/flipnote/reaction/infrastructure/messaging/ReactionEventListener.java
new file mode 100644
index 0000000..c6b0fb8
--- /dev/null
+++ b/src/main/java/flipnote/reaction/infrastructure/messaging/ReactionEventListener.java
@@ -0,0 +1,38 @@
+package flipnote.reaction.infrastructure.messaging;
+
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.event.TransactionPhase;
+import org.springframework.transaction.event.TransactionalEventListener;
+
+import flipnote.reaction.domain.bookmark.event.BookmarkAddedEvent;
+import flipnote.reaction.domain.bookmark.event.BookmarkRemovedEvent;
+import flipnote.reaction.domain.like.event.LikeAddedEvent;
+import flipnote.reaction.domain.like.event.LikeRemovedEvent;
+import lombok.RequiredArgsConstructor;
+
+@Component
+@RequiredArgsConstructor
+public class ReactionEventListener {
+
+ private final ReactionEventPublisher eventPublisher;
+
+ @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
+ public void onBookmarkAdded(BookmarkAddedEvent event) {
+ eventPublisher.bookmarkAdded(event.targetType(), event.targetId(), event.userId());
+ }
+
+ @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
+ public void onBookmarkRemoved(BookmarkRemovedEvent event) {
+ eventPublisher.bookmarkRemoved(event.targetType(), event.targetId(), event.userId());
+ }
+
+ @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
+ public void onLikeAdded(LikeAddedEvent event) {
+ eventPublisher.likeAdded(event.targetType(), event.targetId(), event.userId());
+ }
+
+ @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
+ public void onLikeRemoved(LikeRemovedEvent event) {
+ eventPublisher.likeRemoved(event.targetType(), event.targetId(), event.userId());
+ }
+}
diff --git a/src/main/java/flipnote/reaction/infrastructure/messaging/ReactionEventPublisher.java b/src/main/java/flipnote/reaction/infrastructure/messaging/ReactionEventPublisher.java
new file mode 100644
index 0000000..a41670a
--- /dev/null
+++ b/src/main/java/flipnote/reaction/infrastructure/messaging/ReactionEventPublisher.java
@@ -0,0 +1,44 @@
+package flipnote.reaction.infrastructure.messaging;
+
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.stereotype.Component;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class ReactionEventPublisher {
+
+ private final RabbitTemplate rabbitTemplate;
+
+ public void bookmarkAdded(String targetType, Long targetId, Long userId) {
+ publish(RabbitMqConfig.ROUTING_KEY_BOOKMARK_ADDED, "BOOKMARK_ADDED", targetType, targetId, userId);
+ }
+
+ public void bookmarkRemoved(String targetType, Long targetId, Long userId) {
+ publish(RabbitMqConfig.ROUTING_KEY_BOOKMARK_REMOVED, "BOOKMARK_REMOVED", targetType, targetId, userId);
+ }
+
+ public void likeAdded(String targetType, Long targetId, Long userId) {
+ publish(RabbitMqConfig.ROUTING_KEY_LIKE_ADDED, "LIKE_ADDED", targetType, targetId, userId);
+ }
+
+ public void likeRemoved(String targetType, Long targetId, Long userId) {
+ publish(RabbitMqConfig.ROUTING_KEY_LIKE_REMOVED, "LIKE_REMOVED", targetType, targetId, userId);
+ }
+
+ private void publish(String routingKey, String eventType, String targetType, Long targetId, Long userId) {
+ try {
+ rabbitTemplate.convertAndSend(
+ RabbitMqConfig.EXCHANGE,
+ routingKey,
+ new ReactionMessage(eventType, targetType, targetId, userId)
+ );
+ } catch (Exception e) {
+ log.error("μ΄λ²€νΈ λ°ν μ€ν¨: eventType={}, targetType={}, targetId={}, userId={}",
+ eventType, targetType, targetId, userId, e);
+ }
+ }
+}
diff --git a/src/main/java/flipnote/reaction/common/model/event/ReactionMessage.java b/src/main/java/flipnote/reaction/infrastructure/messaging/ReactionMessage.java
similarity index 67%
rename from src/main/java/flipnote/reaction/common/model/event/ReactionMessage.java
rename to src/main/java/flipnote/reaction/infrastructure/messaging/ReactionMessage.java
index e55b025..015059d 100644
--- a/src/main/java/flipnote/reaction/common/model/event/ReactionMessage.java
+++ b/src/main/java/flipnote/reaction/infrastructure/messaging/ReactionMessage.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.model.event;
+package flipnote.reaction.infrastructure.messaging;
public record ReactionMessage(
String eventType,
diff --git a/src/main/java/flipnote/reaction/common/config/AuditingConfig.java b/src/main/java/flipnote/reaction/infrastructure/persistence/AuditingConfig.java
similarity index 78%
rename from src/main/java/flipnote/reaction/common/config/AuditingConfig.java
rename to src/main/java/flipnote/reaction/infrastructure/persistence/AuditingConfig.java
index e930b9a..87a5431 100644
--- a/src/main/java/flipnote/reaction/common/config/AuditingConfig.java
+++ b/src/main/java/flipnote/reaction/infrastructure/persistence/AuditingConfig.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.config;
+package flipnote.reaction.infrastructure.persistence;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
diff --git a/src/main/java/flipnote/reaction/infrastructure/persistence/BookmarkRepositoryAdapter.java b/src/main/java/flipnote/reaction/infrastructure/persistence/BookmarkRepositoryAdapter.java
new file mode 100644
index 0000000..bc63c24
--- /dev/null
+++ b/src/main/java/flipnote/reaction/infrastructure/persistence/BookmarkRepositoryAdapter.java
@@ -0,0 +1,45 @@
+package flipnote.reaction.infrastructure.persistence;
+
+import java.util.Optional;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Repository;
+
+import flipnote.reaction.domain.bookmark.Bookmark;
+import flipnote.reaction.domain.bookmark.BookmarkRepository;
+import flipnote.reaction.domain.bookmark.BookmarkTargetType;
+import lombok.RequiredArgsConstructor;
+
+@Repository
+@RequiredArgsConstructor
+public class BookmarkRepositoryAdapter implements BookmarkRepository {
+
+ private final SpringDataBookmarkRepository springDataBookmarkRepository;
+
+ @Override
+ public boolean existsByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId) {
+ return springDataBookmarkRepository.existsByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId);
+ }
+
+ @Override
+ public Optional findByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId,
+ Long userId) {
+ return springDataBookmarkRepository.findByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId);
+ }
+
+ @Override
+ public Page findByTargetTypeAndUserId(BookmarkTargetType targetType, Long userId, Pageable pageable) {
+ return springDataBookmarkRepository.findByTargetTypeAndUserId(targetType, userId, pageable);
+ }
+
+ @Override
+ public Bookmark save(Bookmark bookmark) {
+ return springDataBookmarkRepository.save(bookmark);
+ }
+
+ @Override
+ public void delete(Bookmark bookmark) {
+ springDataBookmarkRepository.delete(bookmark);
+ }
+}
diff --git a/src/main/java/flipnote/reaction/infrastructure/persistence/LikeRepositoryAdapter.java b/src/main/java/flipnote/reaction/infrastructure/persistence/LikeRepositoryAdapter.java
new file mode 100644
index 0000000..ec643f0
--- /dev/null
+++ b/src/main/java/flipnote/reaction/infrastructure/persistence/LikeRepositoryAdapter.java
@@ -0,0 +1,44 @@
+package flipnote.reaction.infrastructure.persistence;
+
+import java.util.Optional;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Repository;
+
+import flipnote.reaction.domain.like.Like;
+import flipnote.reaction.domain.like.LikeRepository;
+import flipnote.reaction.domain.like.LikeTargetType;
+import lombok.RequiredArgsConstructor;
+
+@Repository
+@RequiredArgsConstructor
+public class LikeRepositoryAdapter implements LikeRepository {
+
+ private final SpringDataLikeRepository springDataLikeRepository;
+
+ @Override
+ public boolean existsByTargetTypeAndTargetIdAndUserId(LikeTargetType targetType, Long targetId, Long userId) {
+ return springDataLikeRepository.existsByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId);
+ }
+
+ @Override
+ public Optional findByTargetTypeAndTargetIdAndUserId(LikeTargetType targetType, Long targetId, Long userId) {
+ return springDataLikeRepository.findByTargetTypeAndTargetIdAndUserId(targetType, targetId, userId);
+ }
+
+ @Override
+ public Page findByTargetTypeAndUserId(LikeTargetType targetType, Long userId, Pageable pageable) {
+ return springDataLikeRepository.findByTargetTypeAndUserId(targetType, userId, pageable);
+ }
+
+ @Override
+ public Like save(Like like) {
+ return springDataLikeRepository.save(like);
+ }
+
+ @Override
+ public void delete(Like like) {
+ springDataLikeRepository.delete(like);
+ }
+}
diff --git a/src/main/java/flipnote/reaction/bookmark/repository/BookmarkRepository.java b/src/main/java/flipnote/reaction/infrastructure/persistence/SpringDataBookmarkRepository.java
similarity index 57%
rename from src/main/java/flipnote/reaction/bookmark/repository/BookmarkRepository.java
rename to src/main/java/flipnote/reaction/infrastructure/persistence/SpringDataBookmarkRepository.java
index 2ad22f3..769ef96 100644
--- a/src/main/java/flipnote/reaction/bookmark/repository/BookmarkRepository.java
+++ b/src/main/java/flipnote/reaction/infrastructure/persistence/SpringDataBookmarkRepository.java
@@ -1,22 +1,19 @@
-package flipnote.reaction.bookmark.repository;
+package flipnote.reaction.infrastructure.persistence;
-import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
-import flipnote.reaction.bookmark.entity.Bookmark;
-import flipnote.reaction.bookmark.entity.BookmarkTargetType;
+import flipnote.reaction.domain.bookmark.Bookmark;
+import flipnote.reaction.domain.bookmark.BookmarkTargetType;
-public interface BookmarkRepository extends JpaRepository {
+interface SpringDataBookmarkRepository extends JpaRepository {
boolean existsByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId);
Optional findByTargetTypeAndTargetIdAndUserId(BookmarkTargetType targetType, Long targetId, Long userId);
Page findByTargetTypeAndUserId(BookmarkTargetType targetType, Long userId, Pageable pageable);
-
- List findByTargetTypeAndTargetIdInAndUserId(BookmarkTargetType targetType, List targetIds, Long userId);
}
diff --git a/src/main/java/flipnote/reaction/like/repository/LikeRepository.java b/src/main/java/flipnote/reaction/infrastructure/persistence/SpringDataLikeRepository.java
similarity index 59%
rename from src/main/java/flipnote/reaction/like/repository/LikeRepository.java
rename to src/main/java/flipnote/reaction/infrastructure/persistence/SpringDataLikeRepository.java
index 6003798..b189cb1 100644
--- a/src/main/java/flipnote/reaction/like/repository/LikeRepository.java
+++ b/src/main/java/flipnote/reaction/infrastructure/persistence/SpringDataLikeRepository.java
@@ -1,22 +1,19 @@
-package flipnote.reaction.like.repository;
+package flipnote.reaction.infrastructure.persistence;
-import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
-import flipnote.reaction.like.entity.Like;
-import flipnote.reaction.like.entity.LikeTargetType;
+import flipnote.reaction.domain.like.Like;
+import flipnote.reaction.domain.like.LikeTargetType;
-public interface LikeRepository extends JpaRepository {
+interface SpringDataLikeRepository extends JpaRepository {
boolean existsByTargetTypeAndTargetIdAndUserId(LikeTargetType targetType, Long targetId, Long userId);
Optional findByTargetTypeAndTargetIdAndUserId(LikeTargetType targetType, Long targetId, Long userId);
Page findByTargetTypeAndUserId(LikeTargetType targetType, Long userId, Pageable pageable);
-
- List findByTargetTypeAndTargetIdInAndUserId(LikeTargetType targetType, List targetIds, Long userId);
}
diff --git a/src/main/java/flipnote/reaction/interfaces/grpc/GrpcExceptionHandlerImpl.java b/src/main/java/flipnote/reaction/interfaces/grpc/GrpcExceptionHandlerImpl.java
new file mode 100644
index 0000000..8f78899
--- /dev/null
+++ b/src/main/java/flipnote/reaction/interfaces/grpc/GrpcExceptionHandlerImpl.java
@@ -0,0 +1,52 @@
+package flipnote.reaction.interfaces.grpc;
+
+import org.springframework.grpc.server.exception.GrpcExceptionHandler;
+import org.springframework.stereotype.Component;
+
+import flipnote.reaction.domain.common.BizException;
+import flipnote.reaction.domain.common.ErrorCode;
+import io.grpc.Status;
+import io.grpc.StatusException;
+import io.grpc.StatusRuntimeException;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class GrpcExceptionHandlerImpl implements GrpcExceptionHandler {
+
+ @Override
+ public StatusException handleException(Throwable t) {
+ if (t instanceof BizException e) {
+ ErrorCode errorCode = e.getErrorCode();
+ log.warn("gRPC BizException: code={}, status={}, message={}",
+ errorCode.getCode(), errorCode.getStatus(), errorCode.getMessage());
+ return toGrpcStatus(errorCode)
+ .withDescription(errorCode.getMessage())
+ .asException();
+ }
+ if (t instanceof StatusException e) {
+ log.warn("gRPC StatusException: status={}, description={}",
+ e.getStatus().getCode(), e.getStatus().getDescription());
+ return e;
+ }
+ if (t instanceof StatusRuntimeException e) {
+ log.warn("gRPC StatusRuntimeException: status={}, description={}",
+ e.getStatus().getCode(), e.getStatus().getDescription());
+ return e.getStatus().asException(e.getTrailers());
+ }
+ log.error("gRPC Unhandled exception", t);
+ return Status.INTERNAL.withDescription("Internal server error").asException();
+ }
+
+ private Status toGrpcStatus(ErrorCode errorCode) {
+ return switch (errorCode.getStatus()) {
+ case 400 -> Status.INVALID_ARGUMENT;
+ case 401 -> Status.UNAUTHENTICATED;
+ case 403 -> Status.PERMISSION_DENIED;
+ case 404 -> Status.NOT_FOUND;
+ case 409 -> Status.ALREADY_EXISTS;
+ case 429 -> Status.RESOURCE_EXHAUSTED;
+ default -> Status.INTERNAL;
+ };
+ }
+}
diff --git a/src/main/java/flipnote/reaction/common/grpc/ReactionGrpcService.java b/src/main/java/flipnote/reaction/interfaces/grpc/ReactionGrpcService.java
similarity index 63%
rename from src/main/java/flipnote/reaction/common/grpc/ReactionGrpcService.java
rename to src/main/java/flipnote/reaction/interfaces/grpc/ReactionGrpcService.java
index 0be2c88..e599b2d 100644
--- a/src/main/java/flipnote/reaction/common/grpc/ReactionGrpcService.java
+++ b/src/main/java/flipnote/reaction/interfaces/grpc/ReactionGrpcService.java
@@ -1,67 +1,71 @@
-package flipnote.reaction.common.grpc;
+package flipnote.reaction.interfaces.grpc;
import java.util.List;
import java.util.Map;
-import org.springframework.stereotype.Component;
+import org.springframework.grpc.server.service.GrpcService;
-import flipnote.reaction.bookmark.entity.BookmarkTargetType;
-import flipnote.reaction.bookmark.service.BookmarkReader;
-import flipnote.reaction.like.entity.LikeTargetType;
-import flipnote.reaction.like.service.LikeReader;
+import flipnote.reaction.application.bookmark.BookmarkReader;
+import flipnote.reaction.application.like.LikeReader;
+import flipnote.reaction.domain.bookmark.BookmarkTargetType;
+import flipnote.reaction.domain.like.LikeTargetType;
+import io.grpc.Status;
import io.grpc.stub.StreamObserver;
+import lombok.RequiredArgsConstructor;
import reaction.Reaction.AreReactedRequest;
import reaction.Reaction.AreReactedResponse;
import reaction.Reaction.IsReactedRequest;
import reaction.Reaction.IsReactedResponse;
import reaction.ReactionServiceGrpc;
-@Component
+@GrpcService
+@RequiredArgsConstructor
public class ReactionGrpcService extends ReactionServiceGrpc.ReactionServiceImplBase {
private final LikeReader likeReader;
private final BookmarkReader bookmarkReader;
- public ReactionGrpcService(LikeReader likeReader, BookmarkReader bookmarkReader) {
- this.likeReader = likeReader;
- this.bookmarkReader = bookmarkReader;
- }
-
@Override
public void isLiked(IsReactedRequest request, StreamObserver responseObserver) {
- LikeTargetType targetType = LikeTargetType.valueOf(request.getTargetType());
+ LikeTargetType targetType = parseTargetType(request.getTargetType(), LikeTargetType.class);
boolean reacted = likeReader.isLiked(targetType, request.getTargetId(), request.getUserId());
-
responseObserver.onNext(IsReactedResponse.newBuilder().setReacted(reacted).build());
responseObserver.onCompleted();
}
@Override
public void areLiked(AreReactedRequest request, StreamObserver responseObserver) {
- LikeTargetType targetType = LikeTargetType.valueOf(request.getTargetType());
+ LikeTargetType targetType = parseTargetType(request.getTargetType(), LikeTargetType.class);
List targetIds = request.getTargetIdsList();
Map results = likeReader.areLiked(targetType, targetIds, request.getUserId());
-
responseObserver.onNext(AreReactedResponse.newBuilder().putAllResults(results).build());
responseObserver.onCompleted();
}
@Override
public void isBookmarked(IsReactedRequest request, StreamObserver responseObserver) {
- BookmarkTargetType targetType = BookmarkTargetType.valueOf(request.getTargetType());
+ BookmarkTargetType targetType = parseTargetType(request.getTargetType(), BookmarkTargetType.class);
boolean reacted = bookmarkReader.isBookmarked(targetType, request.getTargetId(), request.getUserId());
-
responseObserver.onNext(IsReactedResponse.newBuilder().setReacted(reacted).build());
responseObserver.onCompleted();
}
@Override
public void areBookmarked(AreReactedRequest request, StreamObserver responseObserver) {
- BookmarkTargetType targetType = BookmarkTargetType.valueOf(request.getTargetType());
+ BookmarkTargetType targetType = parseTargetType(request.getTargetType(), BookmarkTargetType.class);
List targetIds = request.getTargetIdsList();
Map results = bookmarkReader.areBookmarked(targetType, targetIds, request.getUserId());
-
responseObserver.onNext(AreReactedResponse.newBuilder().putAllResults(results).build());
responseObserver.onCompleted();
}
+
+ private > T parseTargetType(String value, Class enumClass) {
+ try {
+ return Enum.valueOf(enumClass, value);
+ } catch (IllegalArgumentException e) {
+ throw Status.INVALID_ARGUMENT
+ .withDescription("Invalid target type: " + value)
+ .asRuntimeException();
+ }
+ }
}
diff --git a/src/main/java/flipnote/reaction/bookmark/controller/BookmarkController.java b/src/main/java/flipnote/reaction/interfaces/http/BookmarkController.java
similarity index 66%
rename from src/main/java/flipnote/reaction/bookmark/controller/BookmarkController.java
rename to src/main/java/flipnote/reaction/interfaces/http/BookmarkController.java
index 599657f..f47e6c3 100644
--- a/src/main/java/flipnote/reaction/bookmark/controller/BookmarkController.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/BookmarkController.java
@@ -1,6 +1,6 @@
-package flipnote.reaction.bookmark.controller;
+package flipnote.reaction.interfaces.http;
-import static flipnote.reaction.common.constants.HeaderConstants.USER_ID;
+import static flipnote.reaction.interfaces.http.common.HeaderConstants.USER_ID;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@@ -11,13 +11,13 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-import flipnote.reaction.bookmark.entity.BookmarkTargetType;
-import flipnote.reaction.bookmark.model.request.BookmarkSearchRequest;
-import flipnote.reaction.bookmark.model.request.BookmarkTargetTypeRequest;
-import flipnote.reaction.bookmark.model.response.BookmarkResponse;
-import flipnote.reaction.bookmark.service.BookmarkService;
-import flipnote.reaction.common.model.response.IdResponse;
-import flipnote.reaction.common.model.response.PagingResponse;
+import flipnote.reaction.application.bookmark.BookmarkResult;
+import flipnote.reaction.application.bookmark.BookmarkService;
+import flipnote.reaction.domain.bookmark.BookmarkTargetType;
+import flipnote.reaction.interfaces.http.common.IdResponse;
+import flipnote.reaction.interfaces.http.common.PagingResponse;
+import flipnote.reaction.interfaces.http.dto.request.BookmarkSearchRequest;
+import flipnote.reaction.interfaces.http.dto.request.BookmarkTargetTypeRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
@@ -35,7 +35,7 @@ public IdResponse addBookmark(
@PathVariable Long targetId
) {
BookmarkTargetType type = BookmarkTargetTypeRequest.from(targetType).toEntity();
- return bookmarkService.addBookmark(type, targetId, userId);
+ return IdResponse.from(bookmarkService.addBookmark(type, targetId, userId));
}
@DeleteMapping("/{targetType}/{targetId}")
@@ -49,12 +49,12 @@ public void removeBookmark(
}
@GetMapping("/{targetType}")
- public PagingResponse getBookmarks(
+ public PagingResponse getBookmarks(
@RequestHeader(USER_ID) Long userId,
@PathVariable String targetType,
@Valid @ModelAttribute BookmarkSearchRequest request
) {
BookmarkTargetType type = BookmarkTargetTypeRequest.from(targetType).toEntity();
- return bookmarkService.getBookmarks(type, userId, request);
+ return PagingResponse.from(bookmarkService.getBookmarks(type, userId, request));
}
}
diff --git a/src/main/java/flipnote/reaction/like/controller/LikeController.java b/src/main/java/flipnote/reaction/interfaces/http/LikeController.java
similarity index 66%
rename from src/main/java/flipnote/reaction/like/controller/LikeController.java
rename to src/main/java/flipnote/reaction/interfaces/http/LikeController.java
index f14fc5d..d999545 100644
--- a/src/main/java/flipnote/reaction/like/controller/LikeController.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/LikeController.java
@@ -1,6 +1,6 @@
-package flipnote.reaction.like.controller;
+package flipnote.reaction.interfaces.http;
-import static flipnote.reaction.common.constants.HeaderConstants.USER_ID;
+import static flipnote.reaction.interfaces.http.common.HeaderConstants.USER_ID;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@@ -11,13 +11,13 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-import flipnote.reaction.common.model.response.IdResponse;
-import flipnote.reaction.common.model.response.PagingResponse;
-import flipnote.reaction.like.entity.LikeTargetType;
-import flipnote.reaction.like.model.request.LikeSearchRequest;
-import flipnote.reaction.like.model.request.LikeTargetTypeRequest;
-import flipnote.reaction.like.model.response.LikeResponse;
-import flipnote.reaction.like.service.LikeService;
+import flipnote.reaction.application.like.LikeResult;
+import flipnote.reaction.application.like.LikeService;
+import flipnote.reaction.domain.like.LikeTargetType;
+import flipnote.reaction.interfaces.http.common.IdResponse;
+import flipnote.reaction.interfaces.http.common.PagingResponse;
+import flipnote.reaction.interfaces.http.dto.request.LikeSearchRequest;
+import flipnote.reaction.interfaces.http.dto.request.LikeTargetTypeRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
@@ -35,7 +35,7 @@ public IdResponse addLike(
@PathVariable Long targetId
) {
LikeTargetType type = LikeTargetTypeRequest.from(targetType).toEntity();
- return likeService.addLike(type, targetId, userId);
+ return IdResponse.from(likeService.addLike(type, targetId, userId));
}
@DeleteMapping("/{targetType}/{targetId}")
@@ -49,12 +49,12 @@ public void removeLike(
}
@GetMapping("/{targetType}")
- public PagingResponse getLikes(
+ public PagingResponse getLikes(
@RequestHeader(USER_ID) Long userId,
@PathVariable String targetType,
@Valid @ModelAttribute LikeSearchRequest request
) {
LikeTargetType type = LikeTargetTypeRequest.from(targetType).toEntity();
- return likeService.getLikes(type, userId, request);
+ return PagingResponse.from(likeService.getLikes(type, userId, request));
}
}
diff --git a/src/main/java/flipnote/reaction/common/model/response/ApiResponse.java b/src/main/java/flipnote/reaction/interfaces/http/common/ApiResponse.java
similarity index 93%
rename from src/main/java/flipnote/reaction/common/model/response/ApiResponse.java
rename to src/main/java/flipnote/reaction/interfaces/http/common/ApiResponse.java
index b8de86d..bd5cf0f 100644
--- a/src/main/java/flipnote/reaction/common/model/response/ApiResponse.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/common/ApiResponse.java
@@ -1,11 +1,11 @@
-package flipnote.reaction.common.model.response;
+package flipnote.reaction.interfaces.http.common;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.validation.BindingResult;
-import flipnote.reaction.common.exception.ErrorCode;
+import flipnote.reaction.domain.common.ErrorCode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
diff --git a/src/main/java/flipnote/reaction/common/model/response/ApiResponseAdvice.java b/src/main/java/flipnote/reaction/interfaces/http/common/ApiResponseAdvice.java
similarity index 96%
rename from src/main/java/flipnote/reaction/common/model/response/ApiResponseAdvice.java
rename to src/main/java/flipnote/reaction/interfaces/http/common/ApiResponseAdvice.java
index 67d337b..1275e8d 100644
--- a/src/main/java/flipnote/reaction/common/model/response/ApiResponseAdvice.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/common/ApiResponseAdvice.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.model.response;
+package flipnote.reaction.interfaces.http.common;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
diff --git a/src/main/java/flipnote/reaction/common/exception/GlobalExceptionHandler.java b/src/main/java/flipnote/reaction/interfaces/http/common/GlobalExceptionHandler.java
similarity index 91%
rename from src/main/java/flipnote/reaction/common/exception/GlobalExceptionHandler.java
rename to src/main/java/flipnote/reaction/interfaces/http/common/GlobalExceptionHandler.java
index 72cdc8c..8955786 100644
--- a/src/main/java/flipnote/reaction/common/exception/GlobalExceptionHandler.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/common/GlobalExceptionHandler.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.exception;
+package flipnote.reaction.interfaces.http.common;
import java.util.List;
@@ -9,7 +9,9 @@
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
-import flipnote.reaction.common.model.response.ApiResponse;
+import flipnote.reaction.domain.common.BizException;
+import flipnote.reaction.domain.common.CommonErrorCode;
+import flipnote.reaction.domain.common.ErrorCode;
import lombok.extern.slf4j.Slf4j;
@Slf4j
diff --git a/src/main/java/flipnote/reaction/common/constants/HeaderConstants.java b/src/main/java/flipnote/reaction/interfaces/http/common/HeaderConstants.java
similarity index 80%
rename from src/main/java/flipnote/reaction/common/constants/HeaderConstants.java
rename to src/main/java/flipnote/reaction/interfaces/http/common/HeaderConstants.java
index d9a6331..3f979d9 100644
--- a/src/main/java/flipnote/reaction/common/constants/HeaderConstants.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/common/HeaderConstants.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.constants;
+package flipnote.reaction.interfaces.http.common;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
diff --git a/src/main/java/flipnote/reaction/common/model/response/IdResponse.java b/src/main/java/flipnote/reaction/interfaces/http/common/IdResponse.java
similarity index 69%
rename from src/main/java/flipnote/reaction/common/model/response/IdResponse.java
rename to src/main/java/flipnote/reaction/interfaces/http/common/IdResponse.java
index 6240e46..4f3df70 100644
--- a/src/main/java/flipnote/reaction/common/model/response/IdResponse.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/common/IdResponse.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.model.response;
+package flipnote.reaction.interfaces.http.common;
public record IdResponse(
Long id
diff --git a/src/main/java/flipnote/reaction/common/model/request/PagingRequest.java b/src/main/java/flipnote/reaction/interfaces/http/common/PagingRequest.java
similarity index 89%
rename from src/main/java/flipnote/reaction/common/model/request/PagingRequest.java
rename to src/main/java/flipnote/reaction/interfaces/http/common/PagingRequest.java
index dfe36c4..2dadcb4 100644
--- a/src/main/java/flipnote/reaction/common/model/request/PagingRequest.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/common/PagingRequest.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.model.request;
+package flipnote.reaction.interfaces.http.common;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
@@ -42,6 +42,6 @@ public Sort.Direction getOrder() {
}
public String getSortBy() {
- return sortBy != null ? sortBy.toUpperCase() : null;
+ return sortBy;
}
}
diff --git a/src/main/java/flipnote/reaction/common/model/response/PagingResponse.java b/src/main/java/flipnote/reaction/interfaces/http/common/PagingResponse.java
similarity index 91%
rename from src/main/java/flipnote/reaction/common/model/response/PagingResponse.java
rename to src/main/java/flipnote/reaction/interfaces/http/common/PagingResponse.java
index 304f4df..14c652d 100644
--- a/src/main/java/flipnote/reaction/common/model/response/PagingResponse.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/common/PagingResponse.java
@@ -1,4 +1,4 @@
-package flipnote.reaction.common.model.response;
+package flipnote.reaction.interfaces.http.common;
import java.util.List;
diff --git a/src/main/java/flipnote/reaction/bookmark/model/request/BookmarkSearchRequest.java b/src/main/java/flipnote/reaction/interfaces/http/dto/request/BookmarkSearchRequest.java
similarity index 51%
rename from src/main/java/flipnote/reaction/bookmark/model/request/BookmarkSearchRequest.java
rename to src/main/java/flipnote/reaction/interfaces/http/dto/request/BookmarkSearchRequest.java
index 7b0d9b3..6942060 100644
--- a/src/main/java/flipnote/reaction/bookmark/model/request/BookmarkSearchRequest.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/dto/request/BookmarkSearchRequest.java
@@ -1,6 +1,6 @@
-package flipnote.reaction.bookmark.model.request;
+package flipnote.reaction.interfaces.http.dto.request;
-import flipnote.reaction.common.model.request.PagingRequest;
+import flipnote.reaction.interfaces.http.common.PagingRequest;
import lombok.Getter;
import lombok.Setter;
diff --git a/src/main/java/flipnote/reaction/bookmark/model/request/BookmarkTargetTypeRequest.java b/src/main/java/flipnote/reaction/interfaces/http/dto/request/BookmarkTargetTypeRequest.java
similarity index 64%
rename from src/main/java/flipnote/reaction/bookmark/model/request/BookmarkTargetTypeRequest.java
rename to src/main/java/flipnote/reaction/interfaces/http/dto/request/BookmarkTargetTypeRequest.java
index fba492d..93b9ec9 100644
--- a/src/main/java/flipnote/reaction/bookmark/model/request/BookmarkTargetTypeRequest.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/dto/request/BookmarkTargetTypeRequest.java
@@ -1,8 +1,8 @@
-package flipnote.reaction.bookmark.model.request;
+package flipnote.reaction.interfaces.http.dto.request;
-import flipnote.reaction.bookmark.entity.BookmarkTargetType;
-import flipnote.reaction.bookmark.exception.BookmarkErrorCode;
-import flipnote.reaction.common.exception.BizException;
+import flipnote.reaction.domain.bookmark.BookmarkErrorCode;
+import flipnote.reaction.domain.bookmark.BookmarkTargetType;
+import flipnote.reaction.domain.common.BizException;
public enum BookmarkTargetTypeRequest {
card_set;
diff --git a/src/main/java/flipnote/reaction/like/model/request/LikeSearchRequest.java b/src/main/java/flipnote/reaction/interfaces/http/dto/request/LikeSearchRequest.java
similarity index 50%
rename from src/main/java/flipnote/reaction/like/model/request/LikeSearchRequest.java
rename to src/main/java/flipnote/reaction/interfaces/http/dto/request/LikeSearchRequest.java
index 491bde9..229dc48 100644
--- a/src/main/java/flipnote/reaction/like/model/request/LikeSearchRequest.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/dto/request/LikeSearchRequest.java
@@ -1,6 +1,6 @@
-package flipnote.reaction.like.model.request;
+package flipnote.reaction.interfaces.http.dto.request;
-import flipnote.reaction.common.model.request.PagingRequest;
+import flipnote.reaction.interfaces.http.common.PagingRequest;
import lombok.Getter;
import lombok.Setter;
diff --git a/src/main/java/flipnote/reaction/like/model/request/LikeTargetTypeRequest.java b/src/main/java/flipnote/reaction/interfaces/http/dto/request/LikeTargetTypeRequest.java
similarity index 64%
rename from src/main/java/flipnote/reaction/like/model/request/LikeTargetTypeRequest.java
rename to src/main/java/flipnote/reaction/interfaces/http/dto/request/LikeTargetTypeRequest.java
index d5d459d..0e02093 100644
--- a/src/main/java/flipnote/reaction/like/model/request/LikeTargetTypeRequest.java
+++ b/src/main/java/flipnote/reaction/interfaces/http/dto/request/LikeTargetTypeRequest.java
@@ -1,8 +1,8 @@
-package flipnote.reaction.like.model.request;
+package flipnote.reaction.interfaces.http.dto.request;
-import flipnote.reaction.common.exception.BizException;
-import flipnote.reaction.like.entity.LikeTargetType;
-import flipnote.reaction.like.exception.LikeErrorCode;
+import flipnote.reaction.domain.common.BizException;
+import flipnote.reaction.domain.like.LikeErrorCode;
+import flipnote.reaction.domain.like.LikeTargetType;
public enum LikeTargetTypeRequest {
card_set;
diff --git a/src/main/java/flipnote/reaction/interfaces/http/dto/response/.gitkeep b/src/main/java/flipnote/reaction/interfaces/http/dto/response/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/java/flipnote/reaction/like/model/response/LikeResponse.java b/src/main/java/flipnote/reaction/like/model/response/LikeResponse.java
deleted file mode 100644
index c656567..0000000
--- a/src/main/java/flipnote/reaction/like/model/response/LikeResponse.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package flipnote.reaction.like.model.response;
-
-import java.time.LocalDateTime;
-
-import cardset.Cardset.CardSetSummary;
-import flipnote.reaction.like.entity.Like;
-
-public record LikeResponse(
- LikeTargetResponse target,
- LocalDateTime likedAt
-) {
- public static LikeResponse from(Like like, CardSetSummary summary) {
- return new LikeResponse(
- new LikeTargetResponse(
- like.getTargetType().name(),
- like.getTargetId(),
- summary != null ? summary.getGroupId() : null,
- summary != null ? summary.getName() : null
- ),
- like.getCreatedAt()
- );
- }
-}
diff --git a/src/main/java/flipnote/reaction/like/model/response/LikeTargetResponse.java b/src/main/java/flipnote/reaction/like/model/response/LikeTargetResponse.java
deleted file mode 100644
index d3419dc..0000000
--- a/src/main/java/flipnote/reaction/like/model/response/LikeTargetResponse.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package flipnote.reaction.like.model.response;
-
-public record LikeTargetResponse(
- String type,
- Long id,
- Long groupId,
- String name
-) {
-}
diff --git a/src/main/java/flipnote/reaction/like/service/LikeService.java b/src/main/java/flipnote/reaction/like/service/LikeService.java
deleted file mode 100644
index ed46aba..0000000
--- a/src/main/java/flipnote/reaction/like/service/LikeService.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package flipnote.reaction.like.service;
-
-import java.util.List;
-import java.util.Map;
-
-import org.springframework.dao.DataIntegrityViolationException;
-import org.springframework.data.domain.Page;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import cardset.Cardset.CardSetSummary;
-import flipnote.reaction.common.config.RabbitMqConfig;
-import flipnote.reaction.common.event.ReactionEventPublisher;
-import flipnote.reaction.common.exception.BizException;
-import flipnote.reaction.common.exception.CommonErrorCode;
-import flipnote.reaction.common.grpc.CardSetGrpcClient;
-import flipnote.reaction.common.model.response.IdResponse;
-import flipnote.reaction.common.model.response.PagingResponse;
-import flipnote.reaction.like.entity.Like;
-import flipnote.reaction.like.entity.LikeTargetType;
-import flipnote.reaction.like.exception.LikeErrorCode;
-import flipnote.reaction.like.model.request.LikeSearchRequest;
-import flipnote.reaction.like.model.response.LikeResponse;
-import flipnote.reaction.like.repository.LikeRepository;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-@Transactional(readOnly = true)
-public class LikeService {
-
- private final LikeRepository likeRepository;
- private final LikeReader likeReader;
- private final ReactionEventPublisher eventPublisher;
- private final CardSetGrpcClient cardSetGrpcClient;
-
- @Transactional
- public IdResponse addLike(LikeTargetType targetType, Long targetId, Long userId) {
- if (targetType == LikeTargetType.CARD_SET) {
- if (!cardSetGrpcClient.isCardSetViewable(targetId, userId)) {
- throw new BizException(CommonErrorCode.TARGET_NOT_VIEWABLE);
- }
- }
-
- if (likeReader.isLiked(targetType, targetId, userId)) {
- throw new BizException(LikeErrorCode.ALREADY_LIKED);
- }
-
- Like like = Like.builder()
- .targetType(targetType)
- .targetId(targetId)
- .userId(userId)
- .build();
-
- try {
- likeRepository.save(like);
- } catch (DataIntegrityViolationException e) {
- throw new BizException(LikeErrorCode.ALREADY_LIKED);
- }
-
- eventPublisher.publish(RabbitMqConfig.ROUTING_KEY_LIKE_ADDED, "LIKE_ADDED",
- targetType.name(), targetId, userId);
-
- return IdResponse.from(like.getId());
- }
-
- @Transactional
- public void removeLike(LikeTargetType targetType, Long targetId, Long userId) {
- Like like = likeReader.findByTargetAndUserId(targetType, targetId, userId);
- likeRepository.delete(like);
-
- eventPublisher.publish(RabbitMqConfig.ROUTING_KEY_LIKE_REMOVED, "LIKE_REMOVED",
- targetType.name(), targetId, userId);
- }
-
- public PagingResponse getLikes(LikeTargetType targetType, Long userId, LikeSearchRequest request) {
- Page likePage = likeRepository.findByTargetTypeAndUserId(
- targetType, userId, request.getPageRequest()
- );
-
- List targetIds = likePage.getContent().stream()
- .map(Like::getTargetId)
- .toList();
-
- Map summaryMap = targetIds.isEmpty()
- ? Map.of()
- : cardSetGrpcClient.getCardSetsByIds(targetIds, userId);
-
- Page responsePage = likePage.map(
- like -> LikeResponse.from(like, summaryMap.get(like.getTargetId()))
- );
- return PagingResponse.from(responsePage);
- }
-}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 9984939..1e2a6c6 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -14,17 +14,18 @@ spring:
jpa:
open-in-view: false
hibernate:
- ddl-auto: update
+ ddl-auto: ${DDL_AUTO:validate}
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
-
-grpc:
- server:
- port: ${GRPC_SERVER_PORT:9093}
- cardset:
- host: ${GRPC_CARDSET_HOST:cardset-service}
- port: ${GRPC_CARDSET_PORT:9095}
+ grpc:
+ server:
+ port: ${GRPC_SERVER_PORT:9093}
+ client:
+ channels:
+ cardset:
+ address: ${GRPC_CARDSET_ADDRESS:static://cardset-service:9095}
+ negotiation-type: PLAINTEXT
server:
port: 8083
diff --git a/src/test/java/flipnote/reaction/config/TestGrpcConfig.java b/src/test/java/flipnote/reaction/config/TestGrpcConfig.java
index f9846df..eaedda7 100644
--- a/src/test/java/flipnote/reaction/config/TestGrpcConfig.java
+++ b/src/test/java/flipnote/reaction/config/TestGrpcConfig.java
@@ -3,19 +3,13 @@
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
-import flipnote.reaction.common.grpc.CardSetGrpcClient;
-import io.grpc.ManagedChannel;
+import flipnote.reaction.infrastructure.grpc.CardSetGrpcClient;
import static org.mockito.Mockito.mock;
@TestConfiguration
public class TestGrpcConfig {
- @Bean
- public ManagedChannel cardSetChannel() {
- return mock(ManagedChannel.class);
- }
-
@Bean
public CardSetGrpcClient cardSetGrpcClient() {
return mock(CardSetGrpcClient.class);
diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml
index 0e1effb..62a9de7 100644
--- a/src/test/resources/application.yml
+++ b/src/test/resources/application.yml
@@ -15,10 +15,11 @@ spring:
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
-
-grpc:
- server:
- port: 0
- cardset:
- host: localhost
- port: 9090
+ grpc:
+ server:
+ port: 0
+ client:
+ channels:
+ cardset:
+ address: static://localhost:9090
+ negotiation-type: PLAINTEXT