From 769540c19a72bc1561096ded520a692c498fcf9b Mon Sep 17 00:00:00 2001 From: seovalue Date: Sat, 15 Feb 2025 09:53:12 +0900 Subject: [PATCH 1/9] refactor: rename Product to ProductRecord --- REQUIREMENTS.md | 8 +++++ .../menus/application/MenuService.java | 6 ++-- .../kitchenpos/menus/domain/MenuProduct.java | 8 ++--- .../products/application/ProductService.java | 12 +++---- .../products/domain/JpaProductRepository.java | 2 +- .../{Product.java => ProductRecord.java} | 4 +-- .../products/domain/ProductRepository.java | 9 +++--- .../products/ui/ProductRestController.java | 10 +++--- src/test/java/kitchenpos/Fixtures.java | 10 +++--- .../menus/application/MenuServiceTest.java | 4 +-- .../InMemoryProductRepository.java | 12 +++---- .../application/ProductServiceTest.java | 32 +++++++++---------- 12 files changed, 62 insertions(+), 55 deletions(-) create mode 100644 REQUIREMENTS.md rename src/main/java/kitchenpos/products/domain/{Product.java => ProductRecord.java} (94%) diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md new file mode 100644 index 000000000..37f621fa0 --- /dev/null +++ b/REQUIREMENTS.md @@ -0,0 +1,8 @@ +## 도메인 주도 설계 기본 요소 +### 1단계 - 리팩터링(상품) +- 키친포스의 요구 사항과 용어 사전, 모델링을 기반으로 상품 CONTEXT를 리팩터링한다. +- 상품 CONTEXT의 도메인 계층만 먼저 구현한다. +- products 패키지 밑에 tobe.domain 패키지를 만들고 거기서부터 구현을 시작한다. +- 용어 사전과 모델링이 부자연스럽거나 불완전하거나 잘못된 경우 지속적으로 수정한다. +- 새로운 모델에 맞게끔 클래스, 메서드, 모듈의 이름을 다시 지으면서 코드를 리팩터링한다. +- REPOSITORY 구현 시 자신에게 익숙하고 편한 것을 선택하여 진행한다. diff --git a/src/main/java/kitchenpos/menus/application/MenuService.java b/src/main/java/kitchenpos/menus/application/MenuService.java index abefa5bcf..3847e5867 100644 --- a/src/main/java/kitchenpos/menus/application/MenuService.java +++ b/src/main/java/kitchenpos/menus/application/MenuService.java @@ -5,7 +5,7 @@ import kitchenpos.menus.domain.MenuGroupRepository; import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; -import kitchenpos.products.domain.Product; +import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; import kitchenpos.products.infra.PurgomalumClient; import org.springframework.stereotype.Service; @@ -49,7 +49,7 @@ public Menu create(final Menu request) { if (Objects.isNull(menuProductRequests) || menuProductRequests.isEmpty()) { throw new IllegalArgumentException(); } - final List products = productRepository.findAllByIdIn( + final List products = productRepository.findAllByIdIn( menuProductRequests.stream() .map(MenuProduct::getProductId) .toList() @@ -64,7 +64,7 @@ public Menu create(final Menu request) { if (quantity < 0) { throw new IllegalArgumentException(); } - final Product product = productRepository.findById(menuProductRequest.getProductId()) + final ProductRecord product = productRepository.findById(menuProductRequest.getProductId()) .orElseThrow(NoSuchElementException::new); sum = sum.add( product.getPrice() diff --git a/src/main/java/kitchenpos/menus/domain/MenuProduct.java b/src/main/java/kitchenpos/menus/domain/MenuProduct.java index b47ca26cb..f8d146961 100644 --- a/src/main/java/kitchenpos/menus/domain/MenuProduct.java +++ b/src/main/java/kitchenpos/menus/domain/MenuProduct.java @@ -10,7 +10,7 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.Transient; -import kitchenpos.products.domain.Product; +import kitchenpos.products.domain.ProductRecord; import java.util.UUID; @@ -28,7 +28,7 @@ public class MenuProduct { columnDefinition = "binary(16)", foreignKey = @ForeignKey(name = "fk_menu_product_to_product") ) - private Product product; + private ProductRecord product; @Column(name = "quantity", nullable = false) private long quantity; @@ -47,11 +47,11 @@ public void setSeq(final Long seq) { this.seq = seq; } - public Product getProduct() { + public ProductRecord getProduct() { return product; } - public void setProduct(final Product product) { + public void setProduct(final ProductRecord product) { this.product = product; } diff --git a/src/main/java/kitchenpos/products/application/ProductService.java b/src/main/java/kitchenpos/products/application/ProductService.java index 20cf63996..1a2c4d46e 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/products/application/ProductService.java @@ -3,7 +3,7 @@ import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; -import kitchenpos.products.domain.Product; +import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; import kitchenpos.products.infra.PurgomalumClient; import org.springframework.stereotype.Service; @@ -32,7 +32,7 @@ public ProductService( } @Transactional - public Product create(final Product request) { + public ProductRecord create(final ProductRecord request) { final BigDecimal price = request.getPrice(); if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException(); @@ -41,7 +41,7 @@ public Product create(final Product request) { if (Objects.isNull(name) || purgomalumClient.containsProfanity(name)) { throw new IllegalArgumentException(); } - final Product product = new Product(); + final ProductRecord product = new ProductRecord(); product.setId(UUID.randomUUID()); product.setName(name); product.setPrice(price); @@ -49,12 +49,12 @@ public Product create(final Product request) { } @Transactional - public Product changePrice(final UUID productId, final Product request) { + public ProductRecord changePrice(final UUID productId, final ProductRecord request) { final BigDecimal price = request.getPrice(); if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException(); } - final Product product = productRepository.findById(productId) + final ProductRecord product = productRepository.findById(productId) .orElseThrow(NoSuchElementException::new); product.setPrice(price); final List menus = menuRepository.findAllByProductId(productId); @@ -75,7 +75,7 @@ public Product changePrice(final UUID productId, final Product request) { } @Transactional(readOnly = true) - public List findAll() { + public List findAll() { return productRepository.findAll(); } } diff --git a/src/main/java/kitchenpos/products/domain/JpaProductRepository.java b/src/main/java/kitchenpos/products/domain/JpaProductRepository.java index 90b069779..2ff9247ca 100644 --- a/src/main/java/kitchenpos/products/domain/JpaProductRepository.java +++ b/src/main/java/kitchenpos/products/domain/JpaProductRepository.java @@ -4,5 +4,5 @@ import java.util.UUID; -public interface JpaProductRepository extends ProductRepository, JpaRepository { +public interface JpaProductRepository extends ProductRepository, JpaRepository { } diff --git a/src/main/java/kitchenpos/products/domain/Product.java b/src/main/java/kitchenpos/products/domain/ProductRecord.java similarity index 94% rename from src/main/java/kitchenpos/products/domain/Product.java rename to src/main/java/kitchenpos/products/domain/ProductRecord.java index ee2a7dfa9..5e105ecf5 100644 --- a/src/main/java/kitchenpos/products/domain/Product.java +++ b/src/main/java/kitchenpos/products/domain/ProductRecord.java @@ -10,7 +10,7 @@ @Table(name = "product") @Entity -public class Product { +public class ProductRecord { @Column(name = "id", columnDefinition = "binary(16)") @Id private UUID id; @@ -21,7 +21,7 @@ public class Product { @Column(name = "price", nullable = false) private BigDecimal price; - public Product() { + public ProductRecord() { } public UUID getId() { diff --git a/src/main/java/kitchenpos/products/domain/ProductRepository.java b/src/main/java/kitchenpos/products/domain/ProductRepository.java index 3637e4232..8602f7aa5 100644 --- a/src/main/java/kitchenpos/products/domain/ProductRepository.java +++ b/src/main/java/kitchenpos/products/domain/ProductRepository.java @@ -5,12 +5,11 @@ import java.util.UUID; public interface ProductRepository { - Product save(Product product); + ProductRecord save(ProductRecord product); - Optional findById(UUID id); + Optional findById(UUID id); - List findAll(); + List findAll(); - List findAllByIdIn(List ids); + List findAllByIdIn(List ids); } - diff --git a/src/main/java/kitchenpos/products/ui/ProductRestController.java b/src/main/java/kitchenpos/products/ui/ProductRestController.java index c71c795a4..24c5cc311 100644 --- a/src/main/java/kitchenpos/products/ui/ProductRestController.java +++ b/src/main/java/kitchenpos/products/ui/ProductRestController.java @@ -1,7 +1,7 @@ package kitchenpos.products.ui; import kitchenpos.products.application.ProductService; -import kitchenpos.products.domain.Product; +import kitchenpos.products.domain.ProductRecord; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -25,19 +25,19 @@ public ProductRestController(final ProductService productService) { } @PostMapping - public ResponseEntity create(@RequestBody final Product request) { - final Product response = productService.create(request); + public ResponseEntity create(@RequestBody final ProductRecord request) { + final ProductRecord response = productService.create(request); return ResponseEntity.created(URI.create("/api/products/" + response.getId())) .body(response); } @PutMapping("/{productId}/price") - public ResponseEntity changePrice(@PathVariable final UUID productId, @RequestBody final Product request) { + public ResponseEntity changePrice(@PathVariable final UUID productId, @RequestBody final ProductRecord request) { return ResponseEntity.ok(productService.changePrice(productId, request)); } @GetMapping - public ResponseEntity> findAll() { + public ResponseEntity> findAll() { return ResponseEntity.ok(productService.findAll()); } } diff --git a/src/test/java/kitchenpos/Fixtures.java b/src/test/java/kitchenpos/Fixtures.java index 434768a52..d54a70a32 100644 --- a/src/test/java/kitchenpos/Fixtures.java +++ b/src/test/java/kitchenpos/Fixtures.java @@ -8,7 +8,7 @@ import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuGroup; import kitchenpos.menus.domain.MenuProduct; -import kitchenpos.products.domain.Product; +import kitchenpos.products.domain.ProductRecord; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -57,7 +57,7 @@ public static MenuProduct menuProduct() { return menuProduct; } - public static MenuProduct menuProduct(final Product product, final long quantity) { + public static MenuProduct menuProduct(final ProductRecord product, final long quantity) { final MenuProduct menuProduct = new MenuProduct(); menuProduct.setSeq(new Random().nextLong()); menuProduct.setProduct(product); @@ -117,12 +117,12 @@ public static OrderTable orderTable(final boolean occupied, final int numberOfGu return orderTable; } - public static Product product() { + public static ProductRecord product() { return product("후라이드", 16_000L); } - public static Product product(final String name, final long price) { - final Product product = new Product(); + public static ProductRecord product(final String name, final long price) { + final ProductRecord product = new ProductRecord(); product.setId(UUID.randomUUID()); product.setName(name); product.setPrice(BigDecimal.valueOf(price)); diff --git a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java index 277679118..5fb7976e0 100644 --- a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java +++ b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java @@ -6,7 +6,7 @@ import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.application.FakePurgomalumClient; import kitchenpos.products.application.InMemoryProductRepository; -import kitchenpos.products.domain.Product; +import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; import kitchenpos.products.infra.PurgomalumClient; import org.junit.jupiter.api.BeforeEach; @@ -41,7 +41,7 @@ class MenuServiceTest { private PurgomalumClient purgomalumClient; private MenuService menuService; private UUID menuGroupId; - private Product product; + private ProductRecord product; @BeforeEach void setUp() { diff --git a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java index b55c5ec5e..7bc4c22d5 100644 --- a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java +++ b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java @@ -1,6 +1,6 @@ package kitchenpos.products.application; -import kitchenpos.products.domain.Product; +import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; import java.util.ArrayList; @@ -11,26 +11,26 @@ import java.util.UUID; public class InMemoryProductRepository implements ProductRepository { - private final Map products = new HashMap<>(); + private final Map products = new HashMap<>(); @Override - public Product save(final Product product) { + public ProductRecord save(final ProductRecord product) { products.put(product.getId(), product); return product; } @Override - public Optional findById(final UUID id) { + public Optional findById(final UUID id) { return Optional.ofNullable(products.get(id)); } @Override - public List findAll() { + public List findAll() { return new ArrayList<>(products.values()); } @Override - public List findAllByIdIn(final List ids) { + public List findAllByIdIn(final List ids) { return products.values() .stream() .filter(product -> ids.contains(product.getId())) diff --git a/src/test/java/kitchenpos/products/application/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/ProductServiceTest.java index 74a31073e..7b08aa05e 100644 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/application/ProductServiceTest.java @@ -3,7 +3,7 @@ import kitchenpos.menus.application.InMemoryMenuRepository; import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuRepository; -import kitchenpos.products.domain.Product; +import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; import kitchenpos.products.infra.PurgomalumClient; import org.junit.jupiter.api.BeforeEach; @@ -41,8 +41,8 @@ void setUp() { @DisplayName("상품을 등록할 수 있다.") @Test void create() { - final Product expected = createProductRequest("후라이드", 16_000L); - final Product actual = productService.create(expected); + final ProductRecord expected = createProductRequest("후라이드", 16_000L); + final ProductRecord actual = productService.create(expected); assertThat(actual).isNotNull(); assertAll( () -> assertThat(actual.getId()).isNotNull(), @@ -56,7 +56,7 @@ void create() { @NullSource @ParameterizedTest void create(final BigDecimal price) { - final Product expected = createProductRequest("후라이드", price); + final ProductRecord expected = createProductRequest("후라이드", price); assertThatThrownBy(() -> productService.create(expected)) .isInstanceOf(IllegalArgumentException.class); } @@ -66,7 +66,7 @@ void create(final BigDecimal price) { @NullSource @ParameterizedTest void create(final String name) { - final Product expected = createProductRequest(name, 16_000L); + final ProductRecord expected = createProductRequest(name, 16_000L); assertThatThrownBy(() -> productService.create(expected)) .isInstanceOf(IllegalArgumentException.class); } @@ -75,8 +75,8 @@ void create(final String name) { @Test void changePrice() { final UUID productId = productRepository.save(product("후라이드", 16_000L)).getId(); - final Product expected = changePriceRequest(15_000L); - final Product actual = productService.changePrice(productId, expected); + final ProductRecord expected = changePriceRequest(15_000L); + final ProductRecord actual = productService.changePrice(productId, expected); assertThat(actual.getPrice()).isEqualTo(expected.getPrice()); } @@ -86,7 +86,7 @@ void changePrice() { @ParameterizedTest void changePrice(final BigDecimal price) { final UUID productId = productRepository.save(product("후라이드", 16_000L)).getId(); - final Product expected = changePriceRequest(price); + final ProductRecord expected = changePriceRequest(price); assertThatThrownBy(() -> productService.changePrice(productId, expected)) .isInstanceOf(IllegalArgumentException.class); } @@ -94,7 +94,7 @@ void changePrice(final BigDecimal price) { @DisplayName("상품의 가격이 변경될 때 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 크면 메뉴가 숨겨진다.") @Test void changePriceInMenu() { - final Product product = productRepository.save(product("후라이드", 16_000L)); + final ProductRecord product = productRepository.save(product("후라이드", 16_000L)); final Menu menu = menuRepository.save(menu(19_000L, true, menuProduct(product, 2L))); productService.changePrice(product.getId(), changePriceRequest(8_000L)); assertThat(menuRepository.findById(menu.getId()).get().isDisplayed()).isFalse(); @@ -105,27 +105,27 @@ void changePriceInMenu() { void findAll() { productRepository.save(product("후라이드", 16_000L)); productRepository.save(product("양념치킨", 16_000L)); - final List actual = productService.findAll(); + final List actual = productService.findAll(); assertThat(actual).hasSize(2); } - private Product createProductRequest(final String name, final long price) { + private ProductRecord createProductRequest(final String name, final long price) { return createProductRequest(name, BigDecimal.valueOf(price)); } - private Product createProductRequest(final String name, final BigDecimal price) { - final Product product = new Product(); + private ProductRecord createProductRequest(final String name, final BigDecimal price) { + final ProductRecord product = new ProductRecord(); product.setName(name); product.setPrice(price); return product; } - private Product changePriceRequest(final long price) { + private ProductRecord changePriceRequest(final long price) { return changePriceRequest(BigDecimal.valueOf(price)); } - private Product changePriceRequest(final BigDecimal price) { - final Product product = new Product(); + private ProductRecord changePriceRequest(final BigDecimal price) { + final ProductRecord product = new ProductRecord(); product.setPrice(price); return product; } From d1aa49a0581e90b8a2bd0f96960ca7efa4d01b30 Mon Sep 17 00:00:00 2001 From: seovalue Date: Sat, 15 Feb 2025 09:59:59 +0900 Subject: [PATCH 2/9] feat: implement Product --- .../kitchenpos/products/domain/Product.kt | 9 +++++++ .../kitchenpos/products/domain/ProductName.kt | 27 +++++++++++++++++++ .../products/domain/ProductPrice.kt | 27 +++++++++++++++++++ .../profanity/ProfanityCheckClient.kt | 5 ++++ 4 files changed, 68 insertions(+) create mode 100644 src/main/java/kitchenpos/products/domain/Product.kt create mode 100644 src/main/java/kitchenpos/products/domain/ProductName.kt create mode 100644 src/main/java/kitchenpos/products/domain/ProductPrice.kt create mode 100644 src/main/java/kitchenpos/profanity/ProfanityCheckClient.kt diff --git a/src/main/java/kitchenpos/products/domain/Product.kt b/src/main/java/kitchenpos/products/domain/Product.kt new file mode 100644 index 000000000..f2ae14041 --- /dev/null +++ b/src/main/java/kitchenpos/products/domain/Product.kt @@ -0,0 +1,9 @@ +package kitchenpos.products.domain + +import java.util.* + +data class Product( + private val id: UUID, + private val name: ProductName, + private val price: ProductPrice, +) diff --git a/src/main/java/kitchenpos/products/domain/ProductName.kt b/src/main/java/kitchenpos/products/domain/ProductName.kt new file mode 100644 index 000000000..e17f404fd --- /dev/null +++ b/src/main/java/kitchenpos/products/domain/ProductName.kt @@ -0,0 +1,27 @@ +package kitchenpos.products.domain + +import kitchenpos.profanity.ProfanityCheckClient + +class ProductName private constructor( + val name: String, +) { + companion object Factory { + fun create(value: String, profanityCheckClient: ProfanityCheckClient): ProductName { + validateNull(value) + validateProfanity(value, profanityCheckClient) + return ProductName(value) + } + + private fun validateNull(value: String) { + if (value.isBlank()) { + throw IllegalArgumentException("Invalid product name: $value") + } + } + + private fun validateProfanity(value: String, profanityCheckClient: ProfanityCheckClient) { + if (profanityCheckClient.containsProfanity(value)) { + throw IllegalArgumentException("Profanity is not allowed: $value") + } + } + } +} diff --git a/src/main/java/kitchenpos/products/domain/ProductPrice.kt b/src/main/java/kitchenpos/products/domain/ProductPrice.kt new file mode 100644 index 000000000..282fea72e --- /dev/null +++ b/src/main/java/kitchenpos/products/domain/ProductPrice.kt @@ -0,0 +1,27 @@ +package kitchenpos.products.domain + +import java.math.BigDecimal + +class ProductPrice private constructor( + val price: BigDecimal +) { + companion object Factory { + fun create(value: BigDecimal): ProductPrice { + validateNull(value) + validatePositive(value) + return ProductPrice(value) + } + + private fun validateNull(value: BigDecimal) { + if (value <= BigDecimal.ZERO) { + throw IllegalArgumentException("Invalid product price: $value") + } + } + + private fun validatePositive(value: BigDecimal) { + if (value < BigDecimal.ZERO) { + throw IllegalArgumentException("Invalid product price: $value") + } + } + } +} diff --git a/src/main/java/kitchenpos/profanity/ProfanityCheckClient.kt b/src/main/java/kitchenpos/profanity/ProfanityCheckClient.kt new file mode 100644 index 000000000..815b7464b --- /dev/null +++ b/src/main/java/kitchenpos/profanity/ProfanityCheckClient.kt @@ -0,0 +1,5 @@ +package kitchenpos.profanity + +interface ProfanityCheckClient { + fun containsProfanity(text: String?): Boolean +} From acfa03b1deaf8b060a617aefd83eaa002afcc0e2 Mon Sep 17 00:00:00 2001 From: seovalue Date: Sat, 15 Feb 2025 10:02:12 +0900 Subject: [PATCH 3/9] refactor: rename to checkBadWordClient --- .../menus/application/MenuService.java | 10 ++++----- .../products/application/ProductService.java | 22 +++++++++---------- .../kitchenpos/products/domain/ProductName.kt | 8 +++---- ...nt.java => DefaultCheckBadWordClient.java} | 4 ++-- ...lumClient.java => checkBadWordClient.java} | 2 +- .../profanity/ProfanityCheckClient.kt | 5 ----- .../menus/application/MenuServiceTest.java | 10 ++++----- ...lient.java => FakeCheckBadWordClient.java} | 4 ++-- .../application/ProductServiceTest.java | 8 +++---- 9 files changed, 34 insertions(+), 39 deletions(-) rename src/main/java/kitchenpos/products/infra/{DefaultPurgomalumClient.java => DefaultCheckBadWordClient.java} (82%) rename src/main/java/kitchenpos/products/infra/{PurgomalumClient.java => checkBadWordClient.java} (68%) delete mode 100644 src/main/java/kitchenpos/profanity/ProfanityCheckClient.kt rename src/test/java/kitchenpos/products/application/{FakePurgomalumClient.java => FakeCheckBadWordClient.java} (76%) diff --git a/src/main/java/kitchenpos/menus/application/MenuService.java b/src/main/java/kitchenpos/menus/application/MenuService.java index 3847e5867..ff18bf673 100644 --- a/src/main/java/kitchenpos/menus/application/MenuService.java +++ b/src/main/java/kitchenpos/menus/application/MenuService.java @@ -7,7 +7,7 @@ import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.products.infra.checkBadWordClient; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -23,18 +23,18 @@ public class MenuService { private final MenuRepository menuRepository; private final MenuGroupRepository menuGroupRepository; private final ProductRepository productRepository; - private final PurgomalumClient purgomalumClient; + private final checkBadWordClient checkBadWordClient; public MenuService( final MenuRepository menuRepository, final MenuGroupRepository menuGroupRepository, final ProductRepository productRepository, - final PurgomalumClient purgomalumClient + final checkBadWordClient checkBadWordClient ) { this.menuRepository = menuRepository; this.menuGroupRepository = menuGroupRepository; this.productRepository = productRepository; - this.purgomalumClient = purgomalumClient; + this.checkBadWordClient = checkBadWordClient; } @Transactional @@ -79,7 +79,7 @@ public Menu create(final Menu request) { throw new IllegalArgumentException(); } final String name = request.getName(); - if (Objects.isNull(name) || purgomalumClient.containsProfanity(name)) { + if (Objects.isNull(name) || checkBadWordClient.containsProfanity(name)) { throw new IllegalArgumentException(); } final Menu menu = new Menu(); diff --git a/src/main/java/kitchenpos/products/application/ProductService.java b/src/main/java/kitchenpos/products/application/ProductService.java index 1a2c4d46e..30e433f2b 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/products/application/ProductService.java @@ -3,9 +3,9 @@ import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; -import kitchenpos.products.domain.ProductRecord; +import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.products.infra.checkBadWordClient; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -19,29 +19,29 @@ public class ProductService { private final ProductRepository productRepository; private final MenuRepository menuRepository; - private final PurgomalumClient purgomalumClient; + private final checkBadWordClient checkBadWordClient; public ProductService( final ProductRepository productRepository, final MenuRepository menuRepository, - final PurgomalumClient purgomalumClient + final checkBadWordClient checkBadWordClient ) { this.productRepository = productRepository; this.menuRepository = menuRepository; - this.purgomalumClient = purgomalumClient; + this.checkBadWordClient = checkBadWordClient; } @Transactional - public ProductRecord create(final ProductRecord request) { + public Product create(final Product request) { final BigDecimal price = request.getPrice(); if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException(); } final String name = request.getName(); - if (Objects.isNull(name) || purgomalumClient.containsProfanity(name)) { + if (Objects.isNull(name) || checkBadWordClient.containsProfanity(name)) { throw new IllegalArgumentException(); } - final ProductRecord product = new ProductRecord(); + final Product product = new Product(); product.setId(UUID.randomUUID()); product.setName(name); product.setPrice(price); @@ -49,12 +49,12 @@ public ProductRecord create(final ProductRecord request) { } @Transactional - public ProductRecord changePrice(final UUID productId, final ProductRecord request) { + public Product changePrice(final UUID productId, final Product request) { final BigDecimal price = request.getPrice(); if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException(); } - final ProductRecord product = productRepository.findById(productId) + final Product product = productRepository.findById(productId) .orElseThrow(NoSuchElementException::new); product.setPrice(price); final List menus = menuRepository.findAllByProductId(productId); @@ -75,7 +75,7 @@ public ProductRecord changePrice(final UUID productId, final ProductRecord reque } @Transactional(readOnly = true) - public List findAll() { + public List findAll() { return productRepository.findAll(); } } diff --git a/src/main/java/kitchenpos/products/domain/ProductName.kt b/src/main/java/kitchenpos/products/domain/ProductName.kt index e17f404fd..cd7925061 100644 --- a/src/main/java/kitchenpos/products/domain/ProductName.kt +++ b/src/main/java/kitchenpos/products/domain/ProductName.kt @@ -1,14 +1,14 @@ package kitchenpos.products.domain -import kitchenpos.profanity.ProfanityCheckClient +import kitchenpos.products.infra.checkBadWordClient class ProductName private constructor( val name: String, ) { companion object Factory { - fun create(value: String, profanityCheckClient: ProfanityCheckClient): ProductName { + fun create(value: String, checkBadWordClient: checkBadWordClient): ProductName { validateNull(value) - validateProfanity(value, profanityCheckClient) + validateProfanity(value, checkBadWordClient) return ProductName(value) } @@ -18,7 +18,7 @@ class ProductName private constructor( } } - private fun validateProfanity(value: String, profanityCheckClient: ProfanityCheckClient) { + private fun validateProfanity(value: String, profanityCheckClient: checkBadWordClient) { if (profanityCheckClient.containsProfanity(value)) { throw IllegalArgumentException("Profanity is not allowed: $value") } diff --git a/src/main/java/kitchenpos/products/infra/DefaultPurgomalumClient.java b/src/main/java/kitchenpos/products/infra/DefaultCheckBadWordClient.java similarity index 82% rename from src/main/java/kitchenpos/products/infra/DefaultPurgomalumClient.java rename to src/main/java/kitchenpos/products/infra/DefaultCheckBadWordClient.java index 87dba885c..09f26ab72 100644 --- a/src/main/java/kitchenpos/products/infra/DefaultPurgomalumClient.java +++ b/src/main/java/kitchenpos/products/infra/DefaultCheckBadWordClient.java @@ -8,10 +8,10 @@ import java.net.URI; @Component -public class DefaultPurgomalumClient implements PurgomalumClient { +public class DefaultCheckBadWordClient implements checkBadWordClient { private final RestTemplate restTemplate; - public DefaultPurgomalumClient(final RestTemplateBuilder restTemplateBuilder) { + public DefaultCheckBadWordClient(final RestTemplateBuilder restTemplateBuilder) { this.restTemplate = restTemplateBuilder.build(); } diff --git a/src/main/java/kitchenpos/products/infra/PurgomalumClient.java b/src/main/java/kitchenpos/products/infra/checkBadWordClient.java similarity index 68% rename from src/main/java/kitchenpos/products/infra/PurgomalumClient.java rename to src/main/java/kitchenpos/products/infra/checkBadWordClient.java index 4002a2bb8..f146b4523 100644 --- a/src/main/java/kitchenpos/products/infra/PurgomalumClient.java +++ b/src/main/java/kitchenpos/products/infra/checkBadWordClient.java @@ -1,5 +1,5 @@ package kitchenpos.products.infra; -public interface PurgomalumClient { +public interface checkBadWordClient { boolean containsProfanity(String text); } diff --git a/src/main/java/kitchenpos/profanity/ProfanityCheckClient.kt b/src/main/java/kitchenpos/profanity/ProfanityCheckClient.kt deleted file mode 100644 index 815b7464b..000000000 --- a/src/main/java/kitchenpos/profanity/ProfanityCheckClient.kt +++ /dev/null @@ -1,5 +0,0 @@ -package kitchenpos.profanity - -interface ProfanityCheckClient { - fun containsProfanity(text: String?): Boolean -} diff --git a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java index 5fb7976e0..ad3590053 100644 --- a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java +++ b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java @@ -4,11 +4,11 @@ import kitchenpos.menus.domain.MenuGroupRepository; import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; -import kitchenpos.products.application.FakePurgomalumClient; +import kitchenpos.products.application.FakeCheckBadWordClient; import kitchenpos.products.application.InMemoryProductRepository; import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.products.infra.checkBadWordClient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -38,7 +38,7 @@ class MenuServiceTest { private MenuRepository menuRepository; private MenuGroupRepository menuGroupRepository; private ProductRepository productRepository; - private PurgomalumClient purgomalumClient; + private checkBadWordClient checkBadWordClient; private MenuService menuService; private UUID menuGroupId; private ProductRecord product; @@ -48,8 +48,8 @@ void setUp() { menuRepository = new InMemoryMenuRepository(); menuGroupRepository = new InMemoryMenuGroupRepository(); productRepository = new InMemoryProductRepository(); - purgomalumClient = new FakePurgomalumClient(); - menuService = new MenuService(menuRepository, menuGroupRepository, productRepository, purgomalumClient); + checkBadWordClient = new FakeCheckBadWordClient(); + menuService = new MenuService(menuRepository, menuGroupRepository, productRepository, checkBadWordClient); menuGroupId = menuGroupRepository.save(menuGroup()).getId(); product = productRepository.save(product("후라이드", 16_000L)); } diff --git a/src/test/java/kitchenpos/products/application/FakePurgomalumClient.java b/src/test/java/kitchenpos/products/application/FakeCheckBadWordClient.java similarity index 76% rename from src/test/java/kitchenpos/products/application/FakePurgomalumClient.java rename to src/test/java/kitchenpos/products/application/FakeCheckBadWordClient.java index 3c4114798..a68cac172 100644 --- a/src/test/java/kitchenpos/products/application/FakePurgomalumClient.java +++ b/src/test/java/kitchenpos/products/application/FakeCheckBadWordClient.java @@ -1,11 +1,11 @@ package kitchenpos.products.application; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.products.infra.checkBadWordClient; import java.util.Arrays; import java.util.List; -public class FakePurgomalumClient implements PurgomalumClient { +public class FakeCheckBadWordClient implements checkBadWordClient { private static final List profanities; static { diff --git a/src/test/java/kitchenpos/products/application/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/ProductServiceTest.java index 7b08aa05e..9b642ba52 100644 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/application/ProductServiceTest.java @@ -5,7 +5,7 @@ import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.products.infra.checkBadWordClient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -27,15 +27,15 @@ class ProductServiceTest { private ProductRepository productRepository; private MenuRepository menuRepository; - private PurgomalumClient purgomalumClient; + private checkBadWordClient checkBadWordClient; private ProductService productService; @BeforeEach void setUp() { productRepository = new InMemoryProductRepository(); menuRepository = new InMemoryMenuRepository(); - purgomalumClient = new FakePurgomalumClient(); - productService = new ProductService(productRepository, menuRepository, purgomalumClient); + checkBadWordClient = new FakeCheckBadWordClient(); + productService = new ProductService(productRepository, menuRepository, checkBadWordClient); } @DisplayName("상품을 등록할 수 있다.") From fbe238c5bc3623ee5768ee84885ae0c3fb623b0a Mon Sep 17 00:00:00 2001 From: seovalue Date: Sat, 15 Feb 2025 10:38:58 +0900 Subject: [PATCH 4/9] Rename .java to .kt --- ...nMemoryProductRepository.java => InMemoryProductRepository.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/test/java/kitchenpos/products/application/{InMemoryProductRepository.java => InMemoryProductRepository.kt} (100%) diff --git a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.kt similarity index 100% rename from src/test/java/kitchenpos/products/application/InMemoryProductRepository.java rename to src/test/java/kitchenpos/products/application/InMemoryProductRepository.kt From 6615134dd081c85638d334f7c6f637c3ba060ddc Mon Sep 17 00:00:00 2001 From: seovalue Date: Sat, 15 Feb 2025 10:38:58 +0900 Subject: [PATCH 5/9] refactor: Separate product creation into Request/Response, Product and ProductRecord --- .../menus/application/MenuService.java | 6 +- .../products/application/ProductService.java | 87 +++++++------ .../products/domain/JpaProductRepository.java | 8 -- .../kitchenpos/products/domain/Product.kt | 23 +++- .../products/domain/ProductJpaRepository.kt | 6 + .../kitchenpos/products/domain/ProductName.kt | 13 +- .../products/domain/ProductPrice.kt | 2 +- .../products/domain/ProductRecord.java | 18 +-- .../products/domain/ProductRepository.java | 8 +- .../products/domain/ProductRepositoryImpl.kt | 41 +++++++ ...ordClient.java => CheckBadWordClient.java} | 2 +- .../infra/DefaultCheckBadWordClient.java | 2 +- .../products/ui/ProductRestController.java | 12 +- .../ui/request/CreateProductRequest.kt | 8 ++ .../products/ui/response/ProductResponse.kt | 20 +++ src/test/java/kitchenpos/Fixtures.java | 19 +-- .../menus/application/MenuServiceTest.java | 114 +++++++++--------- .../application/FakeCheckBadWordClient.java | 6 +- .../application/InMemoryProductRepository.kt | 43 +++---- .../application/ProductServiceTest.java | 29 +++-- 20 files changed, 274 insertions(+), 193 deletions(-) delete mode 100644 src/main/java/kitchenpos/products/domain/JpaProductRepository.java create mode 100644 src/main/java/kitchenpos/products/domain/ProductJpaRepository.kt create mode 100644 src/main/java/kitchenpos/products/domain/ProductRepositoryImpl.kt rename src/main/java/kitchenpos/products/infra/{checkBadWordClient.java => CheckBadWordClient.java} (68%) create mode 100644 src/main/java/kitchenpos/products/ui/request/CreateProductRequest.kt create mode 100644 src/main/java/kitchenpos/products/ui/response/ProductResponse.kt diff --git a/src/main/java/kitchenpos/menus/application/MenuService.java b/src/main/java/kitchenpos/menus/application/MenuService.java index ff18bf673..93252f319 100644 --- a/src/main/java/kitchenpos/menus/application/MenuService.java +++ b/src/main/java/kitchenpos/menus/application/MenuService.java @@ -7,7 +7,7 @@ import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.checkBadWordClient; +import kitchenpos.products.infra.CheckBadWordClient; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -23,13 +23,13 @@ public class MenuService { private final MenuRepository menuRepository; private final MenuGroupRepository menuGroupRepository; private final ProductRepository productRepository; - private final checkBadWordClient checkBadWordClient; + private final CheckBadWordClient checkBadWordClient; public MenuService( final MenuRepository menuRepository, final MenuGroupRepository menuGroupRepository, final ProductRepository productRepository, - final checkBadWordClient checkBadWordClient + final CheckBadWordClient checkBadWordClient ) { this.menuRepository = menuRepository; this.menuGroupRepository = menuGroupRepository; diff --git a/src/main/java/kitchenpos/products/application/ProductService.java b/src/main/java/kitchenpos/products/application/ProductService.java index 30e433f2b..54e1d9440 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/products/application/ProductService.java @@ -1,11 +1,14 @@ package kitchenpos.products.application; import kitchenpos.menus.domain.Menu; -import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.domain.Product; +import kitchenpos.products.domain.ProductName; +import kitchenpos.products.domain.ProductPrice; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.checkBadWordClient; +import kitchenpos.products.infra.CheckBadWordClient; +import kitchenpos.products.ui.request.CreateProductRequest; +import kitchenpos.products.ui.response.ProductResponse; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -19,12 +22,12 @@ public class ProductService { private final ProductRepository productRepository; private final MenuRepository menuRepository; - private final checkBadWordClient checkBadWordClient; + private final CheckBadWordClient checkBadWordClient; public ProductService( - final ProductRepository productRepository, - final MenuRepository menuRepository, - final checkBadWordClient checkBadWordClient + final ProductRepository productRepository, + final MenuRepository menuRepository, + final CheckBadWordClient checkBadWordClient ) { this.productRepository = productRepository; this.menuRepository = menuRepository; @@ -32,46 +35,56 @@ public ProductService( } @Transactional - public Product create(final Product request) { - final BigDecimal price = request.getPrice(); - if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) { - throw new IllegalArgumentException(); + public ProductResponse create(final CreateProductRequest request) { + if (checkBadWordClient.containsProfanity(request.getName())) { + throw new IllegalArgumentException( + String.format(""" + Product name contains profanity (name: %s) + """, request.getName()) + ); } - final String name = request.getName(); - if (Objects.isNull(name) || checkBadWordClient.containsProfanity(name)) { - throw new IllegalArgumentException(); - } - final Product product = new Product(); - product.setId(UUID.randomUUID()); - product.setName(name); - product.setPrice(price); - return productRepository.save(product); + final Product product = Product.Companion.create(request.getName(), request.getPrice()); + final Product savedProduct = productRepository.save(product); + return ProductResponse.of(savedProduct); } @Transactional public Product changePrice(final UUID productId, final Product request) { - final BigDecimal price = request.getPrice(); - if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) { - throw new IllegalArgumentException(); + final BigDecimal newPrice = request.getPrice(); + if (Objects.isNull(newPrice) || newPrice.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Price must be greater than or equal to zero"); } - final Product product = productRepository.findById(productId) - .orElseThrow(NoSuchElementException::new); - product.setPrice(price); + + final Product oldProduct = productRepository.findById(productId) + .orElseThrow(() -> new NoSuchElementException("Product not found")); + + // Create new Product instance with updated price + final Product updatedProduct = new Product( + oldProduct.getId(), + ProductName.Companion.create(oldProduct.getName()), + ProductPrice.Companion.create(newPrice) + ); + final List menus = menuRepository.findAllByProductId(productId); for (final Menu menu : menus) { - BigDecimal sum = BigDecimal.ZERO; - for (final MenuProduct menuProduct : menu.getMenuProducts()) { - sum = sum.add( - menuProduct.getProduct() - .getPrice() - .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) - ); - } - if (menu.getPrice().compareTo(sum) > 0) { - menu.setDisplayed(false); - } + validateAndUpdateMenuDisplay(menu); + } + + return productRepository.save(updatedProduct); + } + + private void validateAndUpdateMenuDisplay(Menu menu) { + BigDecimal sum = calculateMenuTotalPrice(menu); + if (menu.getPrice().compareTo(sum) > 0) { + menu.setDisplayed(false); } - return product; + } + + private BigDecimal calculateMenuTotalPrice(Menu menu) { + return menu.getMenuProducts().stream() + .map(menuProduct -> menuProduct.getProduct().getPrice() + .multiply(BigDecimal.valueOf(menuProduct.getQuantity()))) + .reduce(BigDecimal.ZERO, BigDecimal::add); } @Transactional(readOnly = true) diff --git a/src/main/java/kitchenpos/products/domain/JpaProductRepository.java b/src/main/java/kitchenpos/products/domain/JpaProductRepository.java deleted file mode 100644 index 2ff9247ca..000000000 --- a/src/main/java/kitchenpos/products/domain/JpaProductRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package kitchenpos.products.domain; - -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.UUID; - -public interface JpaProductRepository extends ProductRepository, JpaRepository { -} diff --git a/src/main/java/kitchenpos/products/domain/Product.kt b/src/main/java/kitchenpos/products/domain/Product.kt index f2ae14041..e4a5c8727 100644 --- a/src/main/java/kitchenpos/products/domain/Product.kt +++ b/src/main/java/kitchenpos/products/domain/Product.kt @@ -1,9 +1,28 @@ package kitchenpos.products.domain +import java.math.BigDecimal import java.util.* data class Product( - private val id: UUID, + val id: UUID, private val name: ProductName, private val price: ProductPrice, -) +) { + fun getName(): String { + return name.name + } + + fun getPrice(): BigDecimal { + return price.price + } + + companion object { + fun create(name: String, price: BigDecimal): Product { + return Product( + UUID.randomUUID(), + ProductName.create(name), + ProductPrice.create(price) + ) + } + } +} diff --git a/src/main/java/kitchenpos/products/domain/ProductJpaRepository.kt b/src/main/java/kitchenpos/products/domain/ProductJpaRepository.kt new file mode 100644 index 000000000..dc33f7496 --- /dev/null +++ b/src/main/java/kitchenpos/products/domain/ProductJpaRepository.kt @@ -0,0 +1,6 @@ +package kitchenpos.products.domain + +import org.springframework.data.jpa.repository.JpaRepository +import java.util.* + +interface ProductJpaRepository : JpaRepository diff --git a/src/main/java/kitchenpos/products/domain/ProductName.kt b/src/main/java/kitchenpos/products/domain/ProductName.kt index cd7925061..ff49fda12 100644 --- a/src/main/java/kitchenpos/products/domain/ProductName.kt +++ b/src/main/java/kitchenpos/products/domain/ProductName.kt @@ -1,14 +1,11 @@ package kitchenpos.products.domain -import kitchenpos.products.infra.checkBadWordClient - class ProductName private constructor( val name: String, ) { - companion object Factory { - fun create(value: String, checkBadWordClient: checkBadWordClient): ProductName { + companion object { + fun create(value: String): ProductName { validateNull(value) - validateProfanity(value, checkBadWordClient) return ProductName(value) } @@ -17,11 +14,5 @@ class ProductName private constructor( throw IllegalArgumentException("Invalid product name: $value") } } - - private fun validateProfanity(value: String, profanityCheckClient: checkBadWordClient) { - if (profanityCheckClient.containsProfanity(value)) { - throw IllegalArgumentException("Profanity is not allowed: $value") - } - } } } diff --git a/src/main/java/kitchenpos/products/domain/ProductPrice.kt b/src/main/java/kitchenpos/products/domain/ProductPrice.kt index 282fea72e..ee514eb60 100644 --- a/src/main/java/kitchenpos/products/domain/ProductPrice.kt +++ b/src/main/java/kitchenpos/products/domain/ProductPrice.kt @@ -5,7 +5,7 @@ import java.math.BigDecimal class ProductPrice private constructor( val price: BigDecimal ) { - companion object Factory { + companion object { fun create(value: BigDecimal): ProductPrice { validateNull(value) validatePositive(value) diff --git a/src/main/java/kitchenpos/products/domain/ProductRecord.java b/src/main/java/kitchenpos/products/domain/ProductRecord.java index 5e105ecf5..c9ff80d94 100644 --- a/src/main/java/kitchenpos/products/domain/ProductRecord.java +++ b/src/main/java/kitchenpos/products/domain/ProductRecord.java @@ -24,27 +24,21 @@ public class ProductRecord { public ProductRecord() { } - public UUID getId() { - return id; + public ProductRecord(UUID id, String name, BigDecimal price) { + this.id = id; + this.name = name; + this.price = price; } - public void setId(final UUID id) { - this.id = id; + public UUID getId() { + return id; } public String getName() { return name; } - public void setName(final String name) { - this.name = name; - } - public BigDecimal getPrice() { return price; } - - public void setPrice(final BigDecimal price) { - this.price = price; - } } diff --git a/src/main/java/kitchenpos/products/domain/ProductRepository.java b/src/main/java/kitchenpos/products/domain/ProductRepository.java index 8602f7aa5..ed30b2436 100644 --- a/src/main/java/kitchenpos/products/domain/ProductRepository.java +++ b/src/main/java/kitchenpos/products/domain/ProductRepository.java @@ -5,11 +5,11 @@ import java.util.UUID; public interface ProductRepository { - ProductRecord save(ProductRecord product); + Product save(Product product); - Optional findById(UUID id); + Optional findById(UUID id); - List findAll(); + List findAll(); - List findAllByIdIn(List ids); + List findAllByIdIn(List ids); } diff --git a/src/main/java/kitchenpos/products/domain/ProductRepositoryImpl.kt b/src/main/java/kitchenpos/products/domain/ProductRepositoryImpl.kt new file mode 100644 index 000000000..523fcc076 --- /dev/null +++ b/src/main/java/kitchenpos/products/domain/ProductRepositoryImpl.kt @@ -0,0 +1,41 @@ +package kitchenpos.products.domain + +import org.springframework.stereotype.Repository +import java.util.* + +@Repository +class ProductRepositoryImpl( + private val productJpaRepository: ProductJpaRepository +) : ProductRepository { + override fun save(product: Product): Product { + return productJpaRepository.save(product.toRecord()).toProduct() + } + + override fun findById(id: UUID?): Optional { + TODO("Not yet implemented") + } + + override fun findAll(): MutableList { + TODO("Not yet implemented") + } + + override fun findAllByIdIn(ids: MutableList?): MutableList { + TODO("Not yet implemented") + } +} + +fun Product.toRecord(): ProductRecord { + return ProductRecord( + id, + getName(), + getPrice(), + ) +} + +fun ProductRecord.toProduct(): Product { + return Product( + id = id, + name = ProductName.create(name), + price = ProductPrice.create(price) + ) +} diff --git a/src/main/java/kitchenpos/products/infra/checkBadWordClient.java b/src/main/java/kitchenpos/products/infra/CheckBadWordClient.java similarity index 68% rename from src/main/java/kitchenpos/products/infra/checkBadWordClient.java rename to src/main/java/kitchenpos/products/infra/CheckBadWordClient.java index f146b4523..1930a52b1 100644 --- a/src/main/java/kitchenpos/products/infra/checkBadWordClient.java +++ b/src/main/java/kitchenpos/products/infra/CheckBadWordClient.java @@ -1,5 +1,5 @@ package kitchenpos.products.infra; -public interface checkBadWordClient { +public interface CheckBadWordClient { boolean containsProfanity(String text); } diff --git a/src/main/java/kitchenpos/products/infra/DefaultCheckBadWordClient.java b/src/main/java/kitchenpos/products/infra/DefaultCheckBadWordClient.java index 09f26ab72..b7261d343 100644 --- a/src/main/java/kitchenpos/products/infra/DefaultCheckBadWordClient.java +++ b/src/main/java/kitchenpos/products/infra/DefaultCheckBadWordClient.java @@ -8,7 +8,7 @@ import java.net.URI; @Component -public class DefaultCheckBadWordClient implements checkBadWordClient { +public class DefaultCheckBadWordClient implements CheckBadWordClient { private final RestTemplate restTemplate; public DefaultCheckBadWordClient(final RestTemplateBuilder restTemplateBuilder) { diff --git a/src/main/java/kitchenpos/products/ui/ProductRestController.java b/src/main/java/kitchenpos/products/ui/ProductRestController.java index 24c5cc311..87cf175f5 100644 --- a/src/main/java/kitchenpos/products/ui/ProductRestController.java +++ b/src/main/java/kitchenpos/products/ui/ProductRestController.java @@ -1,7 +1,11 @@ package kitchenpos.products.ui; import kitchenpos.products.application.ProductService; +import kitchenpos.products.domain.Product; +import kitchenpos.products.domain.ProductName; import kitchenpos.products.domain.ProductRecord; +import kitchenpos.products.ui.request.CreateProductRequest; +import kitchenpos.products.ui.response.ProductResponse; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -25,19 +29,19 @@ public ProductRestController(final ProductService productService) { } @PostMapping - public ResponseEntity create(@RequestBody final ProductRecord request) { - final ProductRecord response = productService.create(request); + public ResponseEntity create(@RequestBody final CreateProductRequest request) { + final ProductResponse response = productService.create(request); return ResponseEntity.created(URI.create("/api/products/" + response.getId())) .body(response); } @PutMapping("/{productId}/price") - public ResponseEntity changePrice(@PathVariable final UUID productId, @RequestBody final ProductRecord request) { + public ResponseEntity changePrice(@PathVariable final UUID productId, @RequestBody final ProductRecord request) { return ResponseEntity.ok(productService.changePrice(productId, request)); } @GetMapping - public ResponseEntity> findAll() { + public ResponseEntity> findAll() { return ResponseEntity.ok(productService.findAll()); } } diff --git a/src/main/java/kitchenpos/products/ui/request/CreateProductRequest.kt b/src/main/java/kitchenpos/products/ui/request/CreateProductRequest.kt new file mode 100644 index 000000000..84d1a6e09 --- /dev/null +++ b/src/main/java/kitchenpos/products/ui/request/CreateProductRequest.kt @@ -0,0 +1,8 @@ +package kitchenpos.products.ui.request + +import java.math.BigDecimal + +data class CreateProductRequest( + val name: String, + val price: BigDecimal, +) diff --git a/src/main/java/kitchenpos/products/ui/response/ProductResponse.kt b/src/main/java/kitchenpos/products/ui/response/ProductResponse.kt new file mode 100644 index 000000000..ad48135c9 --- /dev/null +++ b/src/main/java/kitchenpos/products/ui/response/ProductResponse.kt @@ -0,0 +1,20 @@ +package kitchenpos.products.ui.response + +import kitchenpos.products.domain.Product + +data class ProductResponse( + val id: String, + val name: String, + val price: String, +) { + companion object { + @JvmStatic + fun of(product: Product): ProductResponse { + return ProductResponse( + id = product.id.toString(), + name = product.getName(), + price = product.getPrice().toString() + ) + } + } +} diff --git a/src/test/java/kitchenpos/Fixtures.java b/src/test/java/kitchenpos/Fixtures.java index d54a70a32..6cf005a6a 100644 --- a/src/test/java/kitchenpos/Fixtures.java +++ b/src/test/java/kitchenpos/Fixtures.java @@ -8,6 +8,7 @@ import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuGroup; import kitchenpos.menus.domain.MenuProduct; +import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRecord; import java.math.BigDecimal; @@ -52,7 +53,7 @@ public static MenuGroup menuGroup(final String name) { public static MenuProduct menuProduct() { final MenuProduct menuProduct = new MenuProduct(); menuProduct.setSeq(new Random().nextLong()); - menuProduct.setProduct(product()); + menuProduct.setProduct(productRecord()); menuProduct.setQuantity(2L); return menuProduct; } @@ -117,15 +118,17 @@ public static OrderTable orderTable(final boolean occupied, final int numberOfGu return orderTable; } - public static ProductRecord product() { + public static Product product() { return product("후라이드", 16_000L); } - public static ProductRecord product(final String name, final long price) { - final ProductRecord product = new ProductRecord(); - product.setId(UUID.randomUUID()); - product.setName(name); - product.setPrice(BigDecimal.valueOf(price)); - return product; + public static ProductRecord productRecord() { + return new ProductRecord(UUID.randomUUID(), "후라이드", BigDecimal.valueOf(16_000L)); + } + + public static Product product(final String name, final long price) { + return Product.Companion.create( + name, BigDecimal.valueOf(price) + ); } } diff --git a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java index ad3590053..2ced35e89 100644 --- a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java +++ b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java @@ -6,9 +6,10 @@ import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.application.FakeCheckBadWordClient; import kitchenpos.products.application.InMemoryProductRepository; +import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.checkBadWordClient; +import kitchenpos.products.infra.CheckBadWordClient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -38,10 +39,10 @@ class MenuServiceTest { private MenuRepository menuRepository; private MenuGroupRepository menuGroupRepository; private ProductRepository productRepository; - private checkBadWordClient checkBadWordClient; + private CheckBadWordClient checkBadWordClient; private MenuService menuService; private UUID menuGroupId; - private ProductRecord product; + private ProductRecord productRecord; @BeforeEach void setUp() { @@ -51,24 +52,27 @@ void setUp() { checkBadWordClient = new FakeCheckBadWordClient(); menuService = new MenuService(menuRepository, menuGroupRepository, productRepository, checkBadWordClient); menuGroupId = menuGroupRepository.save(menuGroup()).getId(); - product = productRepository.save(product("후라이드", 16_000L)); + final Product product = productRepository.save(product("후라이드", 16_000L)); + productRecord = new ProductRecord( + product.getId(), product.getName(), product.getPrice() + ); } @DisplayName("1개 이상의 등록된 상품으로 메뉴를 등록할 수 있다.") @Test void create() { final Menu expected = createMenuRequest( - "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) + "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(productRecord.getId(), 2L) ); final Menu actual = menuService.create(expected); assertThat(actual).isNotNull(); assertAll( - () -> assertThat(actual.getId()).isNotNull(), - () -> assertThat(actual.getName()).isEqualTo(expected.getName()), - () -> assertThat(actual.getPrice()).isEqualTo(expected.getPrice()), - () -> assertThat(actual.getMenuGroup().getId()).isEqualTo(expected.getMenuGroupId()), - () -> assertThat(actual.isDisplayed()).isEqualTo(expected.isDisplayed()), - () -> assertThat(actual.getMenuProducts()).hasSize(1) + () -> assertThat(actual.getId()).isNotNull(), + () -> assertThat(actual.getName()).isEqualTo(expected.getName()), + () -> assertThat(actual.getPrice()).isEqualTo(expected.getPrice()), + () -> assertThat(actual.getMenuGroup().getId()).isEqualTo(expected.getMenuGroupId()), + () -> assertThat(actual.isDisplayed()).isEqualTo(expected.isDisplayed()), + () -> assertThat(actual.getMenuProducts()).hasSize(1) ); } @@ -78,14 +82,14 @@ void create() { void create(final List menuProducts) { final Menu expected = createMenuRequest("후라이드+후라이드", 19_000L, menuGroupId, true, menuProducts); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } private static List menuProducts() { return Arrays.asList( - null, - Arguments.of(Collections.emptyList()), - Arguments.of(Arrays.asList(createMenuProductRequest(INVALID_ID, 2L))) + null, + Arguments.of(Collections.emptyList()), + Arguments.of(Arrays.asList(createMenuProductRequest(INVALID_ID, 2L))) ); } @@ -93,10 +97,10 @@ private static List menuProducts() { @Test void createNegativeQuantity() { final Menu expected = createMenuRequest( - "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), -1L) + "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(productRecord.getId(), -1L) ); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴의 가격이 올바르지 않으면 등록할 수 없다.") @@ -105,20 +109,20 @@ void createNegativeQuantity() { @ParameterizedTest void create(final BigDecimal price) { final Menu expected = createMenuRequest( - "후라이드+후라이드", price, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) + "후라이드+후라이드", price, menuGroupId, true, createMenuProductRequest(productRecord.getId(), 2L) ); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴에 속한 상품 금액의 합은 메뉴의 가격보다 크거나 같아야 한다.") @Test void createExpensiveMenu() { final Menu expected = createMenuRequest( - "후라이드+후라이드", 33_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) + "후라이드+후라이드", 33_000L, menuGroupId, true, createMenuProductRequest(productRecord.getId(), 2L) ); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴는 특정 메뉴 그룹에 속해야 한다.") @@ -126,10 +130,10 @@ void createExpensiveMenu() { @ParameterizedTest void create(final UUID menuGroupId) { final Menu expected = createMenuRequest( - "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) + "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(productRecord.getId(), 2L) ); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(NoSuchElementException.class); + .isInstanceOf(NoSuchElementException.class); } @DisplayName("메뉴의 이름이 올바르지 않으면 등록할 수 없다.") @@ -138,16 +142,16 @@ void create(final UUID menuGroupId) { @ParameterizedTest void create(final String name) { final Menu expected = createMenuRequest( - name, 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) + name, 19_000L, menuGroupId, true, createMenuProductRequest(productRecord.getId(), 2L) ); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴의 가격을 변경할 수 있다.") @Test void changePrice() { - final UUID menuId = menuRepository.save(menu(19_000L, menuProduct(product, 2L))).getId(); + final UUID menuId = menuRepository.save(menu(19_000L, menuProduct(productRecord, 2L))).getId(); final Menu expected = changePriceRequest(16_000L); final Menu actual = menuService.changePrice(menuId, expected); assertThat(actual.getPrice()).isEqualTo(expected.getPrice()); @@ -158,25 +162,25 @@ void changePrice() { @NullSource @ParameterizedTest void changePrice(final BigDecimal price) { - final UUID menuId = menuRepository.save(menu(19_000L, menuProduct(product, 2L))).getId(); + final UUID menuId = menuRepository.save(menu(19_000L, menuProduct(productRecord, 2L))).getId(); final Menu expected = changePriceRequest(price); assertThatThrownBy(() -> menuService.changePrice(menuId, expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴에 속한 상품 금액의 합은 메뉴의 가격보다 크거나 같아야 한다.") @Test void changePriceToExpensive() { - final UUID menuId = menuRepository.save(menu(19_000L, menuProduct(product, 2L))).getId(); + final UUID menuId = menuRepository.save(menu(19_000L, menuProduct(productRecord, 2L))).getId(); final Menu expected = changePriceRequest(33_000L); assertThatThrownBy(() -> menuService.changePrice(menuId, expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴를 노출할 수 있다.") @Test void display() { - final UUID menuId = menuRepository.save(menu(19_000L, false, menuProduct(product, 2L))).getId(); + final UUID menuId = menuRepository.save(menu(19_000L, false, menuProduct(productRecord, 2L))).getId(); final Menu actual = menuService.display(menuId); assertThat(actual.isDisplayed()).isTrue(); } @@ -184,15 +188,15 @@ void display() { @DisplayName("메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 높을 경우 메뉴를 노출할 수 없다.") @Test void displayExpensiveMenu() { - final UUID menuId = menuRepository.save(menu(33_000L, false, menuProduct(product, 2L))).getId(); + final UUID menuId = menuRepository.save(menu(33_000L, false, menuProduct(productRecord, 2L))).getId(); assertThatThrownBy(() -> menuService.display(menuId)) - .isInstanceOf(IllegalStateException.class); + .isInstanceOf(IllegalStateException.class); } @DisplayName("메뉴를 숨길 수 있다.") @Test void hide() { - final UUID menuId = menuRepository.save(menu(19_000L, true, menuProduct(product, 2L))).getId(); + final UUID menuId = menuRepository.save(menu(19_000L, true, menuProduct(productRecord, 2L))).getId(); final Menu actual = menuService.hide(menuId); assertThat(actual.isDisplayed()).isFalse(); } @@ -200,47 +204,47 @@ void hide() { @DisplayName("메뉴의 목록을 조회할 수 있다.") @Test void findAll() { - menuRepository.save(menu(19_000L, true, menuProduct(product, 2L))); + menuRepository.save(menu(19_000L, true, menuProduct(productRecord, 2L))); final List actual = menuService.findAll(); assertThat(actual).hasSize(1); } private Menu createMenuRequest( - final String name, - final long price, - final UUID menuGroupId, - final boolean displayed, - final MenuProduct... menuProducts + final String name, + final long price, + final UUID menuGroupId, + final boolean displayed, + final MenuProduct... menuProducts ) { return createMenuRequest(name, BigDecimal.valueOf(price), menuGroupId, displayed, menuProducts); } private Menu createMenuRequest( - final String name, - final BigDecimal price, - final UUID menuGroupId, - final boolean displayed, - final MenuProduct... menuProducts + final String name, + final BigDecimal price, + final UUID menuGroupId, + final boolean displayed, + final MenuProduct... menuProducts ) { return createMenuRequest(name, price, menuGroupId, displayed, Arrays.asList(menuProducts)); } private Menu createMenuRequest( - final String name, - final long price, - final UUID menuGroupId, - final boolean displayed, - final List menuProducts + final String name, + final long price, + final UUID menuGroupId, + final boolean displayed, + final List menuProducts ) { return createMenuRequest(name, BigDecimal.valueOf(price), menuGroupId, displayed, menuProducts); } private Menu createMenuRequest( - final String name, - final BigDecimal price, - final UUID menuGroupId, - final boolean displayed, - final List menuProducts + final String name, + final BigDecimal price, + final UUID menuGroupId, + final boolean displayed, + final List menuProducts ) { final Menu menu = new Menu(); menu.setName(name); diff --git a/src/test/java/kitchenpos/products/application/FakeCheckBadWordClient.java b/src/test/java/kitchenpos/products/application/FakeCheckBadWordClient.java index a68cac172..0f166fd75 100644 --- a/src/test/java/kitchenpos/products/application/FakeCheckBadWordClient.java +++ b/src/test/java/kitchenpos/products/application/FakeCheckBadWordClient.java @@ -1,11 +1,11 @@ package kitchenpos.products.application; -import kitchenpos.products.infra.checkBadWordClient; +import kitchenpos.products.infra.CheckBadWordClient; import java.util.Arrays; import java.util.List; -public class FakeCheckBadWordClient implements checkBadWordClient { +public class FakeCheckBadWordClient implements CheckBadWordClient { private static final List profanities; static { @@ -15,6 +15,6 @@ public class FakeCheckBadWordClient implements checkBadWordClient { @Override public boolean containsProfanity(final String text) { return profanities.stream() - .anyMatch(profanity -> text.contains(profanity)); + .anyMatch(text::contains); } } diff --git a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.kt b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.kt index 7bc4c22d5..4f7949ee2 100644 --- a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.kt +++ b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.kt @@ -1,39 +1,26 @@ -package kitchenpos.products.application; +package kitchenpos.products.application -import kitchenpos.products.domain.ProductRecord; -import kitchenpos.products.domain.ProductRepository; +import kitchenpos.products.domain.Product +import kitchenpos.products.domain.ProductRepository +import java.util.* -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; +class InMemoryProductRepository : ProductRepository { + private val products: MutableMap = mutableMapOf() -public class InMemoryProductRepository implements ProductRepository { - private final Map products = new HashMap<>(); - - @Override - public ProductRecord save(final ProductRecord product) { - products.put(product.getId(), product); - return product; + override fun save(product: Product): Product { + products[product.id] = product + return product } - @Override - public Optional findById(final UUID id) { - return Optional.ofNullable(products.get(id)); + override fun findById(id: UUID?): Optional { + return Optional.ofNullable(products[id]) } - @Override - public List findAll() { - return new ArrayList<>(products.values()); + override fun findAll(): List { + return products.values.toList() } - @Override - public List findAllByIdIn(final List ids) { - return products.values() - .stream() - .filter(product -> ids.contains(product.getId())) - .toList(); + override fun findAllByIdIn(ids: MutableList?): MutableList { + return products.filterKeys { ids?.contains(it) ?: false }.values.toMutableList() } } diff --git a/src/test/java/kitchenpos/products/application/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/ProductServiceTest.java index 9b642ba52..2650fc0e3 100644 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/application/ProductServiceTest.java @@ -3,9 +3,12 @@ import kitchenpos.menus.application.InMemoryMenuRepository; import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuRepository; +import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.checkBadWordClient; +import kitchenpos.products.infra.CheckBadWordClient; +import kitchenpos.products.ui.request.CreateProductRequest; +import kitchenpos.products.ui.response.ProductResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -27,22 +30,21 @@ class ProductServiceTest { private ProductRepository productRepository; private MenuRepository menuRepository; - private checkBadWordClient checkBadWordClient; private ProductService productService; @BeforeEach void setUp() { productRepository = new InMemoryProductRepository(); menuRepository = new InMemoryMenuRepository(); - checkBadWordClient = new FakeCheckBadWordClient(); + CheckBadWordClient checkBadWordClient = new FakeCheckBadWordClient(); productService = new ProductService(productRepository, menuRepository, checkBadWordClient); } @DisplayName("상품을 등록할 수 있다.") @Test void create() { - final ProductRecord expected = createProductRequest("후라이드", 16_000L); - final ProductRecord actual = productService.create(expected); + final CreateProductRequest expected = createProductRequest("후라이드", 16_000L); + final ProductResponse actual = productService.create(expected); assertThat(actual).isNotNull(); assertAll( () -> assertThat(actual.getId()).isNotNull(), @@ -56,7 +58,7 @@ void create() { @NullSource @ParameterizedTest void create(final BigDecimal price) { - final ProductRecord expected = createProductRequest("후라이드", price); + final CreateProductRequest expected = createProductRequest("후라이드", price); assertThatThrownBy(() -> productService.create(expected)) .isInstanceOf(IllegalArgumentException.class); } @@ -66,7 +68,7 @@ void create(final BigDecimal price) { @NullSource @ParameterizedTest void create(final String name) { - final ProductRecord expected = createProductRequest(name, 16_000L); + final CreateProductRequest expected = createProductRequest(name, 16_000L); assertThatThrownBy(() -> productService.create(expected)) .isInstanceOf(IllegalArgumentException.class); } @@ -94,7 +96,7 @@ void changePrice(final BigDecimal price) { @DisplayName("상품의 가격이 변경될 때 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 크면 메뉴가 숨겨진다.") @Test void changePriceInMenu() { - final ProductRecord product = productRepository.save(product("후라이드", 16_000L)); + final Product product = productRepository.save(product("후라이드", 16_000L)); final Menu menu = menuRepository.save(menu(19_000L, true, menuProduct(product, 2L))); productService.changePrice(product.getId(), changePriceRequest(8_000L)); assertThat(menuRepository.findById(menu.getId()).get().isDisplayed()).isFalse(); @@ -105,19 +107,16 @@ void changePriceInMenu() { void findAll() { productRepository.save(product("후라이드", 16_000L)); productRepository.save(product("양념치킨", 16_000L)); - final List actual = productService.findAll(); + final List actual = productService.findAll(); assertThat(actual).hasSize(2); } - private ProductRecord createProductRequest(final String name, final long price) { + private CreateProductRequest createProductRequest(final String name, final long price) { return createProductRequest(name, BigDecimal.valueOf(price)); } - private ProductRecord createProductRequest(final String name, final BigDecimal price) { - final ProductRecord product = new ProductRecord(); - product.setName(name); - product.setPrice(price); - return product; + private CreateProductRequest createProductRequest(final String name, final BigDecimal price) { + return new CreateProductRequest(name, price); } private ProductRecord changePriceRequest(final long price) { From 90eff4e9bd025bd5c7d564dd279acf03881b8bec Mon Sep 17 00:00:00 2001 From: seovalue Date: Sat, 15 Feb 2025 10:44:11 +0900 Subject: [PATCH 6/9] refactor: Update product price change logic --- .../products/application/ProductService.java | 14 +++----- .../products/ui/ProductRestController.java | 3 +- .../products/ui/request/ChangePriceRequest.kt | 7 ++++ .../application/ProductServiceTest.java | 32 ++++++++++--------- 4 files changed, 31 insertions(+), 25 deletions(-) create mode 100644 src/main/java/kitchenpos/products/ui/request/ChangePriceRequest.kt diff --git a/src/main/java/kitchenpos/products/application/ProductService.java b/src/main/java/kitchenpos/products/application/ProductService.java index 54e1d9440..311d9ffaf 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/products/application/ProductService.java @@ -7,6 +7,7 @@ import kitchenpos.products.domain.ProductPrice; import kitchenpos.products.domain.ProductRepository; import kitchenpos.products.infra.CheckBadWordClient; +import kitchenpos.products.ui.request.ChangePriceRequest; import kitchenpos.products.ui.request.CreateProductRequest; import kitchenpos.products.ui.response.ProductResponse; import org.springframework.stereotype.Service; @@ -15,7 +16,6 @@ import java.math.BigDecimal; import java.util.List; import java.util.NoSuchElementException; -import java.util.Objects; import java.util.UUID; @Service @@ -49,12 +49,7 @@ Product name contains profanity (name: %s) } @Transactional - public Product changePrice(final UUID productId, final Product request) { - final BigDecimal newPrice = request.getPrice(); - if (Objects.isNull(newPrice) || newPrice.compareTo(BigDecimal.ZERO) < 0) { - throw new IllegalArgumentException("Price must be greater than or equal to zero"); - } - + public ProductResponse changePrice(final UUID productId, final ChangePriceRequest request) { final Product oldProduct = productRepository.findById(productId) .orElseThrow(() -> new NoSuchElementException("Product not found")); @@ -62,7 +57,7 @@ public Product changePrice(final UUID productId, final Product request) { final Product updatedProduct = new Product( oldProduct.getId(), ProductName.Companion.create(oldProduct.getName()), - ProductPrice.Companion.create(newPrice) + ProductPrice.Companion.create(request.getPrice()) ); final List menus = menuRepository.findAllByProductId(productId); @@ -70,7 +65,8 @@ public Product changePrice(final UUID productId, final Product request) { validateAndUpdateMenuDisplay(menu); } - return productRepository.save(updatedProduct); + final Product product = productRepository.save(updatedProduct); + return ProductResponse.of(product); } private void validateAndUpdateMenuDisplay(Menu menu) { diff --git a/src/main/java/kitchenpos/products/ui/ProductRestController.java b/src/main/java/kitchenpos/products/ui/ProductRestController.java index 87cf175f5..fced0b064 100644 --- a/src/main/java/kitchenpos/products/ui/ProductRestController.java +++ b/src/main/java/kitchenpos/products/ui/ProductRestController.java @@ -4,6 +4,7 @@ import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductName; import kitchenpos.products.domain.ProductRecord; +import kitchenpos.products.ui.request.ChangePriceRequest; import kitchenpos.products.ui.request.CreateProductRequest; import kitchenpos.products.ui.response.ProductResponse; import org.springframework.http.ResponseEntity; @@ -36,7 +37,7 @@ public ResponseEntity create(@RequestBody final CreateProductRe } @PutMapping("/{productId}/price") - public ResponseEntity changePrice(@PathVariable final UUID productId, @RequestBody final ProductRecord request) { + public ResponseEntity changePrice(@PathVariable final UUID productId, @RequestBody final ChangePriceRequest request) { return ResponseEntity.ok(productService.changePrice(productId, request)); } diff --git a/src/main/java/kitchenpos/products/ui/request/ChangePriceRequest.kt b/src/main/java/kitchenpos/products/ui/request/ChangePriceRequest.kt new file mode 100644 index 000000000..edca4c6d6 --- /dev/null +++ b/src/main/java/kitchenpos/products/ui/request/ChangePriceRequest.kt @@ -0,0 +1,7 @@ +package kitchenpos.products.ui.request + +import java.math.BigDecimal + +data class ChangePriceRequest( + val price: BigDecimal +) diff --git a/src/test/java/kitchenpos/products/application/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/ProductServiceTest.java index 2650fc0e3..609d672ea 100644 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/application/ProductServiceTest.java @@ -2,11 +2,13 @@ import kitchenpos.menus.application.InMemoryMenuRepository; import kitchenpos.menus.domain.Menu; +import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; import kitchenpos.products.infra.CheckBadWordClient; +import kitchenpos.products.ui.request.ChangePriceRequest; import kitchenpos.products.ui.request.CreateProductRequest; import kitchenpos.products.ui.response.ProductResponse; import org.junit.jupiter.api.BeforeEach; @@ -47,9 +49,9 @@ void create() { final ProductResponse actual = productService.create(expected); assertThat(actual).isNotNull(); assertAll( - () -> assertThat(actual.getId()).isNotNull(), - () -> assertThat(actual.getName()).isEqualTo(expected.getName()), - () -> assertThat(actual.getPrice()).isEqualTo(expected.getPrice()) + () -> assertThat(actual.getId()).isNotNull(), + () -> assertThat(actual.getName()).isEqualTo(expected.getName()), + () -> assertThat(actual.getPrice()).isEqualTo(expected.getPrice()) ); } @@ -60,7 +62,7 @@ void create() { void create(final BigDecimal price) { final CreateProductRequest expected = createProductRequest("후라이드", price); assertThatThrownBy(() -> productService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("상품의 이름이 올바르지 않으면 등록할 수 없다.") @@ -70,15 +72,15 @@ void create(final BigDecimal price) { void create(final String name) { final CreateProductRequest expected = createProductRequest(name, 16_000L); assertThatThrownBy(() -> productService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("상품의 가격을 변경할 수 있다.") @Test void changePrice() { final UUID productId = productRepository.save(product("후라이드", 16_000L)).getId(); - final ProductRecord expected = changePriceRequest(15_000L); - final ProductRecord actual = productService.changePrice(productId, expected); + final ChangePriceRequest expected = changePriceRequest(15_000L); + final ProductResponse actual = productService.changePrice(productId, expected); assertThat(actual.getPrice()).isEqualTo(expected.getPrice()); } @@ -88,16 +90,18 @@ void changePrice() { @ParameterizedTest void changePrice(final BigDecimal price) { final UUID productId = productRepository.save(product("후라이드", 16_000L)).getId(); - final ProductRecord expected = changePriceRequest(price); + final ChangePriceRequest expected = changePriceRequest(price); assertThatThrownBy(() -> productService.changePrice(productId, expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("상품의 가격이 변경될 때 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 크면 메뉴가 숨겨진다.") @Test void changePriceInMenu() { final Product product = productRepository.save(product("후라이드", 16_000L)); - final Menu menu = menuRepository.save(menu(19_000L, true, menuProduct(product, 2L))); + // TODO, Change to use Product instead of ProductRecord + final MenuProduct menuProduct = menuProduct(new ProductRecord(product.getId(), product.getName(), product.getPrice()), 2L); + final Menu menu = menuRepository.save(menu(19_000L, true, menuProduct)); productService.changePrice(product.getId(), changePriceRequest(8_000L)); assertThat(menuRepository.findById(menu.getId()).get().isDisplayed()).isFalse(); } @@ -119,13 +123,11 @@ private CreateProductRequest createProductRequest(final String name, final BigDe return new CreateProductRequest(name, price); } - private ProductRecord changePriceRequest(final long price) { + private ChangePriceRequest changePriceRequest(final long price) { return changePriceRequest(BigDecimal.valueOf(price)); } - private ProductRecord changePriceRequest(final BigDecimal price) { - final ProductRecord product = new ProductRecord(); - product.setPrice(price); - return product; + private ChangePriceRequest changePriceRequest(final BigDecimal price) { + return new ChangePriceRequest(price); } } From 00b70032cd59af7224cd8d8e89212401fc3c72a8 Mon Sep 17 00:00:00 2001 From: seovalue Date: Sat, 15 Feb 2025 10:45:25 +0900 Subject: [PATCH 7/9] refactor: Update read all products --- .../kitchenpos/products/application/ProductService.java | 7 +++++-- .../java/kitchenpos/products/ui/ProductRestController.java | 3 ++- .../products/application/ProductServiceTest.java | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/kitchenpos/products/application/ProductService.java b/src/main/java/kitchenpos/products/application/ProductService.java index 311d9ffaf..86d9773a1 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/products/application/ProductService.java @@ -84,7 +84,10 @@ private BigDecimal calculateMenuTotalPrice(Menu menu) { } @Transactional(readOnly = true) - public List findAll() { - return productRepository.findAll(); + public List findAll() { + final List products = productRepository.findAll(); + return products.stream() + .map(ProductResponse::of) + .toList(); } } diff --git a/src/main/java/kitchenpos/products/ui/ProductRestController.java b/src/main/java/kitchenpos/products/ui/ProductRestController.java index fced0b064..3e4f9ca09 100644 --- a/src/main/java/kitchenpos/products/ui/ProductRestController.java +++ b/src/main/java/kitchenpos/products/ui/ProductRestController.java @@ -43,6 +43,7 @@ public ResponseEntity changePrice(@PathVariable final UUID prod @GetMapping public ResponseEntity> findAll() { - return ResponseEntity.ok(productService.findAll()); + List products = productService.findAll(); + return ResponseEntity.ok(products); } } diff --git a/src/test/java/kitchenpos/products/application/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/ProductServiceTest.java index 609d672ea..0867a9d26 100644 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/application/ProductServiceTest.java @@ -111,7 +111,7 @@ void changePriceInMenu() { void findAll() { productRepository.save(product("후라이드", 16_000L)); productRepository.save(product("양념치킨", 16_000L)); - final List actual = productService.findAll(); + final List actual = productService.findAll(); assertThat(actual).hasSize(2); } From 4e513f1d83e32f3d73d018ad49fcba8c8ad119af Mon Sep 17 00:00:00 2001 From: seovalue Date: Sat, 15 Feb 2025 11:00:35 +0900 Subject: [PATCH 8/9] temp: temporary commits --- .../menus/application/MenuService.java | 50 +++++++++++-------- .../products/application/ProductService.java | 46 +++++++---------- .../kitchenpos/products/domain/Product.kt | 8 +++ .../products/ui/response/ProductResponse.kt | 13 ++--- .../application/ProductServiceTest.java | 3 -- 5 files changed, 62 insertions(+), 58 deletions(-) diff --git a/src/main/java/kitchenpos/menus/application/MenuService.java b/src/main/java/kitchenpos/menus/application/MenuService.java index 93252f319..34b682d46 100644 --- a/src/main/java/kitchenpos/menus/application/MenuService.java +++ b/src/main/java/kitchenpos/menus/application/MenuService.java @@ -5,6 +5,7 @@ import kitchenpos.menus.domain.MenuGroupRepository; import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; +import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; import kitchenpos.products.infra.CheckBadWordClient; @@ -26,10 +27,10 @@ public class MenuService { private final CheckBadWordClient checkBadWordClient; public MenuService( - final MenuRepository menuRepository, - final MenuGroupRepository menuGroupRepository, - final ProductRepository productRepository, - final CheckBadWordClient checkBadWordClient + final MenuRepository menuRepository, + final MenuGroupRepository menuGroupRepository, + final ProductRepository productRepository, + final CheckBadWordClient checkBadWordClient ) { this.menuRepository = menuRepository; this.menuGroupRepository = menuGroupRepository; @@ -44,16 +45,20 @@ public Menu create(final Menu request) { throw new IllegalArgumentException(); } final MenuGroup menuGroup = menuGroupRepository.findById(request.getMenuGroupId()) - .orElseThrow(NoSuchElementException::new); + .orElseThrow(NoSuchElementException::new); final List menuProductRequests = request.getMenuProducts(); if (Objects.isNull(menuProductRequests) || menuProductRequests.isEmpty()) { throw new IllegalArgumentException(); } - final List products = productRepository.findAllByIdIn( - menuProductRequests.stream() - .map(MenuProduct::getProductId) - .toList() + // TODO, remove this line + final List tempProducts = productRepository.findAllByIdIn( + menuProductRequests.stream() + .map(MenuProduct::getProductId) + .toList() ); + final List products = tempProducts.stream() + .map(product -> new ProductRecord(product.getId(), product.getName(), product.getPrice())) + .toList(); if (products.size() != menuProductRequests.size()) { throw new IllegalArgumentException(); } @@ -64,11 +69,12 @@ public Menu create(final Menu request) { if (quantity < 0) { throw new IllegalArgumentException(); } - final ProductRecord product = productRepository.findById(menuProductRequest.getProductId()) - .orElseThrow(NoSuchElementException::new); + final Product tempProduct = productRepository.findById(menuProductRequest.getProductId()) + .orElseThrow(NoSuchElementException::new); + final ProductRecord product = new ProductRecord(tempProduct.getId(), tempProduct.getName(), tempProduct.getPrice()); sum = sum.add( - product.getPrice() - .multiply(BigDecimal.valueOf(quantity)) + product.getPrice() + .multiply(BigDecimal.valueOf(quantity)) ); final MenuProduct menuProduct = new MenuProduct(); menuProduct.setProduct(product); @@ -99,13 +105,13 @@ public Menu changePrice(final UUID menuId, final Menu request) { throw new IllegalArgumentException(); } final Menu menu = menuRepository.findById(menuId) - .orElseThrow(NoSuchElementException::new); + .orElseThrow(NoSuchElementException::new); BigDecimal sum = BigDecimal.ZERO; for (final MenuProduct menuProduct : menu.getMenuProducts()) { sum = sum.add( - menuProduct.getProduct() - .getPrice() - .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) + menuProduct.getProduct() + .getPrice() + .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) ); } if (price.compareTo(sum) > 0) { @@ -118,13 +124,13 @@ public Menu changePrice(final UUID menuId, final Menu request) { @Transactional public Menu display(final UUID menuId) { final Menu menu = menuRepository.findById(menuId) - .orElseThrow(NoSuchElementException::new); + .orElseThrow(NoSuchElementException::new); BigDecimal sum = BigDecimal.ZERO; for (final MenuProduct menuProduct : menu.getMenuProducts()) { sum = sum.add( - menuProduct.getProduct() - .getPrice() - .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) + menuProduct.getProduct() + .getPrice() + .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) ); } if (menu.getPrice().compareTo(sum) > 0) { @@ -137,7 +143,7 @@ public Menu display(final UUID menuId) { @Transactional public Menu hide(final UUID menuId) { final Menu menu = menuRepository.findById(menuId) - .orElseThrow(NoSuchElementException::new); + .orElseThrow(NoSuchElementException::new); menu.setDisplayed(false); return menu; } diff --git a/src/main/java/kitchenpos/products/application/ProductService.java b/src/main/java/kitchenpos/products/application/ProductService.java index 86d9773a1..a2db6b3e4 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/products/application/ProductService.java @@ -1,6 +1,7 @@ package kitchenpos.products.application; import kitchenpos.menus.domain.Menu; +import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductName; @@ -16,6 +17,7 @@ import java.math.BigDecimal; import java.util.List; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.UUID; @Service @@ -50,38 +52,28 @@ Product name contains profanity (name: %s) @Transactional public ProductResponse changePrice(final UUID productId, final ChangePriceRequest request) { - final Product oldProduct = productRepository.findById(productId) - .orElseThrow(() -> new NoSuchElementException("Product not found")); + final Product product = productRepository.findById(productId) + .orElseThrow(NoSuchElementException::new); + final Product updatedProduct = productRepository.save(product.changePrice(request.getPrice())); - // Create new Product instance with updated price - final Product updatedProduct = new Product( - oldProduct.getId(), - ProductName.Companion.create(oldProduct.getName()), - ProductPrice.Companion.create(request.getPrice()) - ); - - final List menus = menuRepository.findAllByProductId(productId); + // TODO, 우선 Product 도메인만 수정 + final List menus = menuRepository.findAllByProductId(updatedProduct.getId()); for (final Menu menu : menus) { - validateAndUpdateMenuDisplay(menu); - } - - final Product product = productRepository.save(updatedProduct); - return ProductResponse.of(product); - } - - private void validateAndUpdateMenuDisplay(Menu menu) { - BigDecimal sum = calculateMenuTotalPrice(menu); - if (menu.getPrice().compareTo(sum) > 0) { - menu.setDisplayed(false); + BigDecimal sum = BigDecimal.ZERO; + for (final MenuProduct menuProduct : menu.getMenuProducts()) { + sum = sum.add( + menuProduct.getProduct() + .getPrice() + .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) + ); + } + if (menu.getPrice().compareTo(sum) > 0) { + menu.setDisplayed(false); + } } + return ProductResponse.of(productRepository.save(updatedProduct)); } - private BigDecimal calculateMenuTotalPrice(Menu menu) { - return menu.getMenuProducts().stream() - .map(menuProduct -> menuProduct.getProduct().getPrice() - .multiply(BigDecimal.valueOf(menuProduct.getQuantity()))) - .reduce(BigDecimal.ZERO, BigDecimal::add); - } @Transactional(readOnly = true) public List findAll() { diff --git a/src/main/java/kitchenpos/products/domain/Product.kt b/src/main/java/kitchenpos/products/domain/Product.kt index e4a5c8727..b77e27817 100644 --- a/src/main/java/kitchenpos/products/domain/Product.kt +++ b/src/main/java/kitchenpos/products/domain/Product.kt @@ -16,6 +16,14 @@ data class Product( return price.price } + fun changePrice(price: BigDecimal): Product { + return Product( + id, + name, + ProductPrice.create(price) + ) + } + companion object { fun create(name: String, price: BigDecimal): Product { return Product( diff --git a/src/main/java/kitchenpos/products/ui/response/ProductResponse.kt b/src/main/java/kitchenpos/products/ui/response/ProductResponse.kt index ad48135c9..343b0e375 100644 --- a/src/main/java/kitchenpos/products/ui/response/ProductResponse.kt +++ b/src/main/java/kitchenpos/products/ui/response/ProductResponse.kt @@ -1,20 +1,21 @@ package kitchenpos.products.ui.response import kitchenpos.products.domain.Product +import java.math.BigDecimal data class ProductResponse( val id: String, val name: String, - val price: String, + val price: BigDecimal, ) { companion object { @JvmStatic fun of(product: Product): ProductResponse { - return ProductResponse( - id = product.id.toString(), - name = product.getName(), - price = product.getPrice().toString() - ) + return ProductResponse( + id = product.id.toString(), + name = product.getName(), + price = product.getPrice() + ) } } } diff --git a/src/test/java/kitchenpos/products/application/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/ProductServiceTest.java index 0867a9d26..5d5470d65 100644 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/application/ProductServiceTest.java @@ -57,7 +57,6 @@ void create() { @DisplayName("상품의 가격이 올바르지 않으면 등록할 수 없다.") @ValueSource(strings = "-1000") - @NullSource @ParameterizedTest void create(final BigDecimal price) { final CreateProductRequest expected = createProductRequest("후라이드", price); @@ -67,7 +66,6 @@ void create(final BigDecimal price) { @DisplayName("상품의 이름이 올바르지 않으면 등록할 수 없다.") @ValueSource(strings = {"비속어", "욕설이 포함된 이름"}) - @NullSource @ParameterizedTest void create(final String name) { final CreateProductRequest expected = createProductRequest(name, 16_000L); @@ -86,7 +84,6 @@ void changePrice() { @DisplayName("상품의 가격이 올바르지 않으면 변경할 수 없다.") @ValueSource(strings = "-1000") - @NullSource @ParameterizedTest void changePrice(final BigDecimal price) { final UUID productId = productRepository.save(product("후라이드", 16_000L)).getId(); From e7ade817537fec6b92eb8bb5adf0607a2635f828 Mon Sep 17 00:00:00 2001 From: seovalue Date: Sat, 15 Feb 2025 11:10:27 +0900 Subject: [PATCH 9/9] refactor: Separate updateMenuDisplay --- .../menus/application/MenuService.java | 18 +++++++++++++ .../products/application/ProductService.java | 26 +++++-------------- .../application/ProductServiceTest.java | 16 ++++++++---- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/main/java/kitchenpos/menus/application/MenuService.java b/src/main/java/kitchenpos/menus/application/MenuService.java index 34b682d46..89d4441c6 100644 --- a/src/main/java/kitchenpos/menus/application/MenuService.java +++ b/src/main/java/kitchenpos/menus/application/MenuService.java @@ -152,4 +152,22 @@ public Menu hide(final UUID menuId) { public List findAll() { return menuRepository.findAll(); } + + @Transactional + public void updateMenuDisplay(Product product) { + final List menus = menuRepository.findAllByProductId(product.getId()); + for (final Menu menu : menus) { + BigDecimal sum = BigDecimal.ZERO; + for (final MenuProduct menuProduct : menu.getMenuProducts()) { + sum = sum.add( + menuProduct.getProduct() + .getPrice() + .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) + ); + } + if (menu.getPrice().compareTo(sum) > 0) { + menu.setDisplayed(false); + } + } + } } diff --git a/src/main/java/kitchenpos/products/application/ProductService.java b/src/main/java/kitchenpos/products/application/ProductService.java index a2db6b3e4..e630c6af9 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/products/application/ProductService.java @@ -1,5 +1,6 @@ package kitchenpos.products.application; +import kitchenpos.menus.application.MenuService; import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; @@ -23,17 +24,17 @@ @Service public class ProductService { private final ProductRepository productRepository; - private final MenuRepository menuRepository; private final CheckBadWordClient checkBadWordClient; + private final MenuService menuService; public ProductService( final ProductRepository productRepository, - final MenuRepository menuRepository, - final CheckBadWordClient checkBadWordClient + final CheckBadWordClient checkBadWordClient, + final MenuService menuService ) { this.productRepository = productRepository; - this.menuRepository = menuRepository; this.checkBadWordClient = checkBadWordClient; + this.menuService = menuService; } @Transactional @@ -55,22 +56,7 @@ public ProductResponse changePrice(final UUID productId, final ChangePriceReques final Product product = productRepository.findById(productId) .orElseThrow(NoSuchElementException::new); final Product updatedProduct = productRepository.save(product.changePrice(request.getPrice())); - - // TODO, 우선 Product 도메인만 수정 - final List menus = menuRepository.findAllByProductId(updatedProduct.getId()); - for (final Menu menu : menus) { - BigDecimal sum = BigDecimal.ZERO; - for (final MenuProduct menuProduct : menu.getMenuProducts()) { - sum = sum.add( - menuProduct.getProduct() - .getPrice() - .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) - ); - } - if (menu.getPrice().compareTo(sum) > 0) { - menu.setDisplayed(false); - } - } + menuService.updateMenuDisplay(updatedProduct); return ProductResponse.of(productRepository.save(updatedProduct)); } diff --git a/src/test/java/kitchenpos/products/application/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/ProductServiceTest.java index 5d5470d65..f884fa44f 100644 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/application/ProductServiceTest.java @@ -1,9 +1,10 @@ package kitchenpos.products.application; +import kitchenpos.menus.application.InMemoryMenuGroupRepository; import kitchenpos.menus.application.InMemoryMenuRepository; +import kitchenpos.menus.application.MenuService; import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuProduct; -import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRecord; import kitchenpos.products.domain.ProductRepository; @@ -15,7 +16,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; import java.math.BigDecimal; @@ -31,15 +31,21 @@ class ProductServiceTest { private ProductRepository productRepository; - private MenuRepository menuRepository; private ProductService productService; + private InMemoryMenuRepository menuRepository; @BeforeEach void setUp() { productRepository = new InMemoryProductRepository(); - menuRepository = new InMemoryMenuRepository(); CheckBadWordClient checkBadWordClient = new FakeCheckBadWordClient(); - productService = new ProductService(productRepository, menuRepository, checkBadWordClient); + menuRepository = new InMemoryMenuRepository(); + MenuService menuService = new MenuService( + menuRepository, + new InMemoryMenuGroupRepository(), + productRepository, + checkBadWordClient + ); + productService = new ProductService(productRepository, checkBadWordClient, menuService); } @DisplayName("상품을 등록할 수 있다.")