getOrders(String userId) {
*/
@Transactional(readOnly = true)
public OrderInfo getOrder(String userId, Long orderId) {
- User user = userService.findByUserId(userId);
+ User user = userService.getUser(userId);
Order order = orderService.getById(orderId);
if (!order.getUserId().equals(user.getId())) {
@@ -483,7 +487,7 @@ public boolean updateOrderStatusByPaymentResult(
String reason
) {
try {
- Order order = orderService.findById(orderId).orElse(null);
+ Order order = orderService.getOrder(orderId).orElse(null);
if (order == null) {
log.warn("주문 상태 업데이트 시 주문을 찾을 수 없습니다. (orderId: {})", orderId);
@@ -509,7 +513,7 @@ public boolean updateOrderStatusByPaymentResult(
return true;
} else if (paymentStatus == PaymentStatus.FAILED) {
// 결제 실패: 주문 취소 및 리소스 원복
- User user = userService.findById(order.getUserId());
+ User user = userService.getUserById(order.getUserId());
if (user == null) {
log.warn("주문 상태 업데이트 시 사용자를 찾을 수 없습니다. (orderId: {}, userId: {})",
orderId, order.getUserId());
@@ -551,7 +555,7 @@ public void updateOrderStatusToCompleted(Long orderId, String transactionKey) {
}
// Payment 상태 업데이트 (PaymentService 사용)
- paymentService.findByOrderId(orderId).ifPresent(payment -> {
+ paymentService.getPaymentByOrderId(orderId).ifPresent(payment -> {
if (payment.getStatus() == PaymentStatus.PENDING) {
paymentService.toSuccess(payment.getId(), java.time.LocalDateTime.now());
}
@@ -741,7 +745,7 @@ private PaymentGatewayDto.TransactionStatus verifyCallbackWithPgInquiry(
// User의 userId (String)를 가져오기 위해 User 조회
User user;
try {
- user = userService.findById(userId);
+ user = userService.getUserById(userId);
} catch (CoreException e) {
log.warn("콜백 검증 시 사용자를 찾을 수 없습니다. 콜백 정보를 사용합니다. (orderId: {}, userId: {})",
orderId, userId);
@@ -864,7 +868,7 @@ private void handlePaymentFailure(String userId, Long orderId, String errorCode,
// 사용자 조회 (Service를 통한 접근)
User user;
try {
- user = userService.findByUserId(userId);
+ user = userService.getUser(userId);
} catch (CoreException e) {
log.warn("결제 실패 처리 시 사용자를 찾을 수 없습니다. (userId: {}, orderId: {})", userId, orderId);
return;
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/signup/SignUpFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/signup/SignUpFacade.java
deleted file mode 100644
index 293505b15..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/application/signup/SignUpFacade.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package com.loopers.application.signup;
-
-import com.loopers.domain.user.Gender;
-import com.loopers.domain.user.Point;
-import com.loopers.domain.user.User;
-import com.loopers.domain.user.UserService;
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
-import jakarta.transaction.Transactional;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-
-import java.util.Locale;
-
-/**
- * 회원가입 파사드.
- *
- * 회원가입 시 사용자 생성을 처리하는 애플리케이션 서비스입니다.
- * 사용자 생성 시 포인트는 자동으로 0으로 초기화됩니다.
- * 트랜잭션 경계를 관리하여 데이터 일관성을 보장합니다.
- *
- *
- * @author Loopers
- * @version 1.0
- */
-@RequiredArgsConstructor
-@Component
-public class SignUpFacade {
- private final UserService userService;
-
- /**
- * 회원가입을 처리합니다.
- *
- * 사용자를 생성하며, 포인트는 자동으로 0으로 초기화됩니다.
- * 전체 과정이 하나의 트랜잭션으로 처리됩니다.
- *
- *
- * @param userId 사용자 ID
- * @param email 이메일 주소
- * @param birthDateStr 생년월일 (yyyy-MM-dd)
- * @param genderStr 성별 문자열 (MALE 또는 FEMALE)
- * @return 생성된 사용자 정보
- * @throws CoreException gender 값이 유효하지 않거나, 유효성 검증 실패 또는 중복 ID 존재 시
- */
- @Transactional
- public SignUpInfo signUp(String userId, String email, String birthDateStr, String genderStr) {
- Gender gender = parseGender(genderStr);
- Point point = Point.of(0L);
- User user = userService.create(userId, email, birthDateStr, gender, point);
- return SignUpInfo.from(user);
- }
-
- /**
- * 성별 문자열을 Gender enum으로 변환합니다.
- *
- * 도메인 진입점에서 방어 로직을 제공하여 NPE를 방지합니다.
- *
- *
- * @param genderStr 성별 문자열
- * @return Gender enum
- * @throws CoreException gender 값이 null이거나 유효하지 않은 경우
- */
- private Gender parseGender(String genderStr) {
- if (genderStr == null) {
- throw new CoreException(ErrorType.BAD_REQUEST, "gender 값이 올바르지 않습니다.");
- }
- try {
- String genderValue = genderStr.trim().toUpperCase(Locale.ROOT);
- return Gender.valueOf(genderValue);
- } catch (IllegalArgumentException e) {
- throw new CoreException(ErrorType.BAD_REQUEST, "gender 값이 올바르지 않습니다.");
- }
- }
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/signup/SignUpInfo.java b/apps/commerce-api/src/main/java/com/loopers/application/signup/SignUpInfo.java
deleted file mode 100644
index c84caf7a3..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/application/signup/SignUpInfo.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.loopers.application.signup;
-
-import com.loopers.domain.user.Gender;
-import com.loopers.domain.user.User;
-
-import java.time.LocalDate;
-
-/**
- * 회원가입 결과 정보를 담는 레코드.
- *
- * User 도메인 엔티티로부터 생성된 불변 데이터 전송 객체입니다.
- *
- *
- * @param id 사용자 엔티티 ID
- * @param userId 사용자 ID
- * @param email 이메일 주소
- * @param birthDate 생년월일
- * @param gender 성별
- * @author Loopers
- * @version 1.0
- */
-public record SignUpInfo(Long id, String userId, String email, LocalDate birthDate, Gender gender) {
- /**
- * User 엔티티로부터 SignUpInfo를 생성합니다.
- *
- * @param user 변환할 사용자 엔티티
- * @return 생성된 SignUpInfo
- */
- public static SignUpInfo from(User user) {
- return new SignUpInfo(
- user.getId(),
- user.getUserId(),
- user.getEmail(),
- user.getBirthDate(),
- user.getGender()
- );
- }
-}
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java b/apps/commerce-api/src/main/java/com/loopers/application/user/UserService.java
similarity index 59%
rename from apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java
rename to apps/commerce-api/src/main/java/com/loopers/application/user/UserService.java
index 8c6d062e2..1b6689d34 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java
+++ b/apps/commerce-api/src/main/java/com/loopers/application/user/UserService.java
@@ -1,5 +1,9 @@
-package com.loopers.domain.user;
+package com.loopers.application.user;
+import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.Point;
+import com.loopers.domain.user.User;
+import com.loopers.domain.user.UserRepository;
import com.loopers.support.error.CoreException;
import com.loopers.support.error.ErrorType;
import lombok.RequiredArgsConstructor;
@@ -8,10 +12,10 @@
import org.springframework.transaction.annotation.Transactional;
/**
- * 사용자 도메인 서비스.
+ * 사용자 애플리케이션 서비스.
*
- * 사용자 생성, 조회 등의 도메인 로직을 처리합니다.
- * Repository에 의존하며 데이터 무결성 제약 조건을 처리합니다.
+ * 사용자 생성, 조회, 포인트 관리 등의 애플리케이션 로직을 처리합니다.
+ * Repository에 의존하며 트랜잭션 관리 및 데이터 무결성 제약 조건을 처리합니다.
*
*
* @author Loopers
@@ -52,7 +56,7 @@ public User create(String userId, String email, String birthDateStr, Gender gend
* @throws CoreException 사용자를 찾을 수 없는 경우
*/
@Transactional(readOnly = true)
- public User findByUserId(String userId) {
+ public User getUser(String userId) {
User user = userRepository.findByUserId(userId);
if (user == null) {
throw new CoreException(ErrorType.NOT_FOUND, "사용자를 찾을 수 없습니다.");
@@ -71,7 +75,7 @@ public User findByUserId(String userId) {
* @throws CoreException 사용자를 찾을 수 없는 경우
*/
@Transactional
- public User findByUserIdForUpdate(String userId) {
+ public User getUserForUpdate(String userId) {
User user = userRepository.findByUserIdForUpdate(userId);
if (user == null) {
throw new CoreException(ErrorType.NOT_FOUND, "사용자를 찾을 수 없습니다.");
@@ -87,7 +91,7 @@ public User findByUserIdForUpdate(String userId) {
* @throws CoreException 사용자를 찾을 수 없는 경우
*/
@Transactional(readOnly = true)
- public User findById(Long id) {
+ public User getUserById(Long id) {
User user = userRepository.findById(id);
if (user == null) {
throw new CoreException(ErrorType.NOT_FOUND, "사용자를 찾을 수 없습니다.");
@@ -105,4 +109,55 @@ public User findById(Long id) {
public User save(User user) {
return userRepository.save(user);
}
+
+ /**
+ * 사용자의 포인트를 조회합니다.
+ *
+ * @param userId 조회할 사용자 ID
+ * @return 포인트 정보
+ * @throws CoreException 사용자를 찾을 수 없는 경우
+ */
+ @Transactional(readOnly = true)
+ public PointsInfo getPoints(String userId) {
+ User user = getUser(userId);
+ return PointsInfo.from(user);
+ }
+
+ /**
+ * 사용자의 포인트를 충전합니다.
+ *
+ * 트랜잭션 내에서 실행되어 데이터 일관성을 보장합니다.
+ *
+ *
+ * @param userId 충전할 사용자 ID
+ * @param amount 충전할 포인트 금액
+ * @return 충전된 포인트 정보
+ * @throws CoreException 사용자를 찾을 수 없거나 충전 금액이 유효하지 않은 경우
+ */
+ @Transactional
+ public PointsInfo chargePoint(String userId, Long amount) {
+ User user = getUser(userId);
+ Point point = Point.of(amount);
+ user.receivePoint(point);
+ User savedUser = save(user);
+ return PointsInfo.from(savedUser);
+ }
+
+ /**
+ * 포인트 정보를 담는 레코드.
+ *
+ * @param userId 사용자 ID
+ * @param balance 포인트 잔액
+ */
+ public record PointsInfo(String userId, Long balance) {
+ /**
+ * User 엔티티로부터 PointsInfo를 생성합니다.
+ *
+ * @param user 사용자 엔티티
+ * @return 생성된 PointsInfo
+ */
+ public static PointsInfo from(User user) {
+ return new PointsInfo(user.getUserId(), user.getPoint().getValue());
+ }
+ }
}
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/userinfo/UserInfoFacade.java b/apps/commerce-api/src/main/java/com/loopers/application/userinfo/UserInfoFacade.java
deleted file mode 100644
index ceae04eef..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/application/userinfo/UserInfoFacade.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.loopers.application.userinfo;
-
-import com.loopers.domain.user.User;
-import com.loopers.domain.user.UserRepository;
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-
-/**
- * 사용자 정보 조회 파사드.
- *
- * 사용자 정보 조회 유즈케이스를 처리하는 애플리케이션 서비스입니다.
- *
- *
- * @author Loopers
- * @version 1.0
- */
-@RequiredArgsConstructor
-@Component
-public class UserInfoFacade {
- private final UserRepository userRepository;
-
- /**
- * 사용자 ID로 사용자 정보를 조회합니다.
- *
- * @param userId 조회할 사용자 ID
- * @return 조회된 사용자 정보
- * @throws CoreException 사용자를 찾을 수 없는 경우
- */
- public UserInfo getUserInfo(String userId) {
- User user = userRepository.findByUserId(userId);
- if (user == null) {
- throw new CoreException(ErrorType.NOT_FOUND, "사용자를 찾을 수 없습니다.");
- }
- return UserInfo.from(user);
- }
-
- /**
- * 사용자 정보를 담는 레코드.
- *
- * @param userId 사용자 ID
- * @param email 이메일 주소
- * @param birthDate 생년월일
- * @param gender 성별
- */
- public record UserInfo(
- String userId,
- String email,
- java.time.LocalDate birthDate,
- com.loopers.domain.user.Gender gender
- ) {
- /**
- * User 엔티티로부터 UserInfo를 생성합니다.
- *
- * @param user 사용자 엔티티
- * @return 생성된 UserInfo
- */
- public static UserInfo from(User user) {
- return new UserInfo(
- user.getUserId(),
- user.getEmail(),
- user.getBirthDate(),
- user.getGender()
- );
- }
- }
-}
-
diff --git a/apps/commerce-api/src/main/java/com/loopers/config/batch/LikeCountSyncBatchConfig.java b/apps/commerce-api/src/main/java/com/loopers/config/batch/LikeCountSyncBatchConfig.java
index 6bf72da5c..d92ccd4e8 100644
--- a/apps/commerce-api/src/main/java/com/loopers/config/batch/LikeCountSyncBatchConfig.java
+++ b/apps/commerce-api/src/main/java/com/loopers/config/batch/LikeCountSyncBatchConfig.java
@@ -1,6 +1,6 @@
package com.loopers.config.batch;
-import com.loopers.application.catalog.ProductCacheService;
+import com.loopers.application.product.ProductCacheService;
import com.loopers.domain.like.LikeRepository;
import com.loopers.domain.product.ProductRepository;
import lombok.RequiredArgsConstructor;
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/payment/PaymentGateway.java b/apps/commerce-api/src/main/java/com/loopers/domain/payment/PaymentGateway.java
index a8f2864d8..1612276d4 100644
--- a/apps/commerce-api/src/main/java/com/loopers/domain/payment/PaymentGateway.java
+++ b/apps/commerce-api/src/main/java/com/loopers/domain/payment/PaymentGateway.java
@@ -1,6 +1,6 @@
package com.loopers.domain.payment;
-import com.loopers.application.purchasing.PaymentRequestCommand;
+import com.loopers.application.payment.PaymentRequestCommand;
/**
* 결제 게이트웨이 인터페이스.
diff --git a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java b/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
deleted file mode 100644
index dde8b402b..000000000
--- a/apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.loopers.domain.product;
-
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.List;
-
-/**
- * 상품 도메인 서비스.
- *
- * 상품 조회, 저장 등의 도메인 로직을 처리합니다.
- * Repository에 의존하며 비즈니스 규칙을 캡슐화합니다.
- *
- *
- * @author Loopers
- * @version 1.0
- */
-@RequiredArgsConstructor
-@Component
-public class ProductService {
- private final ProductRepository productRepository;
-
- /**
- * 상품 ID로 상품을 조회합니다. (비관적 락)
- *
- * 동시성 제어가 필요한 경우 사용합니다. (예: 재고 차감)
- *
- *
- * @param productId 조회할 상품 ID
- * @return 조회된 상품
- * @throws CoreException 상품을 찾을 수 없는 경우
- */
- @Transactional
- public Product findByIdForUpdate(Long productId) {
- return productRepository.findByIdForUpdate(productId)
- .orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND,
- String.format("상품을 찾을 수 없습니다. (상품 ID: %d)", productId)));
- }
-
- /**
- * 상품 목록을 저장합니다.
- *
- * @param products 저장할 상품 목록
- */
- @Transactional
- public void saveAll(List products) {
- products.forEach(productRepository::save);
- }
-}
-
diff --git a/apps/commerce-api/src/main/java/com/loopers/infrastructure/payment/PaymentGatewayImpl.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/payment/PaymentGatewayImpl.java
index 5d4b994fa..ee81c5438 100644
--- a/apps/commerce-api/src/main/java/com/loopers/infrastructure/payment/PaymentGatewayImpl.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/payment/PaymentGatewayImpl.java
@@ -1,7 +1,7 @@
package com.loopers.infrastructure.payment;
import com.loopers.domain.payment.PaymentGateway;
-import com.loopers.application.purchasing.PaymentRequestCommand;
+import com.loopers.application.payment.PaymentRequestCommand;
import com.loopers.domain.payment.PaymentRequestResult;
import com.loopers.domain.payment.PaymentStatus;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
diff --git a/apps/commerce-api/src/main/java/com/loopers/application/scheduler/LikeCountSyncScheduler.java b/apps/commerce-api/src/main/java/com/loopers/infrastructure/scheduler/LikeCountSyncScheduler.java
similarity index 98%
rename from apps/commerce-api/src/main/java/com/loopers/application/scheduler/LikeCountSyncScheduler.java
rename to apps/commerce-api/src/main/java/com/loopers/infrastructure/scheduler/LikeCountSyncScheduler.java
index 4621b4fef..a4e144a47 100644
--- a/apps/commerce-api/src/main/java/com/loopers/application/scheduler/LikeCountSyncScheduler.java
+++ b/apps/commerce-api/src/main/java/com/loopers/infrastructure/scheduler/LikeCountSyncScheduler.java
@@ -1,4 +1,4 @@
-package com.loopers.application.scheduler;
+package com.loopers.infrastructure.scheduler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/BrandV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/BrandV1Controller.java
index a56cc1c63..32fc066ad 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/BrandV1Controller.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/BrandV1Controller.java
@@ -1,6 +1,6 @@
package com.loopers.interfaces.api.catalog;
-import com.loopers.application.catalog.CatalogBrandFacade;
+import com.loopers.application.brand.BrandService;
import com.loopers.interfaces.api.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
@@ -22,7 +22,7 @@
@RequestMapping("/api/v1/brands")
public class BrandV1Controller {
- private final CatalogBrandFacade catalogBrandFacade;
+ private final BrandService brandService;
/**
* 브랜드 정보를 조회합니다.
@@ -32,7 +32,7 @@ public class BrandV1Controller {
*/
@GetMapping("/{brandId}")
public ApiResponse getBrand(@PathVariable Long brandId) {
- CatalogBrandFacade.BrandInfo brandInfo = catalogBrandFacade.getBrand(brandId);
+ BrandService.BrandInfo brandInfo = brandService.getBrand(brandId);
return ApiResponse.success(BrandV1Dto.BrandResponse.from(brandInfo));
}
}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/BrandV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/BrandV1Dto.java
index 2bc497615..53e8fa13a 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/BrandV1Dto.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/BrandV1Dto.java
@@ -1,6 +1,6 @@
package com.loopers.interfaces.api.catalog;
-import com.loopers.application.catalog.CatalogBrandFacade;
+import com.loopers.application.brand.BrandService;
/**
* 브랜드 조회 API v1의 데이터 전송 객체(DTO) 컨테이너.
@@ -22,7 +22,7 @@ public record BrandResponse(Long brandId, String name) {
* @param brandInfo 브랜드 정보
* @return 생성된 응답 객체
*/
- public static BrandResponse from(CatalogBrandFacade.BrandInfo brandInfo) {
+ public static BrandResponse from(BrandService.BrandInfo brandInfo) {
return new BrandResponse(brandInfo.id(), brandInfo.name());
}
}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/ProductV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/ProductV1Controller.java
index 4dc38d439..c275b3b7d 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/ProductV1Controller.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/catalog/ProductV1Controller.java
@@ -1,6 +1,6 @@
package com.loopers.interfaces.api.catalog;
-import com.loopers.application.catalog.CatalogProductFacade;
+import com.loopers.application.catalog.CatalogFacade;
import com.loopers.application.catalog.ProductInfo;
import com.loopers.application.catalog.ProductInfoList;
import com.loopers.interfaces.api.ApiResponse;
@@ -25,7 +25,7 @@
@RequestMapping("/api/v1/products")
public class ProductV1Controller {
- private final CatalogProductFacade catalogProductFacade;
+ private final CatalogFacade catalogFacade;
/**
* 상품 목록을 조회합니다.
@@ -43,7 +43,7 @@ public ApiResponse getProducts(
@RequestParam(required = false, defaultValue = "0") int page,
@RequestParam(required = false, defaultValue = "20") int size
) {
- ProductInfoList result = catalogProductFacade.getProducts(brandId, sort, page, size);
+ ProductInfoList result = catalogFacade.getProducts(brandId, sort, page, size);
return ApiResponse.success(ProductV1Dto.ProductsResponse.from(result));
}
@@ -55,7 +55,7 @@ public ApiResponse getProducts(
*/
@GetMapping("/{productId}")
public ApiResponse getProduct(@PathVariable Long productId) {
- ProductInfo productInfo = catalogProductFacade.getProduct(productId);
+ ProductInfo productInfo = catalogFacade.getProduct(productId);
return ApiResponse.success(ProductV1Dto.ProductResponse.from(productInfo));
}
}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Controller.java
index 2935b424d..640c909e3 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Controller.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Controller.java
@@ -1,6 +1,6 @@
package com.loopers.interfaces.api.like;
-import com.loopers.application.like.LikeFacade;
+import com.loopers.application.heart.HeartFacade;
import com.loopers.interfaces.api.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -25,7 +25,7 @@
@RequestMapping("/api/v1/like/products")
public class LikeV1Controller {
- private final LikeFacade likeFacade;
+ private final HeartFacade heartFacade;
/**
* 상품에 좋아요를 추가합니다.
@@ -39,7 +39,7 @@ public ApiResponse addLike(
@RequestHeader("X-USER-ID") String userId,
@PathVariable Long productId
) {
- likeFacade.addLike(userId, productId);
+ heartFacade.addLike(userId, productId);
return ApiResponse.success(null);
}
@@ -55,7 +55,7 @@ public ApiResponse removeLike(
@RequestHeader("X-USER-ID") String userId,
@PathVariable Long productId
) {
- likeFacade.removeLike(userId, productId);
+ heartFacade.removeLike(userId, productId);
return ApiResponse.success(null);
}
@@ -69,7 +69,7 @@ public ApiResponse removeLike(
public ApiResponse getLikedProducts(
@RequestHeader("X-USER-ID") String userId
) {
- var likedProducts = likeFacade.getLikedProducts(userId);
+ var likedProducts = heartFacade.getLikedProducts(userId);
return ApiResponse.success(LikeV1Dto.LikedProductsResponse.from(likedProducts));
}
}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Dto.java
index 1fc6f20f0..e154c036b 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Dto.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeV1Dto.java
@@ -1,6 +1,6 @@
package com.loopers.interfaces.api.like;
-import com.loopers.application.like.LikeFacade;
+import com.loopers.application.heart.HeartFacade;
import java.util.List;
@@ -25,7 +25,7 @@ public record LikedProductsResponse(
* @param likedProducts 좋아요한 상품 목록
* @return 생성된 응답 객체
*/
- public static LikedProductsResponse from(List likedProducts) {
+ public static LikedProductsResponse from(List likedProducts) {
return new LikedProductsResponse(
likedProducts.stream()
.map(LikedProductResponse::from)
@@ -58,7 +58,7 @@ public record LikedProductResponse(
* @param likedProduct 좋아요한 상품 정보
* @return 생성된 응답 객체
*/
- public static LikedProductResponse from(LikeFacade.LikedProduct likedProduct) {
+ public static LikedProductResponse from(HeartFacade.LikedProduct likedProduct) {
return new LikedProductResponse(
likedProduct.productId(),
likedProduct.name(),
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/pointwallet/PointWalletV1Controller.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/pointwallet/PointWalletV1Controller.java
index 4efc043ca..e0ffa6f26 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/pointwallet/PointWalletV1Controller.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/pointwallet/PointWalletV1Controller.java
@@ -1,6 +1,6 @@
package com.loopers.interfaces.api.pointwallet;
-import com.loopers.application.pointwallet.PointWalletFacade;
+import com.loopers.application.user.UserService;
import com.loopers.interfaces.api.ApiResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
@@ -25,7 +25,7 @@
@RequestMapping("/api/v1")
public class PointWalletV1Controller {
- private final PointWalletFacade pointWalletFacade;
+ private final UserService userService;
/**
* 현재 사용자의 포인트를 조회합니다.
@@ -38,7 +38,7 @@ public class PointWalletV1Controller {
public ApiResponse getMyPoints(
@RequestHeader("X-USER-ID") String userId
) {
- PointWalletFacade.PointsInfo pointsInfo = pointWalletFacade.getPoints(userId);
+ UserService.PointsInfo pointsInfo = userService.getPoints(userId);
return ApiResponse.success(PointWalletV1Dto.PointsResponse.from(pointsInfo));
}
@@ -55,7 +55,7 @@ public ApiResponse chargePoints(
@RequestHeader("X-USER-ID") String userId,
@Valid @RequestBody PointWalletV1Dto.ChargeRequest request
) {
- PointWalletFacade.PointsInfo pointsInfo = pointWalletFacade.chargePoint(userId, request.amount());
+ UserService.PointsInfo pointsInfo = userService.chargePoint(userId, request.amount());
return ApiResponse.success(PointWalletV1Dto.PointsResponse.from(pointsInfo));
}
}
diff --git a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/pointwallet/PointWalletV1Dto.java b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/pointwallet/PointWalletV1Dto.java
index 461605598..c6e7c97b6 100644
--- a/apps/commerce-api/src/main/java/com/loopers/interfaces/api/pointwallet/PointWalletV1Dto.java
+++ b/apps/commerce-api/src/main/java/com/loopers/interfaces/api/pointwallet/PointWalletV1Dto.java
@@ -1,6 +1,6 @@
package com.loopers.interfaces.api.pointwallet;
-import com.loopers.application.pointwallet.PointWalletFacade;
+import com.loopers.application.user.UserService;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
@@ -24,7 +24,7 @@ public record PointsResponse(String userId, Long balance) {
* @param pointsInfo 포인트 정보
* @return 생성된 응답 객체
*/
- public static PointsResponse from(PointWalletFacade.PointsInfo pointsInfo) {
+ public static PointsResponse from(UserService.PointsInfo pointsInfo) {
return new PointsResponse(pointsInfo.userId(), pointsInfo.balance());
}
}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeConcurrencyTest.java b/apps/commerce-api/src/test/java/com/loopers/application/heart/HeartFacadeConcurrencyTest.java
similarity index 93%
rename from apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeConcurrencyTest.java
rename to apps/commerce-api/src/test/java/com/loopers/application/heart/HeartFacadeConcurrencyTest.java
index 1e7b42394..fc9afe984 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeConcurrencyTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/heart/HeartFacadeConcurrencyTest.java
@@ -1,4 +1,4 @@
-package com.loopers.application.like;
+package com.loopers.application.heart;
import com.loopers.domain.brand.Brand;
import com.loopers.domain.brand.BrandRepository;
@@ -37,10 +37,10 @@
@SpringBootTest
@Import(MySqlTestContainersConfig.class)
@DisplayName("LikeFacade 동시성 테스트")
-class LikeFacadeConcurrencyTest {
+class HeartFacadeConcurrencyTest {
@Autowired
- private LikeFacade likeFacade;
+ private HeartFacade heartFacade;
@Autowired
private UserRepository userRepository;
@@ -116,7 +116,7 @@ void concurrencyTest_likeShouldBeProperlyCounted() throws InterruptedException {
for (User user : users) {
executorService.submit(() -> {
try {
- likeFacade.addLike(user.getUserId(), productId);
+ heartFacade.addLike(user.getUserId(), productId);
successCount.incrementAndGet();
} catch (Exception e) {
synchronized (exceptions) {
@@ -159,7 +159,7 @@ void concurrencyTest_sameUserMultipleRequests_shouldBeCountedCorrectly() throws
for (int i = 0; i < concurrentRequestCount; i++) {
executorService.submit(() -> {
try {
- likeFacade.addLike(userId, productId);
+ heartFacade.addLike(userId, productId);
successCount.incrementAndGet();
} catch (Exception e) {
synchronized (exceptions) {
@@ -233,19 +233,19 @@ void concurrencyTest_transactionReadOnlyAndUniqueConstraintServeDifferentPurpose
String userId2 = user2.getUserId();
// user1이 상품1, 상품2에 좋아요를 이미 누른 상태
- likeFacade.addLike(userId1, product1.getId());
- likeFacade.addLike(userId1, product2.getId());
+ heartFacade.addLike(userId1, product1.getId());
+ heartFacade.addLike(userId1, product2.getId());
ExecutorService executorService = Executors.newFixedThreadPool(20);
CountDownLatch latch = new CountDownLatch(20);
- List> allResults = new ArrayList<>();
+ List> allResults = new ArrayList<>();
// act
// 여러 스레드에서 동시에 조회를 수행
for (int i = 0; i < 10; i++) {
executorService.submit(() -> {
try {
- List result = likeFacade.getLikedProducts(userId1);
+ List result = heartFacade.getLikedProducts(userId1);
synchronized (allResults) {
allResults.add(result);
}
@@ -267,14 +267,14 @@ void concurrencyTest_transactionReadOnlyAndUniqueConstraintServeDifferentPurpose
if (index % 2 == 0) {
// user2가 상품1에 좋아요 추가
try {
- likeFacade.addLike(userId2, product1.getId());
+ heartFacade.addLike(userId2, product1.getId());
} catch (Exception e) {
// 이미 좋아요가 있으면 무시
}
} else {
// user2가 상품2에 좋아요 추가
try {
- likeFacade.addLike(userId2, product2.getId());
+ heartFacade.addLike(userId2, product2.getId());
} catch (Exception e) {
// 이미 좋아요가 있으면 무시
}
@@ -310,25 +310,25 @@ void concurrencyTest_transactionReadOnlyAndUniqueConstraintServeDifferentPurpose
// 참고: allResults는 동기화 이전에 조회된 결과이므로 likesCount가 0일 수 있습니다.
// 이 테스트는 @Transactional(readOnly = true)의 일관성 보장을 검증하는 것이 목적이므로,
// 동시성 테스트 중 조회된 결과의 상품 ID 일관성만 확인합니다.
- for (List result : allResults) {
+ for (List result : allResults) {
// user1의 좋아요 목록에는 상품1, 상품2가 포함되어야 함
List resultProductIds = result.stream()
- .map(LikeFacade.LikedProduct::productId)
+ .map(HeartFacade.LikedProduct::productId)
.sorted()
.toList();
assertThat(resultProductIds).contains(product1.getId(), product2.getId());
}
// 최종 상태 확인 (동기화 후)
- List finalResult = likeFacade.getLikedProducts(userId1);
+ List finalResult = heartFacade.getLikedProducts(userId1);
List finalProductIds = finalResult.stream()
- .map(LikeFacade.LikedProduct::productId)
+ .map(HeartFacade.LikedProduct::productId)
.sorted()
.toList();
assertThat(finalProductIds).containsExactlyInAnyOrder(product1.getId(), product2.getId());
// 동기화 후에는 정확한 좋아요 수가 반영되어야 함
- for (LikeFacade.LikedProduct likedProduct : finalResult) {
+ for (HeartFacade.LikedProduct likedProduct : finalResult) {
assertThat(likedProduct.likesCount()).isGreaterThan(0);
}
}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeTest.java b/apps/commerce-api/src/test/java/com/loopers/application/heart/HeartFacadeTest.java
similarity index 89%
rename from apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeTest.java
rename to apps/commerce-api/src/test/java/com/loopers/application/heart/HeartFacadeTest.java
index 148fe1968..b8edf53c0 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/like/LikeFacadeTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/heart/HeartFacadeTest.java
@@ -1,6 +1,6 @@
-package com.loopers.application.like;
+package com.loopers.application.heart;
-import com.loopers.application.catalog.ProductCacheService;
+import com.loopers.application.product.ProductCacheService;
import com.loopers.domain.like.Like;
import com.loopers.domain.like.LikeRepository;
import com.loopers.domain.product.Product;
@@ -28,7 +28,7 @@
import static org.mockito.Mockito.never;
@DisplayName("LikeFacade 좋아요 등록/취소/중복 방지 흐름 검증")
-class LikeFacadeTest {
+class HeartFacadeTest {
@Mock
private LikeRepository likeRepository;
@@ -43,7 +43,7 @@ class LikeFacadeTest {
private ProductCacheService productCacheService;
@InjectMocks
- private LikeFacade likeFacade;
+ private HeartFacade heartFacade;
private static final String DEFAULT_USER_ID = "testuser";
private static final Long DEFAULT_USER_INTERNAL_ID = 1L;
@@ -63,7 +63,7 @@ void addLike_success() {
.thenReturn(Optional.empty());
// act
- likeFacade.addLike(DEFAULT_USER_ID, DEFAULT_PRODUCT_ID);
+ heartFacade.addLike(DEFAULT_USER_ID, DEFAULT_PRODUCT_ID);
// assert
verify(likeRepository).save(any(Like.class));
@@ -79,7 +79,7 @@ void removeLike_success() {
.thenReturn(Optional.of(like));
// act
- likeFacade.removeLike(DEFAULT_USER_ID, DEFAULT_PRODUCT_ID);
+ heartFacade.removeLike(DEFAULT_USER_ID, DEFAULT_PRODUCT_ID);
// assert
verify(likeRepository).delete(like);
@@ -94,7 +94,7 @@ void addLike_isIdempotent() {
.thenReturn(Optional.of(Like.of(DEFAULT_USER_INTERNAL_ID, DEFAULT_PRODUCT_ID)));
// act
- likeFacade.addLike(DEFAULT_USER_ID, DEFAULT_PRODUCT_ID);
+ heartFacade.addLike(DEFAULT_USER_ID, DEFAULT_PRODUCT_ID);
// assert - save는 한 번만 호출되어야 함 (중복 방지)
verify(likeRepository, never()).save(any(Like.class));
@@ -109,7 +109,7 @@ void removeLike_isIdempotent() {
.thenReturn(Optional.empty()); // 좋아요 없음
// act - 좋아요가 없는 상태에서 취소 시도
- likeFacade.removeLike(DEFAULT_USER_ID, DEFAULT_PRODUCT_ID);
+ heartFacade.removeLike(DEFAULT_USER_ID, DEFAULT_PRODUCT_ID);
// assert - 예외가 발생하지 않아야 함 (멱등성 보장)
verify(likeRepository).findByUserIdAndProductId(DEFAULT_USER_INTERNAL_ID, DEFAULT_PRODUCT_ID);
@@ -124,7 +124,7 @@ void addLike_userNotFound() {
when(userRepository.findByUserId(unknownUserId)).thenReturn(null);
// act & assert
- assertThatThrownBy(() -> likeFacade.addLike(unknownUserId, DEFAULT_PRODUCT_ID))
+ assertThatThrownBy(() -> heartFacade.addLike(unknownUserId, DEFAULT_PRODUCT_ID))
.isInstanceOf(CoreException.class)
.hasFieldOrPropertyWithValue("errorType", ErrorType.NOT_FOUND);
}
@@ -138,7 +138,7 @@ void addLike_productNotFound() {
when(productRepository.findById(nonExistentProductId)).thenReturn(Optional.empty());
// act & assert
- assertThatThrownBy(() -> likeFacade.addLike(DEFAULT_USER_ID, nonExistentProductId))
+ assertThatThrownBy(() -> heartFacade.addLike(DEFAULT_USER_ID, nonExistentProductId))
.isInstanceOf(CoreException.class)
.hasFieldOrPropertyWithValue("errorType", ErrorType.NOT_FOUND);
}
@@ -166,13 +166,13 @@ void getLikedProducts_success() {
.thenReturn(List.of(product1, product2));
// act
- List result = likeFacade.getLikedProducts(DEFAULT_USER_ID);
+ List result = heartFacade.getLikedProducts(DEFAULT_USER_ID);
// assert
assertThat(result).hasSize(2);
- assertThat(result).extracting(LikeFacade.LikedProduct::productId)
+ assertThat(result).extracting(HeartFacade.LikedProduct::productId)
.containsExactlyInAnyOrder(productId1, productId2);
- assertThat(result).extracting(LikeFacade.LikedProduct::likesCount)
+ assertThat(result).extracting(HeartFacade.LikedProduct::likesCount)
.containsExactlyInAnyOrder(5L, 3L);
}
@@ -184,7 +184,7 @@ void getLikedProducts_emptyList() {
when(likeRepository.findAllByUserId(DEFAULT_USER_INTERNAL_ID)).thenReturn(List.of());
// act
- List result = likeFacade.getLikedProducts(DEFAULT_USER_ID);
+ List result = heartFacade.getLikedProducts(DEFAULT_USER_ID);
// assert
assertThat(result).isEmpty();
@@ -212,7 +212,7 @@ void getLikedProducts_productNotFound() {
.thenReturn(List.of(product1)); // product1만 반환 (nonExistentProductId는 없음)
// act & assert
- assertThatThrownBy(() -> likeFacade.getLikedProducts(DEFAULT_USER_ID))
+ assertThatThrownBy(() -> heartFacade.getLikedProducts(DEFAULT_USER_ID))
.isInstanceOf(CoreException.class)
.hasFieldOrPropertyWithValue("errorType", ErrorType.NOT_FOUND);
}
@@ -225,7 +225,7 @@ void getLikedProducts_userNotFound() {
when(userRepository.findByUserId(unknownUserId)).thenReturn(null);
// act & assert
- assertThatThrownBy(() -> likeFacade.getLikedProducts(unknownUserId))
+ assertThatThrownBy(() -> heartFacade.getLikedProducts(unknownUserId))
.isInstanceOf(CoreException.class)
.hasFieldOrPropertyWithValue("errorType", ErrorType.NOT_FOUND);
}
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/signup/SignUpFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/signup/SignUpFacadeIntegrationTest.java
deleted file mode 100644
index c827cae1d..000000000
--- a/apps/commerce-api/src/test/java/com/loopers/application/signup/SignUpFacadeIntegrationTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.loopers.application.signup;
-
-import com.loopers.domain.user.Gender;
-import com.loopers.domain.user.User;
-import com.loopers.domain.user.UserTestFixture;
-import com.loopers.infrastructure.user.UserJpaRepository;
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
-import com.loopers.utils.DatabaseCleanUp;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.EnumSource;
-import org.mockito.Mockito;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertAll;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-@SpringBootTest
-@DisplayName("SignUpFacade 통합 테스트")
-class SignUpFacadeIntegrationTest {
- @Autowired
- private SignUpFacade signUpFacade;
-
- @MockitoSpyBean
- private UserJpaRepository userJpaRepository;
-
- @Autowired
- private DatabaseCleanUp databaseCleanUp;
-
- @AfterEach
- void tearDown() {
- databaseCleanUp.truncateAllTables();
- }
-
- @DisplayName("회원 가입에 관한 통합 테스트")
- @Nested
- class SignUp {
- @DisplayName("회원가입시 User 저장이 수행된다.")
- @ParameterizedTest
- @EnumSource(Gender.class)
- void returnsSignUpInfo_whenValidIdIsProvided(Gender gender) {
- // arrange
- String userId = UserTestFixture.ValidUser.USER_ID;
- String email = UserTestFixture.ValidUser.EMAIL;
- String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
- Mockito.reset(userJpaRepository);
-
- // act
- SignUpInfo signUpInfo = signUpFacade.signUp(userId, email, birthDate, gender.name());
-
- // assert
- assertAll(
- () -> assertThat(signUpInfo).isNotNull(),
- () -> assertThat(signUpInfo.userId()).isEqualTo(userId),
- () -> verify(userJpaRepository, times(1)).save(any(User.class))
- );
- }
-
- @DisplayName("이미 가입된 ID로 회원가입 시도 시, 실패한다.")
- @ParameterizedTest
- @EnumSource(Gender.class)
- void fails_whenDuplicateUserIdExists(Gender gender) {
- // arrange
- String userId = UserTestFixture.ValidUser.USER_ID;
- String email = UserTestFixture.ValidUser.EMAIL;
- String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
- signUpFacade.signUp(userId, email, birthDate, gender.name());
-
- // act
- CoreException result = assertThrows(CoreException.class, () ->
- signUpFacade.signUp(userId, email, birthDate, gender.name())
- );
-
- // assert
- assertThat(result.getErrorType()).isEqualTo(ErrorType.CONFLICT);
- }
- }
-}
-
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/pointwallet/PointWalletFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/user/UserServiceIntegrationTest.java
similarity index 53%
rename from apps/commerce-api/src/test/java/com/loopers/application/pointwallet/PointWalletFacadeIntegrationTest.java
rename to apps/commerce-api/src/test/java/com/loopers/application/user/UserServiceIntegrationTest.java
index cebc13975..db7a1de10 100644
--- a/apps/commerce-api/src/test/java/com/loopers/application/pointwallet/PointWalletFacadeIntegrationTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/application/user/UserServiceIntegrationTest.java
@@ -1,8 +1,10 @@
-package com.loopers.application.pointwallet;
+package com.loopers.application.user;
-import com.loopers.application.signup.SignUpFacade;
import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.Point;
+import com.loopers.domain.user.User;
import com.loopers.domain.user.UserTestFixture;
+import com.loopers.infrastructure.user.UserJpaRepository;
import com.loopers.support.error.CoreException;
import com.loopers.support.error.ErrorType;
import com.loopers.utils.DatabaseCleanUp;
@@ -12,21 +14,27 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
+import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
@SpringBootTest
-@DisplayName("PointWalletFacade 통합 테스트")
-class PointWalletFacadeIntegrationTest {
+@DisplayName("UserService 통합 테스트")
+class UserServiceIntegrationTest {
@Autowired
- private PointWalletFacade pointWalletFacade;
+ private UserService userService;
- @Autowired
- private SignUpFacade signUpFacade;
+ @MockitoSpyBean
+ private UserJpaRepository userJpaRepository;
@Autowired
private DatabaseCleanUp databaseCleanUp;
@@ -36,6 +44,57 @@ void tearDown() {
databaseCleanUp.truncateAllTables();
}
+ /**
+ * 테스트용 사용자를 생성합니다.
+ */
+ private void createUser(String userId, String email, String birthDate, Gender gender) {
+ userService.create(userId, email, birthDate, gender, Point.of(0L));
+ }
+
+ @DisplayName("회원 가입에 관한 통합 테스트")
+ @Nested
+ class SignUp {
+ @DisplayName("회원가입시 User 저장이 수행된다.")
+ @ParameterizedTest
+ @EnumSource(Gender.class)
+ void createsUser_whenValidIdIsProvided(Gender gender) {
+ // arrange
+ String userId = UserTestFixture.ValidUser.USER_ID;
+ String email = UserTestFixture.ValidUser.EMAIL;
+ String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
+ Mockito.reset(userJpaRepository);
+
+ // act
+ User user = userService.create(userId, email, birthDate, gender, Point.of(0L));
+
+ // assert
+ assertAll(
+ () -> assertThat(user).isNotNull(),
+ () -> assertThat(user.getUserId()).isEqualTo(userId),
+ () -> verify(userJpaRepository, times(1)).save(any(User.class))
+ );
+ }
+
+ @DisplayName("이미 가입된 ID로 회원가입 시도 시, 실패한다.")
+ @ParameterizedTest
+ @EnumSource(Gender.class)
+ void fails_whenDuplicateUserIdExists(Gender gender) {
+ // arrange
+ String userId = UserTestFixture.ValidUser.USER_ID;
+ String email = UserTestFixture.ValidUser.EMAIL;
+ String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
+ userService.create(userId, email, birthDate, gender, Point.of(0L));
+
+ // act
+ CoreException result = assertThrows(CoreException.class, () ->
+ userService.create(userId, email, birthDate, gender, Point.of(0L))
+ );
+
+ // assert
+ assertThat(result.getErrorType()).isEqualTo(ErrorType.CONFLICT);
+ }
+ }
+
@DisplayName("포인트 조회에 관한 통합 테스트")
@Nested
class PointInfo {
@@ -47,10 +106,10 @@ void returnsPoints_whenUserExists(Gender gender) {
String userId = UserTestFixture.ValidUser.USER_ID;
String email = UserTestFixture.ValidUser.EMAIL;
String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
- signUpFacade.signUp(userId, email, birthDate, gender.name());
+ createUser(userId, email, birthDate, gender);
// act
- PointWalletFacade.PointsInfo pointsInfo = pointWalletFacade.getPoints(userId);
+ UserService.PointsInfo pointsInfo = userService.getPoints(userId);
// assert
assertAll(
@@ -67,7 +126,7 @@ void throwsException_whenUserDoesNotExist() {
String userId = "unknown";
// act & assert
- assertThatThrownBy(() -> pointWalletFacade.getPoints(userId))
+ assertThatThrownBy(() -> userService.getPoints(userId))
.isInstanceOf(CoreException.class)
.hasFieldOrPropertyWithValue("errorType", ErrorType.NOT_FOUND);
}
@@ -84,11 +143,11 @@ void chargesPoints_success(Gender gender) {
String userId = UserTestFixture.ValidUser.USER_ID;
String email = UserTestFixture.ValidUser.EMAIL;
String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
- signUpFacade.signUp(userId, email, birthDate, gender.name());
+ createUser(userId, email, birthDate, gender);
Long chargeAmount = 10_000L;
// act
- PointWalletFacade.PointsInfo pointsInfo = pointWalletFacade.chargePoint(userId, chargeAmount);
+ UserService.PointsInfo pointsInfo = userService.chargePoint(userId, chargeAmount);
// assert
assertAll(
@@ -106,10 +165,9 @@ void throwsException_whenUserDoesNotExist() {
Long chargeAmount = 10_000L;
// act & assert
- assertThatThrownBy(() -> pointWalletFacade.chargePoint(userId, chargeAmount))
+ assertThatThrownBy(() -> userService.chargePoint(userId, chargeAmount))
.isInstanceOf(CoreException.class)
.hasFieldOrPropertyWithValue("errorType", ErrorType.NOT_FOUND);
}
}
}
-
diff --git a/apps/commerce-api/src/test/java/com/loopers/application/userinfo/UserInfoFacadeIntegrationTest.java b/apps/commerce-api/src/test/java/com/loopers/application/userinfo/UserInfoFacadeIntegrationTest.java
deleted file mode 100644
index 78efa5e30..000000000
--- a/apps/commerce-api/src/test/java/com/loopers/application/userinfo/UserInfoFacadeIntegrationTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.loopers.application.userinfo;
-
-import com.loopers.application.signup.SignUpFacade;
-import com.loopers.domain.user.Gender;
-import com.loopers.domain.user.UserTestFixture;
-import com.loopers.support.error.CoreException;
-import com.loopers.support.error.ErrorType;
-import com.loopers.utils.DatabaseCleanUp;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.EnumSource;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-
-import java.time.LocalDate;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.jupiter.api.Assertions.assertAll;
-
-@SpringBootTest
-@DisplayName("UserInfoFacade 통합 테스트")
-class UserInfoFacadeIntegrationTest {
- @Autowired
- private UserInfoFacade userInfoFacade;
-
- @Autowired
- private SignUpFacade signUpFacade;
-
- @Autowired
- private DatabaseCleanUp databaseCleanUp;
-
- @AfterEach
- void tearDown() {
- databaseCleanUp.truncateAllTables();
- }
-
- @DisplayName("회원 조회에 관한 통합 테스트")
- @Nested
- class UserInfo {
- @DisplayName("해당 ID 의 회원이 존재할 경우, 회원 정보가 반환된다.")
- @ParameterizedTest
- @EnumSource(Gender.class)
- void returnsUserInfo_whenUserExists(Gender gender) {
- // arrange
- String userId = UserTestFixture.ValidUser.USER_ID;
- String email = UserTestFixture.ValidUser.EMAIL;
- String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
- signUpFacade.signUp(userId, email, birthDate, gender.name());
-
- // act
- UserInfoFacade.UserInfo userInfo = userInfoFacade.getUserInfo(userId);
-
- // assert
- assertAll(
- () -> assertThat(userInfo).isNotNull(),
- () -> assertThat(userInfo.userId()).isEqualTo(userId),
- () -> assertThat(userInfo.email()).isEqualTo(email),
- () -> assertThat(userInfo.birthDate()).isEqualTo(LocalDate.parse(birthDate)),
- () -> assertThat(userInfo.gender()).isEqualTo(gender)
- );
- }
-
- @DisplayName("해당 ID 의 회원이 존재하지 않을 경우, 예외가 발생한다.")
- @Test
- void throwsException_whenUserDoesNotExist() {
- // arrange
- String userId = "unknown";
-
- // act & assert
- assertThatThrownBy(() -> userInfoFacade.getUserInfo(userId))
- .isInstanceOf(CoreException.class)
- .hasFieldOrPropertyWithValue("errorType", ErrorType.NOT_FOUND);
- }
- }
-}
-
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponServiceTest.java
index c15742303..e0c867d89 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponServiceTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/coupon/CouponServiceTest.java
@@ -1,5 +1,6 @@
package com.loopers.domain.coupon;
+import com.loopers.application.coupon.CouponService;
import com.loopers.domain.coupon.discount.CouponDiscountStrategy;
import com.loopers.domain.coupon.discount.CouponDiscountStrategyFactory;
import com.loopers.support.error.CoreException;
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceTest.java
index 1dab0a951..b259c2dd2 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/order/OrderServiceTest.java
@@ -1,5 +1,6 @@
package com.loopers.domain.order;
+import com.loopers.application.order.OrderService;
import com.loopers.domain.payment.PaymentStatus;
import com.loopers.domain.product.Product;
import com.loopers.domain.user.Gender;
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/payment/PaymentServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/payment/PaymentServiceTest.java
index 963eab173..daf03ca99 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/payment/PaymentServiceTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/payment/PaymentServiceTest.java
@@ -1,6 +1,7 @@
package com.loopers.domain.payment;
-import com.loopers.application.purchasing.PaymentRequestCommand;
+import com.loopers.application.payment.PaymentService;
+import com.loopers.application.payment.PaymentRequestCommand;
import com.loopers.support.error.CoreException;
import com.loopers.support.error.ErrorType;
import org.junit.jupiter.api.BeforeEach;
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceTest.java
index 2ae4afeb2..1b1682d8a 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/product/ProductServiceTest.java
@@ -1,5 +1,6 @@
package com.loopers.domain.product;
+import com.loopers.application.product.ProductService;
import com.loopers.support.error.CoreException;
import com.loopers.support.error.ErrorType;
import org.junit.jupiter.api.DisplayName;
diff --git a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceTest.java b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceTest.java
index 087413f54..e907edbbe 100644
--- a/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceTest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceTest.java
@@ -1,5 +1,6 @@
package com.loopers.domain.user;
+import com.loopers.application.user.UserService;
import com.loopers.support.error.CoreException;
import com.loopers.support.error.ErrorType;
import org.junit.jupiter.api.DisplayName;
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointWalletV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointWalletV1ApiE2ETest.java
index c2629fb78..559a26477 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointWalletV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PointWalletV1ApiE2ETest.java
@@ -1,7 +1,8 @@
package com.loopers.interfaces.api;
-import com.loopers.application.signup.SignUpFacade;
+import com.loopers.application.user.UserService;
import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.Point;
import com.loopers.domain.user.UserTestFixture;
import com.loopers.interfaces.api.pointwallet.PointWalletV1Dto;
import com.loopers.utils.DatabaseCleanUp;
@@ -31,17 +32,17 @@ public class PointWalletV1ApiE2ETest {
private static final String ENDPOINT_POINTS = "/api/v1/me/points";
private final TestRestTemplate testRestTemplate;
- private final SignUpFacade signUpFacade;
+ private final UserService userService;
private final DatabaseCleanUp databaseCleanUp;
@Autowired
public PointWalletV1ApiE2ETest(
TestRestTemplate testRestTemplate,
- SignUpFacade signUpFacade,
+ UserService userService,
DatabaseCleanUp databaseCleanUp
) {
this.testRestTemplate = testRestTemplate;
- this.signUpFacade = signUpFacade;
+ this.userService = userService;
this.databaseCleanUp = databaseCleanUp;
}
@@ -61,7 +62,7 @@ void returnsPoints_whenUserExists(Gender gender) {
String userId = UserTestFixture.ValidUser.USER_ID;
String email = UserTestFixture.ValidUser.EMAIL;
String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
- signUpFacade.signUp(userId, email, birthDate, gender.name());
+ userService.create(userId, email, birthDate, gender, Point.of(0L));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
@@ -145,7 +146,7 @@ void returnsChargedBalance_whenUserExists(Gender gender) {
String userId = UserTestFixture.ValidUser.USER_ID;
String email = UserTestFixture.ValidUser.EMAIL;
String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
- signUpFacade.signUp(userId, email, birthDate, gender.name());
+ userService.create(userId, email, birthDate, gender, Point.of(0L));
Long chargeAmount = 1000L;
PointWalletV1Dto.ChargeRequest requestBody = new PointWalletV1Dto.ChargeRequest(chargeAmount);
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PurchasingV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PurchasingV1ApiE2ETest.java
index a09516c93..9c597b09b 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PurchasingV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/PurchasingV1ApiE2ETest.java
@@ -1,6 +1,6 @@
package com.loopers.interfaces.api;
-import com.loopers.application.pointwallet.PointWalletFacade;
+import com.loopers.application.user.UserService;
import com.loopers.application.signup.SignUpFacade;
import com.loopers.domain.brand.Brand;
import com.loopers.domain.brand.BrandRepository;
@@ -72,7 +72,7 @@ public class PurchasingV1ApiE2ETest {
private SignUpFacade signUpFacade;
@Autowired
- private PointWalletFacade pointWalletFacade;
+ private UserService userService;
@Autowired
private ProductRepository productRepository;
@@ -108,7 +108,7 @@ private HttpEntity createOrderRequest(Long produc
String email = UserTestFixture.ValidUser.EMAIL;
String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
signUpFacade.signUp(userId, email, birthDate, Gender.MALE.name());
- pointWalletFacade.chargePoint(userId, 500_000L);
+ userService.chargePoint(userId, 500_000L);
Brand brand = Brand.of("테스트 브랜드");
Brand savedBrand = brandRepository.save(brand);
@@ -240,7 +240,7 @@ void returns200_whenPaymentCallbackSuccess() {
String email = UserTestFixture.ValidUser.EMAIL;
String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
signUpFacade.signUp(userId, email, birthDate, Gender.MALE.name());
- pointWalletFacade.chargePoint(userId, 500_000L);
+ userService.chargePoint(userId, 500_000L);
Brand brand = Brand.of("테스트 브랜드");
Brand savedBrand = brandRepository.save(brand);
@@ -345,7 +345,7 @@ void returns200_whenPaymentCallbackFailure() {
String email = UserTestFixture.ValidUser.EMAIL;
String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
signUpFacade.signUp(userId, email, birthDate, Gender.MALE.name());
- pointWalletFacade.chargePoint(userId, 500_000L);
+ userService.chargePoint(userId, 500_000L);
Brand brand = Brand.of("테스트 브랜드");
Brand savedBrand = brandRepository.save(brand);
@@ -456,7 +456,7 @@ void returns200_whenOrderStatusRecovered() {
String email = UserTestFixture.ValidUser.EMAIL;
String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
signUpFacade.signUp(userId, email, birthDate, Gender.MALE.name());
- pointWalletFacade.chargePoint(userId, 500_000L);
+ userService.chargePoint(userId, 500_000L);
Brand brand = Brand.of("테스트 브랜드");
Brand savedBrand = brandRepository.save(brand);
diff --git a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserInfoV1ApiE2ETest.java b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserInfoV1ApiE2ETest.java
index f9d33a421..6cea33855 100644
--- a/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserInfoV1ApiE2ETest.java
+++ b/apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserInfoV1ApiE2ETest.java
@@ -1,7 +1,8 @@
package com.loopers.interfaces.api;
-import com.loopers.application.signup.SignUpFacade;
+import com.loopers.application.user.UserService;
import com.loopers.domain.user.Gender;
+import com.loopers.domain.user.Point;
import com.loopers.domain.user.UserTestFixture;
import com.loopers.interfaces.api.userinfo.UserInfoV1Dto;
import com.loopers.utils.DatabaseCleanUp;
@@ -31,17 +32,17 @@ public class UserInfoV1ApiE2ETest {
private static final String ENDPOINT_ME = "/api/v1/me";
private final TestRestTemplate testRestTemplate;
- private final SignUpFacade signUpFacade;
+ private final UserService userService;
private final DatabaseCleanUp databaseCleanUp;
@Autowired
public UserInfoV1ApiE2ETest(
TestRestTemplate testRestTemplate,
- SignUpFacade signUpFacade,
+ UserService userService,
DatabaseCleanUp databaseCleanUp
) {
this.testRestTemplate = testRestTemplate;
- this.signUpFacade = signUpFacade;
+ this.userService = userService;
this.databaseCleanUp = databaseCleanUp;
}
@@ -61,7 +62,7 @@ void returnsUserInfo_whenUserExists(Gender gender) {
String userId = UserTestFixture.ValidUser.USER_ID;
String email = UserTestFixture.ValidUser.EMAIL;
String birthDate = UserTestFixture.ValidUser.BIRTH_DATE;
- signUpFacade.signUp(userId, email, birthDate, gender.name());
+ userService.create(userId, email, birthDate, gender, Point.of(0L));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);