From 1ce635fcbd598a7f421040d50190f76cbae5b9bc Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Tue, 25 Feb 2025 17:15:30 +0900 Subject: [PATCH 01/14] =?UTF-8?q?chore:=20DB=20Connection=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=B3=80=EA=B2=BD=20=E2=80=93=20HikariCP,=20Hibern?= =?UTF-8?q?ate=20Auto-Commit=20=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94?= =?UTF-8?q?=EB=A1=9C=20DbConnection=20=EC=B7=A8=EB=93=9D=20=EC=A7=80?= =?UTF-8?q?=EC=97=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.properties | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index da61c4529..4f58b8945 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -6,3 +6,8 @@ spring.jpa.hibernate.ddl-auto=validate spring.jpa.properties.hibernate.format_sql=true spring.jpa.show-sql=true logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE + + +# ?? db connection ?? +spring.datasource.hikari.auto-commit=false +spring.jpa.properties.hibernate.connection.provider_disables_autocommit=true \ No newline at end of file From 7f3a747360deeb3c6182b4c1839072951efeb76c Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Tue, 25 Feb 2025 19:49:30 +0900 Subject: [PATCH 02/14] =?UTF-8?q?chore:=20docker=20compose=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..c7728161f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +version: '3.8' + +services: + mysql: + image: mysql:8.0 + container_name: kitchenpos-mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: kitchenpos + MYSQL_USER: user + MYSQL_PASSWORD: password + ports: + - "33306:3306" + command: --default-authentication-plugin=mysql_native_password + volumes: + - mysql_data:/var/lib/mysql + +volumes: + mysql_data: \ No newline at end of file From 28bb20039ccb67f78b161d036d8392c285b2f8b9 Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Tue, 25 Feb 2025 19:49:48 +0900 Subject: [PATCH 03/14] =?UTF-8?q?feat(core):=20=EA=B8=B0=EB=B3=B8=20Entity?= =?UTF-8?q?=20Type=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shared/domain/DomainEntity.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/main/java/kitchenpos/shared/domain/DomainEntity.java diff --git a/src/main/java/kitchenpos/shared/domain/DomainEntity.java b/src/main/java/kitchenpos/shared/domain/DomainEntity.java new file mode 100644 index 000000000..0783574ac --- /dev/null +++ b/src/main/java/kitchenpos/shared/domain/DomainEntity.java @@ -0,0 +1,37 @@ +package kitchenpos.shared.domain; + +import java.io.Serializable; + +public abstract class DomainEntity, TID> implements Serializable { + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + + return equals((T)other); + } + + public boolean equals(T other) { + if (other == null) { + return false; + } + + if (getId() == null) { + return false; + } + + if (other.getClass().equals(getClass())) { + return getId().equals(other.getId()); + } + + return super.equals(other); + } + + @Override + public int hashCode() { + return getId() == null ? 0 : getId().hashCode(); + } + + abstract public TID getId(); +} From 6626e1aad7db47e57e080836c03b5d59d7668358 Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Tue, 25 Feb 2025 21:29:01 +0900 Subject: [PATCH 04/14] =?UTF-8?q?feat(core):=20=EA=B3=B5=ED=86=B5=20Money?= =?UTF-8?q?=20=ED=83=80=EC=9E=85,=20Converter=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/kitchenpos/shared/domain/Money.java | 36 +++++++++++++++++++ .../shared/infra/jpa/MoneyConverter.java | 18 ++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/main/java/kitchenpos/shared/domain/Money.java create mode 100644 src/main/java/kitchenpos/shared/infra/jpa/MoneyConverter.java diff --git a/src/main/java/kitchenpos/shared/domain/Money.java b/src/main/java/kitchenpos/shared/domain/Money.java new file mode 100644 index 000000000..8ddd5d9bd --- /dev/null +++ b/src/main/java/kitchenpos/shared/domain/Money.java @@ -0,0 +1,36 @@ +package kitchenpos.shared.domain; + +import java.math.BigDecimal; + +public record Money(BigDecimal amount) { + public Money { + if (amount == null || amount.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("금액은 null 이거나 0보다 작을 수 없습니다."); + } + } + + public static Money of(long amount) { + return new Money(BigDecimal.valueOf(amount)); + } + + public static Money ZERO() { + return new Money(BigDecimal.ZERO); + } + + public Money add(Money other) { + return new Money(this.amount.add(other.amount)); + } + + public Money subtract(Money other) { + return new Money(this.amount.subtract(other.amount)); + } + + public boolean isGreaterThanOrEqual(Money other) { + return this.amount.compareTo(other.amount) >= 0; + } + + public boolean isLessThan(Money other) { + return this.amount.compareTo(other.amount) < 0; + } + +} \ No newline at end of file diff --git a/src/main/java/kitchenpos/shared/infra/jpa/MoneyConverter.java b/src/main/java/kitchenpos/shared/infra/jpa/MoneyConverter.java new file mode 100644 index 000000000..3455f721d --- /dev/null +++ b/src/main/java/kitchenpos/shared/infra/jpa/MoneyConverter.java @@ -0,0 +1,18 @@ +package kitchenpos.shared.infra.jpa; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import kitchenpos.shared.domain.Money; + +@Converter(autoApply = true) +public class MoneyConverter implements AttributeConverter { + @Override + public Long convertToDatabaseColumn(Money money) { + return money.amount().longValue(); + } + + @Override + public Money convertToEntityAttribute(Long amount) { + return Money.of(amount); + } +} \ No newline at end of file From 54e1cce476957972ddadfb3ba5bd7d6c05425eb8 Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Tue, 25 Feb 2025 21:29:26 +0900 Subject: [PATCH 05/14] =?UTF-8?q?refactor(product):=20Product=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/TobeJpaProductRepository.java | 9 ++++ .../products/tobe/domain/Product.java | 52 +++++++++++++++++++ .../products/tobe/domain/ProductId.java | 30 +++++++++++ .../tobe/domain/ProductIdGenerator.java | 5 ++ .../products/tobe/domain/ProductName.java | 15 ++++++ .../products/tobe/domain/ProductPrice.java | 16 ++++++ .../tobe/domain/TobeProductRepository.java | 20 +++++++ .../exception/InvalidProductIdException.java | 7 +++ .../InvalidProductNameException.java | 7 +++ .../InvalidProductPriceException.java | 7 +++ .../support/UUIDBasedProductIdGenerator.java | 15 ++++++ .../db/migration/V3__temp_product_tobe.sql | 7 +++ 12 files changed, 190 insertions(+) create mode 100644 src/main/java/kitchenpos/products/infra/TobeJpaProductRepository.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/Product.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/ProductId.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/ProductIdGenerator.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/ProductName.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/TobeProductRepository.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductIdException.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductNameException.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductPriceException.java create mode 100644 src/main/java/kitchenpos/products/tobe/domain/support/UUIDBasedProductIdGenerator.java create mode 100644 src/main/resources/db/migration/V3__temp_product_tobe.sql diff --git a/src/main/java/kitchenpos/products/infra/TobeJpaProductRepository.java b/src/main/java/kitchenpos/products/infra/TobeJpaProductRepository.java new file mode 100644 index 000000000..4370bc5f2 --- /dev/null +++ b/src/main/java/kitchenpos/products/infra/TobeJpaProductRepository.java @@ -0,0 +1,9 @@ +package kitchenpos.products.infra; + +import kitchenpos.products.tobe.domain.Product; +import kitchenpos.products.tobe.domain.ProductId; +import kitchenpos.products.tobe.domain.TobeProductRepository; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TobeJpaProductRepository extends TobeProductRepository, JpaRepository { +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/Product.java b/src/main/java/kitchenpos/products/tobe/domain/Product.java new file mode 100644 index 000000000..074efa1a2 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/Product.java @@ -0,0 +1,52 @@ +package kitchenpos.products.tobe.domain; + +import jakarta.persistence.*; +import kitchenpos.shared.domain.DomainEntity; +import kitchenpos.shared.domain.Money; + +import java.math.BigDecimal; +import java.util.Objects; +import java.util.UUID; + +@Table(name = "product_tobe") +@Entity(name = "ProductTobe") +public class Product extends DomainEntity { + + @EmbeddedId + @AttributeOverride(name = "value", column = @Column(name = "id")) + private ProductId id; + + @Embedded + @AttributeOverride(name = "name", column = @Column(name = "name", nullable = false)) + private ProductName name; + + @Embedded + @AttributeOverride(name = "price", column = @Column(name = "price", nullable = false)) + private ProductPrice price; + + @SuppressWarnings("unused") + protected Product() {} + + private Product(ProductId id, ProductName name, ProductPrice price) { + this.id = Objects.requireNonNull(id, "id must not be null"); + this.name = Objects.requireNonNull(name, "name must not be null"); + this.price = Objects.requireNonNull(price, "price must not be null"); + } + + public static Product create(ProductId id, ProductName name, ProductPrice price) { + return new Product(id, name, price); + } + + /** + * getter + */ + @Override + public ProductId getId() { + return id; + } + + public ProductName getName() { return name; } + + public ProductPrice getPrice() { return price; } + +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductId.java b/src/main/java/kitchenpos/products/tobe/domain/ProductId.java new file mode 100644 index 000000000..3179f2d08 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductId.java @@ -0,0 +1,30 @@ +package kitchenpos.products.tobe.domain; + +import jakarta.persistence.Embeddable; +import kitchenpos.products.tobe.domain.exception.InvalidProductIdException; + +import java.io.Serializable; + +@Embeddable +public class ProductId implements Serializable { + + private String value; + + @SuppressWarnings("unused") + protected ProductId() {} + + private ProductId(String value) { + if (value == null || value.isBlank()) { + throw new InvalidProductIdException("Product ID 는 null 이거나 빈 값이 될 수 없습니다."); + } + this.value = value.strip(); + } + + public static ProductId of(String value) { + return new ProductId(value); + } + + public String getValue() { + return value; + } +} \ No newline at end of file diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductIdGenerator.java b/src/main/java/kitchenpos/products/tobe/domain/ProductIdGenerator.java new file mode 100644 index 000000000..9e8478396 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductIdGenerator.java @@ -0,0 +1,5 @@ +package kitchenpos.products.tobe.domain; + +public interface ProductIdGenerator { + ProductId generateId(); +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductName.java b/src/main/java/kitchenpos/products/tobe/domain/ProductName.java new file mode 100644 index 000000000..d12a61392 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductName.java @@ -0,0 +1,15 @@ +package kitchenpos.products.tobe.domain; + +import jakarta.persistence.Embeddable; +import kitchenpos.products.tobe.domain.exception.InvalidProductNameException; + +@Embeddable +public record ProductName(String name) { + + public ProductName { + if (name == null || name.isBlank()) { + throw new InvalidProductNameException("Product name 은 null 이거나 빈 값이 될 수 없습니다."); + } + name = name.strip(); + } +} \ No newline at end of file diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java b/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java new file mode 100644 index 000000000..30b0e9cfb --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java @@ -0,0 +1,16 @@ +package kitchenpos.products.tobe.domain; + +import kitchenpos.products.tobe.domain.exception.InvalidProductPriceException; +import kitchenpos.shared.domain.Money; + +public record ProductPrice(Money price) { + public ProductPrice { + if (price == null || price.isLessThan(Money.ZERO())) { + throw new InvalidProductPriceException("상품 가격은 null 이거나 0보다 작을 수 없습니다."); + } + } + + public ProductPrice (long price) { + this(Money.of(price)); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/TobeProductRepository.java b/src/main/java/kitchenpos/products/tobe/domain/TobeProductRepository.java new file mode 100644 index 000000000..93c9b52b8 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/TobeProductRepository.java @@ -0,0 +1,20 @@ +package kitchenpos.products.tobe.domain; + +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +@Repository +@Transactional +public interface TobeProductRepository { + Product save(Product product); + + Optional findById(ProductId id); + + List findAll(); + + List findAllByIdIn(List ids); +} + diff --git a/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductIdException.java b/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductIdException.java new file mode 100644 index 000000000..a903e88cd --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductIdException.java @@ -0,0 +1,7 @@ +package kitchenpos.products.tobe.domain.exception; + +public class InvalidProductIdException extends IllegalArgumentException { + public InvalidProductIdException(String s) { + super(s); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductNameException.java b/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductNameException.java new file mode 100644 index 000000000..f69088626 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductNameException.java @@ -0,0 +1,7 @@ +package kitchenpos.products.tobe.domain.exception; + +public class InvalidProductNameException extends IllegalArgumentException { + public InvalidProductNameException(String s) { + super(s); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductPriceException.java b/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductPriceException.java new file mode 100644 index 000000000..612778fa5 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductPriceException.java @@ -0,0 +1,7 @@ +package kitchenpos.products.tobe.domain.exception; + +public class InvalidProductPriceException extends IllegalArgumentException { + public InvalidProductPriceException(String s) { + super(s); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/support/UUIDBasedProductIdGenerator.java b/src/main/java/kitchenpos/products/tobe/domain/support/UUIDBasedProductIdGenerator.java new file mode 100644 index 000000000..e5af40abd --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/support/UUIDBasedProductIdGenerator.java @@ -0,0 +1,15 @@ +package kitchenpos.products.tobe.domain.support; + +import kitchenpos.products.tobe.domain.ProductId; +import kitchenpos.products.tobe.domain.ProductIdGenerator; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +public class UUIDBasedProductIdGenerator implements ProductIdGenerator { + @Override + public ProductId generateId() { + return ProductId.of(UUID.randomUUID().toString()); + } +} diff --git a/src/main/resources/db/migration/V3__temp_product_tobe.sql b/src/main/resources/db/migration/V3__temp_product_tobe.sql new file mode 100644 index 000000000..5c2956f09 --- /dev/null +++ b/src/main/resources/db/migration/V3__temp_product_tobe.sql @@ -0,0 +1,7 @@ +create table product_tobe +( + id varchar(255) not null + primary key, + name varchar(255) not null, + price bigint not null +); From ef2b0b9243b4993abffe0c7e3cb2f868c4a1bc41 Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Tue, 25 Feb 2025 22:36:08 +0900 Subject: [PATCH 06/14] =?UTF-8?q?refactor(product):=20Product=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../menus/application/MenuService.java | 10 ++-- .../products/application/ProductService.java | 10 ++-- .../products/infra/PurgomalumClient.java | 5 -- .../products/tobe/domain/ProductName.java | 33 +++++++++++-- .../products/tobe/domain/ProductPrice.java | 24 +++++++-- .../java/kitchenpos/shared/domain/Money.java | 35 ++++++++++--- .../shared/domain/ProfanityChecker.java | 5 ++ .../kitchenpos/shared/domain/ValueObject.java | 49 +++++++++++++++++++ .../infra/DefaultProfanityChecker.java} | 7 +-- .../shared/infra/jpa/MoneyConverter.java | 4 +- .../menus/application/MenuServiceTest.java | 10 ++-- ...mClient.java => FakeProfanityChecker.java} | 4 +- .../application/ProductServiceTest.java | 8 +-- 13 files changed, 157 insertions(+), 47 deletions(-) delete mode 100644 src/main/java/kitchenpos/products/infra/PurgomalumClient.java create mode 100644 src/main/java/kitchenpos/shared/domain/ProfanityChecker.java create mode 100644 src/main/java/kitchenpos/shared/domain/ValueObject.java rename src/main/java/kitchenpos/{products/infra/DefaultPurgomalumClient.java => shared/infra/DefaultProfanityChecker.java} (78%) rename src/test/java/kitchenpos/products/application/{FakePurgomalumClient.java => FakeProfanityChecker.java} (77%) diff --git a/src/main/java/kitchenpos/menus/application/MenuService.java b/src/main/java/kitchenpos/menus/application/MenuService.java index abefa5bcf..1dc8e0387 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.Product; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.shared.domain.ProfanityChecker; 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 ProfanityChecker profanityChecker; public MenuService( final MenuRepository menuRepository, final MenuGroupRepository menuGroupRepository, final ProductRepository productRepository, - final PurgomalumClient purgomalumClient + final ProfanityChecker profanityChecker ) { this.menuRepository = menuRepository; this.menuGroupRepository = menuGroupRepository; this.productRepository = productRepository; - this.purgomalumClient = purgomalumClient; + this.profanityChecker = profanityChecker; } @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) || profanityChecker.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 20cf63996..36f82826c 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/products/application/ProductService.java @@ -5,7 +5,7 @@ import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.shared.domain.ProfanityChecker; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -19,16 +19,16 @@ public class ProductService { private final ProductRepository productRepository; private final MenuRepository menuRepository; - private final PurgomalumClient purgomalumClient; + private final ProfanityChecker profanityChecker; public ProductService( final ProductRepository productRepository, final MenuRepository menuRepository, - final PurgomalumClient purgomalumClient + final ProfanityChecker profanityChecker ) { this.productRepository = productRepository; this.menuRepository = menuRepository; - this.purgomalumClient = purgomalumClient; + this.profanityChecker = profanityChecker; } @Transactional @@ -38,7 +38,7 @@ public Product create(final Product request) { throw new IllegalArgumentException(); } final String name = request.getName(); - if (Objects.isNull(name) || purgomalumClient.containsProfanity(name)) { + if (Objects.isNull(name) || profanityChecker.containsProfanity(name)) { throw new IllegalArgumentException(); } final Product product = new Product(); diff --git a/src/main/java/kitchenpos/products/infra/PurgomalumClient.java b/src/main/java/kitchenpos/products/infra/PurgomalumClient.java deleted file mode 100644 index 4002a2bb8..000000000 --- a/src/main/java/kitchenpos/products/infra/PurgomalumClient.java +++ /dev/null @@ -1,5 +0,0 @@ -package kitchenpos.products.infra; - -public interface PurgomalumClient { - boolean containsProfanity(String text); -} diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductName.java b/src/main/java/kitchenpos/products/tobe/domain/ProductName.java index d12a61392..94de10d7f 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/ProductName.java +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductName.java @@ -1,15 +1,42 @@ package kitchenpos.products.tobe.domain; import jakarta.persistence.Embeddable; +import jakarta.persistence.Transient; import kitchenpos.products.tobe.domain.exception.InvalidProductNameException; +import kitchenpos.shared.domain.ProfanityChecker; +import kitchenpos.shared.domain.ValueObject; @Embeddable -public record ProductName(String name) { +public class ProductName extends ValueObject { - public ProductName { + private String name; + + @SuppressWarnings("unused") + protected ProductName() {} + + // 외부에서 직접 호출할 수 없는 private 생성자: 검증된 이름만 받음 + private ProductName(String name) { + this.name = name; + } + + public static ProductName create(ProfanityChecker profanityChecker, String name) { if (name == null || name.isBlank()) { throw new InvalidProductNameException("Product name 은 null 이거나 빈 값이 될 수 없습니다."); } - name = name.strip(); + + if (profanityChecker.containsProfanity(name)) { + throw new InvalidProductNameException("Product name 에 비속어가 포함될 수 없습니다."); + } + return new ProductName(name.strip()); + } + + public String getName() { + return name; + } + + @Override + @Transient + protected Object[] getEqualityFields() { + return new Object[] { name }; } } \ No newline at end of file diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java b/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java index 30b0e9cfb..daeae196f 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java @@ -2,15 +2,29 @@ import kitchenpos.products.tobe.domain.exception.InvalidProductPriceException; import kitchenpos.shared.domain.Money; +import kitchenpos.shared.domain.ValueObject; -public record ProductPrice(Money price) { - public ProductPrice { - if (price == null || price.isLessThan(Money.ZERO())) { +public class ProductPrice extends ValueObject { + private Money price; + + private ProductPrice(Money price) { + if (price == null || price.isLessThan(Money.ZERO)) { throw new InvalidProductPriceException("상품 가격은 null 이거나 0보다 작을 수 없습니다."); } + + this.price = price; + } + + public static ProductPrice of(Money price) { + return new ProductPrice(price); + } + + public Money getPrice() { + return price; } - public ProductPrice (long price) { - this(Money.of(price)); + @Override + protected Object[] getEqualityFields() { + return new Object[] { price }; } } diff --git a/src/main/java/kitchenpos/shared/domain/Money.java b/src/main/java/kitchenpos/shared/domain/Money.java index 8ddd5d9bd..db8c425f1 100644 --- a/src/main/java/kitchenpos/shared/domain/Money.java +++ b/src/main/java/kitchenpos/shared/domain/Money.java @@ -2,19 +2,24 @@ import java.math.BigDecimal; -public record Money(BigDecimal amount) { - public Money { - if (amount == null || amount.compareTo(BigDecimal.ZERO) < 0) { - throw new IllegalArgumentException("금액은 null 이거나 0보다 작을 수 없습니다."); - } +public class Money extends ValueObject { + public static final Money ZERO = Money.wons(0); + + private final BigDecimal amount; + + public static Money wons(long amount) { + return new Money(BigDecimal.valueOf(amount)); } - public static Money of(long amount) { + public static Money wons(double amount) { return new Money(BigDecimal.valueOf(amount)); } - public static Money ZERO() { - return new Money(BigDecimal.ZERO); + private Money(BigDecimal amount) { + if(amount == null || amount.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("금액은 null 이거나 0보다 작을 수 없습니다."); + } + this.amount = amount; } public Money add(Money other) { @@ -33,4 +38,18 @@ public boolean isLessThan(Money other) { return this.amount.compareTo(other.amount) < 0; } + public BigDecimal getAmount() { + return amount; + } + + @Override + protected Object[] getEqualityFields() { + return new Object[] { amount.doubleValue() }; + } + + @Override + public String toString() { + return amount.toString() + "원"; + } + } \ No newline at end of file diff --git a/src/main/java/kitchenpos/shared/domain/ProfanityChecker.java b/src/main/java/kitchenpos/shared/domain/ProfanityChecker.java new file mode 100644 index 000000000..d05b6c293 --- /dev/null +++ b/src/main/java/kitchenpos/shared/domain/ProfanityChecker.java @@ -0,0 +1,5 @@ +package kitchenpos.shared.domain; + +public interface ProfanityChecker { + boolean containsProfanity(String text); +} diff --git a/src/main/java/kitchenpos/shared/domain/ValueObject.java b/src/main/java/kitchenpos/shared/domain/ValueObject.java new file mode 100644 index 000000000..16f08257a --- /dev/null +++ b/src/main/java/kitchenpos/shared/domain/ValueObject.java @@ -0,0 +1,49 @@ +package kitchenpos.shared.domain; + +import java.io.Serializable; +import java.util.Arrays; + +public abstract class ValueObject> implements Serializable { + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + + if (!(other.getClass().equals(getClass()))) { + return false; + } + + return equals((T)other); + } + + public boolean equals(T other) { + if (other == null) { + return false; + } + + return Arrays.equals(getEqualityFields(), other.getEqualityFields()); + } + + @Override + public int hashCode() { + int hash = 17; + for(Object each : getEqualityFields()) { + hash = hash * 31 + (each == null ? 0 : each.hashCode()); + } + return hash; + } + + protected Object[] getEqualityFields() { + return Arrays.stream(getClass().getDeclaredFields()) + .map(field -> { + try { + field.setAccessible(true); + return field.get(this); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }) + .toArray(); + } +} \ No newline at end of file diff --git a/src/main/java/kitchenpos/products/infra/DefaultPurgomalumClient.java b/src/main/java/kitchenpos/shared/infra/DefaultProfanityChecker.java similarity index 78% rename from src/main/java/kitchenpos/products/infra/DefaultPurgomalumClient.java rename to src/main/java/kitchenpos/shared/infra/DefaultProfanityChecker.java index 87dba885c..b955ca38b 100644 --- a/src/main/java/kitchenpos/products/infra/DefaultPurgomalumClient.java +++ b/src/main/java/kitchenpos/shared/infra/DefaultProfanityChecker.java @@ -1,5 +1,6 @@ -package kitchenpos.products.infra; +package kitchenpos.shared.infra; +import kitchenpos.shared.domain.ProfanityChecker; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @@ -8,10 +9,10 @@ import java.net.URI; @Component -public class DefaultPurgomalumClient implements PurgomalumClient { +public class DefaultProfanityChecker implements ProfanityChecker { private final RestTemplate restTemplate; - public DefaultPurgomalumClient(final RestTemplateBuilder restTemplateBuilder) { + public DefaultProfanityChecker(final RestTemplateBuilder restTemplateBuilder) { this.restTemplate = restTemplateBuilder.build(); } diff --git a/src/main/java/kitchenpos/shared/infra/jpa/MoneyConverter.java b/src/main/java/kitchenpos/shared/infra/jpa/MoneyConverter.java index 3455f721d..7d8df4a94 100644 --- a/src/main/java/kitchenpos/shared/infra/jpa/MoneyConverter.java +++ b/src/main/java/kitchenpos/shared/infra/jpa/MoneyConverter.java @@ -8,11 +8,11 @@ public class MoneyConverter implements AttributeConverter { @Override public Long convertToDatabaseColumn(Money money) { - return money.amount().longValue(); + return money.getAmount().longValue(); } @Override public Money convertToEntityAttribute(Long amount) { - return Money.of(amount); + return Money.wons(amount); } } \ No newline at end of file diff --git a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java index 277679118..64f2222d8 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.FakeProfanityChecker; import kitchenpos.products.application.InMemoryProductRepository; import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.shared.domain.ProfanityChecker; 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 ProfanityChecker profanityChecker; private MenuService menuService; private UUID menuGroupId; private Product 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); + profanityChecker = new FakeProfanityChecker(); + menuService = new MenuService(menuRepository, menuGroupRepository, productRepository, profanityChecker); 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/FakeProfanityChecker.java similarity index 77% rename from src/test/java/kitchenpos/products/application/FakePurgomalumClient.java rename to src/test/java/kitchenpos/products/application/FakeProfanityChecker.java index 3c4114798..d70e6c089 100644 --- a/src/test/java/kitchenpos/products/application/FakePurgomalumClient.java +++ b/src/test/java/kitchenpos/products/application/FakeProfanityChecker.java @@ -1,11 +1,11 @@ package kitchenpos.products.application; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.shared.domain.ProfanityChecker; import java.util.Arrays; import java.util.List; -public class FakePurgomalumClient implements PurgomalumClient { +public class FakeProfanityChecker implements ProfanityChecker { 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 74a31073e..826c2ada0 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.Product; import kitchenpos.products.domain.ProductRepository; -import kitchenpos.products.infra.PurgomalumClient; +import kitchenpos.shared.domain.ProfanityChecker; 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 ProfanityChecker profanityChecker; private ProductService productService; @BeforeEach void setUp() { productRepository = new InMemoryProductRepository(); menuRepository = new InMemoryMenuRepository(); - purgomalumClient = new FakePurgomalumClient(); - productService = new ProductService(productRepository, menuRepository, purgomalumClient); + profanityChecker = new FakeProfanityChecker(); + productService = new ProductService(productRepository, menuRepository, profanityChecker); } @DisplayName("상품을 등록할 수 있다.") From 4696cff35f9b04ff1cb60f6c69ade5f688c29c0d Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Thu, 27 Feb 2025 21:43:38 +0900 Subject: [PATCH 07/14] =?UTF-8?q?chore:=20=EC=A4=91=EB=B3=B5=20docker=20co?= =?UTF-8?q?mpose=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index c7728161f..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,20 +0,0 @@ -version: '3.8' - -services: - mysql: - image: mysql:8.0 - container_name: kitchenpos-mysql - restart: always - environment: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: kitchenpos - MYSQL_USER: user - MYSQL_PASSWORD: password - ports: - - "33306:3306" - command: --default-authentication-plugin=mysql_native_password - volumes: - - mysql_data:/var/lib/mysql - -volumes: - mysql_data: \ No newline at end of file From 5bd913bc012398bcda1882ab4ee5e681501017ab Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Fri, 28 Feb 2025 00:42:59 +0900 Subject: [PATCH 08/14] refactor: Product refactoring --- .../eatinorders/application/OrderService.java | 22 +++++----- .../application/OrderTableService.java | 10 ++--- .../domain/JpaOrderRepository.java | 2 +- .../domain/JpaOrderTableRepository.java | 2 +- .../{ => core}/eatinorders/domain/Order.java | 2 +- .../eatinorders/domain/OrderLineItem.java | 4 +- .../eatinorders/domain/OrderRepository.java | 2 +- .../eatinorders/domain/OrderStatus.java | 2 +- .../eatinorders/domain/OrderTable.java | 2 +- .../domain/OrderTableRepository.java | 2 +- .../eatinorders/domain/OrderType.java | 2 +- .../menus/application/MenuGroupService.java | 6 +-- .../menus/application/MenuService.java | 18 ++++---- .../menus/domain/JpaMenuGroupRepository.java | 2 +- .../menus/domain/JpaMenuRepository.java | 2 +- .../{ => core}/menus/domain/Menu.java | 2 +- .../{ => core}/menus/domain/MenuGroup.java | 2 +- .../menus/domain/MenuGroupRepository.java | 2 +- .../{ => core}/menus/domain/MenuProduct.java | 4 +- .../menus/domain/MenuRepository.java | 2 +- .../core/products/application/AddProduct.java | 7 ++++ .../application/CommandProductService.java} | 18 ++++---- .../application/CreateProductRequest.java | 12 ++++++ .../products/domain/JpaProductRepository.java | 2 +- .../{ => core}/products/domain/Product.java | 2 +- .../products/domain/ProductRepository.java | 2 +- .../products/tobe/domain/Product.java | 8 ++-- .../tobe/domain/ProductIdGenerator.java | 7 ++++ .../products/tobe/domain/ProductName.java | 39 +++++++++++++++++ .../tobe/domain/ProductNamePolicy.java | 7 ++++ .../products/tobe/domain/ProductPrice.java | 8 ++-- .../tobe/domain/TobeProductRepository.java | 3 +- .../exception/InvalidProductIdException.java | 2 +- .../InvalidProductNameException.java | 2 +- .../InvalidProductPriceException.java | 2 +- .../support/DefaultProductNamePolicy.java | 30 +++++++++++++ .../support/ProductNameValidationResult.java | 5 +++ .../support/UUIDBasedProductIdGenerator.java | 6 +-- .../shared/data}/DefaultProfanityChecker.java | 4 +- .../shared/data}/jpa/MoneyConverter.java | 4 +- .../shared/domain/DomainEntity.java | 2 +- .../shared/domain/ProfanityChecker.java | 2 +- .../{ => core}/shared/domain/ValueObject.java | 2 +- .../shared/identifier}/ProductId.java | 4 +- .../domain => core/shared/value}/Money.java | 26 +++++------- .../{ => core}/takeoutorders/empty.txt | 0 .../DefaultKitchenridersClient.java | 2 +- .../infra => data}/KitchenridersClient.java | 2 +- .../data/jpa/TobeJpaProductRepository.java | 9 ++++ .../infra/TobeJpaProductRepository.java | 9 ---- .../tobe/domain/ProductIdGenerator.java | 5 --- .../products/tobe/domain/ProductName.java | 42 ------------------- .../ui => web}/MenuGroupRestController.java | 6 +-- .../{menus/ui => web}/MenuRestController.java | 6 +-- .../ui => web}/OrderRestController.java | 6 +-- .../ui => web}/OrderTableRestController.java | 6 +-- .../ui => web}/ProductRestController.java | 10 ++--- src/test/java/kitchenpos/Fixtures.java | 18 ++++---- .../application/FakeKitchenridersClient.java | 2 +- .../application/InMemoryOrderRepository.java | 8 ++-- .../InMemoryOrderTableRepository.java | 4 +- .../application/OrderServiceTest.java | 17 ++++---- .../application/OrderTableServiceTest.java | 9 ++-- .../InMemoryMenuGroupRepository.java | 4 +- .../application/InMemoryMenuRepository.java | 4 +- .../application/MenuGroupServiceTest.java | 5 ++- .../menus/application/MenuServiceTest.java | 15 +++---- .../application/FakeProfanityChecker.java | 2 +- .../InMemoryProductRepository.java | 4 +- .../application/ProductServiceTest.java | 15 +++---- 70 files changed, 283 insertions(+), 225 deletions(-) rename src/main/java/kitchenpos/{ => core}/eatinorders/application/OrderService.java (92%) rename src/main/java/kitchenpos/{ => core}/eatinorders/application/OrderTableService.java (90%) rename src/main/java/kitchenpos/{ => core}/eatinorders/domain/JpaOrderRepository.java (80%) rename src/main/java/kitchenpos/{ => core}/eatinorders/domain/JpaOrderTableRepository.java (81%) rename src/main/java/kitchenpos/{ => core}/eatinorders/domain/Order.java (98%) rename src/main/java/kitchenpos/{ => core}/eatinorders/domain/OrderLineItem.java (95%) rename src/main/java/kitchenpos/{ => core}/eatinorders/domain/OrderRepository.java (87%) rename src/main/java/kitchenpos/{ => core}/eatinorders/domain/OrderStatus.java (67%) rename src/main/java/kitchenpos/{ => core}/eatinorders/domain/OrderTable.java (96%) rename src/main/java/kitchenpos/{ => core}/eatinorders/domain/OrderTableRepository.java (84%) rename src/main/java/kitchenpos/{ => core}/eatinorders/domain/OrderType.java (56%) rename src/main/java/kitchenpos/{ => core}/menus/application/MenuGroupService.java (86%) rename src/main/java/kitchenpos/{ => core}/menus/application/MenuService.java (92%) rename src/main/java/kitchenpos/{ => core}/menus/domain/JpaMenuGroupRepository.java (83%) rename src/main/java/kitchenpos/{ => core}/menus/domain/JpaMenuRepository.java (92%) rename src/main/java/kitchenpos/{ => core}/menus/domain/Menu.java (98%) rename src/main/java/kitchenpos/{ => core}/menus/domain/MenuGroup.java (94%) rename src/main/java/kitchenpos/{ => core}/menus/domain/MenuGroupRepository.java (86%) rename src/main/java/kitchenpos/{ => core}/menus/domain/MenuProduct.java (94%) rename src/main/java/kitchenpos/{ => core}/menus/domain/MenuRepository.java (88%) create mode 100644 src/main/java/kitchenpos/core/products/application/AddProduct.java rename src/main/java/kitchenpos/{products/application/ProductService.java => core/products/application/CommandProductService.java} (85%) create mode 100644 src/main/java/kitchenpos/core/products/application/CreateProductRequest.java rename src/main/java/kitchenpos/{ => core}/products/domain/JpaProductRepository.java (81%) rename src/main/java/kitchenpos/{ => core}/products/domain/Product.java (95%) rename src/main/java/kitchenpos/{ => core}/products/domain/ProductRepository.java (86%) rename src/main/java/kitchenpos/{ => core}/products/tobe/domain/Product.java (87%) create mode 100644 src/main/java/kitchenpos/core/products/tobe/domain/ProductIdGenerator.java create mode 100644 src/main/java/kitchenpos/core/products/tobe/domain/ProductName.java create mode 100644 src/main/java/kitchenpos/core/products/tobe/domain/ProductNamePolicy.java rename src/main/java/kitchenpos/{ => core}/products/tobe/domain/ProductPrice.java (73%) rename src/main/java/kitchenpos/{ => core}/products/tobe/domain/TobeProductRepository.java (80%) rename src/main/java/kitchenpos/{ => core}/products/tobe/domain/exception/InvalidProductIdException.java (72%) rename src/main/java/kitchenpos/{ => core}/products/tobe/domain/exception/InvalidProductNameException.java (73%) rename src/main/java/kitchenpos/{ => core}/products/tobe/domain/exception/InvalidProductPriceException.java (73%) create mode 100644 src/main/java/kitchenpos/core/products/tobe/domain/support/DefaultProductNamePolicy.java create mode 100644 src/main/java/kitchenpos/core/products/tobe/domain/support/ProductNameValidationResult.java rename src/main/java/kitchenpos/{ => core}/products/tobe/domain/support/UUIDBasedProductIdGenerator.java (61%) rename src/main/java/kitchenpos/{shared/infra => core/shared/data}/DefaultProfanityChecker.java (90%) rename src/main/java/kitchenpos/{shared/infra => core/shared/data}/jpa/MoneyConverter.java (83%) rename src/main/java/kitchenpos/{ => core}/shared/domain/DomainEntity.java (95%) rename src/main/java/kitchenpos/{ => core}/shared/domain/ProfanityChecker.java (68%) rename src/main/java/kitchenpos/{ => core}/shared/domain/ValueObject.java (96%) rename src/main/java/kitchenpos/{products/tobe/domain => core/shared/identifier}/ProductId.java (83%) rename src/main/java/kitchenpos/{shared/domain => core/shared/value}/Money.java (60%) rename src/main/java/kitchenpos/{ => core}/takeoutorders/empty.txt (100%) rename src/main/java/kitchenpos/{deliveryorders/infra => data}/DefaultKitchenridersClient.java (88%) rename src/main/java/kitchenpos/{deliveryorders/infra => data}/KitchenridersClient.java (81%) create mode 100644 src/main/java/kitchenpos/data/jpa/TobeJpaProductRepository.java delete mode 100644 src/main/java/kitchenpos/products/infra/TobeJpaProductRepository.java delete mode 100644 src/main/java/kitchenpos/products/tobe/domain/ProductIdGenerator.java delete mode 100644 src/main/java/kitchenpos/products/tobe/domain/ProductName.java rename src/main/java/kitchenpos/{menus/ui => web}/MenuGroupRestController.java (89%) rename src/main/java/kitchenpos/{menus/ui => web}/MenuRestController.java (93%) rename src/main/java/kitchenpos/{eatinorders/ui => web}/OrderRestController.java (94%) rename src/main/java/kitchenpos/{eatinorders/ui => web}/OrderTableRestController.java (93%) rename src/main/java/kitchenpos/{products/ui => web}/ProductRestController.java (83%) diff --git a/src/main/java/kitchenpos/eatinorders/application/OrderService.java b/src/main/java/kitchenpos/core/eatinorders/application/OrderService.java similarity index 92% rename from src/main/java/kitchenpos/eatinorders/application/OrderService.java rename to src/main/java/kitchenpos/core/eatinorders/application/OrderService.java index 1159a0ba0..bddf30f80 100644 --- a/src/main/java/kitchenpos/eatinorders/application/OrderService.java +++ b/src/main/java/kitchenpos/core/eatinorders/application/OrderService.java @@ -1,15 +1,15 @@ -package kitchenpos.eatinorders.application; +package kitchenpos.core.eatinorders.application; -import kitchenpos.deliveryorders.infra.KitchenridersClient; -import kitchenpos.eatinorders.domain.Order; -import kitchenpos.eatinorders.domain.OrderLineItem; -import kitchenpos.eatinorders.domain.OrderRepository; -import kitchenpos.eatinorders.domain.OrderStatus; -import kitchenpos.eatinorders.domain.OrderTable; -import kitchenpos.eatinorders.domain.OrderTableRepository; -import kitchenpos.eatinorders.domain.OrderType; -import kitchenpos.menus.domain.Menu; -import kitchenpos.menus.domain.MenuRepository; +import kitchenpos.data.KitchenridersClient; +import kitchenpos.core.eatinorders.domain.Order; +import kitchenpos.core.eatinorders.domain.OrderLineItem; +import kitchenpos.core.eatinorders.domain.OrderRepository; +import kitchenpos.core.eatinorders.domain.OrderStatus; +import kitchenpos.core.eatinorders.domain.OrderTable; +import kitchenpos.core.eatinorders.domain.OrderTableRepository; +import kitchenpos.core.eatinorders.domain.OrderType; +import kitchenpos.core.menus.domain.Menu; +import kitchenpos.core.menus.domain.MenuRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/kitchenpos/eatinorders/application/OrderTableService.java b/src/main/java/kitchenpos/core/eatinorders/application/OrderTableService.java similarity index 90% rename from src/main/java/kitchenpos/eatinorders/application/OrderTableService.java rename to src/main/java/kitchenpos/core/eatinorders/application/OrderTableService.java index 1df7e345f..a59666ac0 100644 --- a/src/main/java/kitchenpos/eatinorders/application/OrderTableService.java +++ b/src/main/java/kitchenpos/core/eatinorders/application/OrderTableService.java @@ -1,9 +1,9 @@ -package kitchenpos.eatinorders.application; +package kitchenpos.core.eatinorders.application; -import kitchenpos.eatinorders.domain.OrderRepository; -import kitchenpos.eatinorders.domain.OrderStatus; -import kitchenpos.eatinorders.domain.OrderTable; -import kitchenpos.eatinorders.domain.OrderTableRepository; +import kitchenpos.core.eatinorders.domain.OrderRepository; +import kitchenpos.core.eatinorders.domain.OrderStatus; +import kitchenpos.core.eatinorders.domain.OrderTable; +import kitchenpos.core.eatinorders.domain.OrderTableRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/kitchenpos/eatinorders/domain/JpaOrderRepository.java b/src/main/java/kitchenpos/core/eatinorders/domain/JpaOrderRepository.java similarity index 80% rename from src/main/java/kitchenpos/eatinorders/domain/JpaOrderRepository.java rename to src/main/java/kitchenpos/core/eatinorders/domain/JpaOrderRepository.java index 01c825c45..993242cc8 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/JpaOrderRepository.java +++ b/src/main/java/kitchenpos/core/eatinorders/domain/JpaOrderRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.core.eatinorders.domain; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/kitchenpos/eatinorders/domain/JpaOrderTableRepository.java b/src/main/java/kitchenpos/core/eatinorders/domain/JpaOrderTableRepository.java similarity index 81% rename from src/main/java/kitchenpos/eatinorders/domain/JpaOrderTableRepository.java rename to src/main/java/kitchenpos/core/eatinorders/domain/JpaOrderTableRepository.java index 84c0d3c6f..abd03ed25 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/JpaOrderTableRepository.java +++ b/src/main/java/kitchenpos/core/eatinorders/domain/JpaOrderTableRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.core.eatinorders.domain; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/kitchenpos/eatinorders/domain/Order.java b/src/main/java/kitchenpos/core/eatinorders/domain/Order.java similarity index 98% rename from src/main/java/kitchenpos/eatinorders/domain/Order.java rename to src/main/java/kitchenpos/core/eatinorders/domain/Order.java index 4a5991301..e6034ffc5 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/Order.java +++ b/src/main/java/kitchenpos/core/eatinorders/domain/Order.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.core.eatinorders.domain; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderLineItem.java b/src/main/java/kitchenpos/core/eatinorders/domain/OrderLineItem.java similarity index 95% rename from src/main/java/kitchenpos/eatinorders/domain/OrderLineItem.java rename to src/main/java/kitchenpos/core/eatinorders/domain/OrderLineItem.java index a5fe38278..2d1f083bc 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderLineItem.java +++ b/src/main/java/kitchenpos/core/eatinorders/domain/OrderLineItem.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.core.eatinorders.domain; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -10,7 +10,7 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.Transient; -import kitchenpos.menus.domain.Menu; +import kitchenpos.core.menus.domain.Menu; import java.math.BigDecimal; import java.util.UUID; diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderRepository.java b/src/main/java/kitchenpos/core/eatinorders/domain/OrderRepository.java similarity index 87% rename from src/main/java/kitchenpos/eatinorders/domain/OrderRepository.java rename to src/main/java/kitchenpos/core/eatinorders/domain/OrderRepository.java index f98d45c15..fd5466748 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderRepository.java +++ b/src/main/java/kitchenpos/core/eatinorders/domain/OrderRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.core.eatinorders.domain; import java.util.List; import java.util.Optional; diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderStatus.java b/src/main/java/kitchenpos/core/eatinorders/domain/OrderStatus.java similarity index 67% rename from src/main/java/kitchenpos/eatinorders/domain/OrderStatus.java rename to src/main/java/kitchenpos/core/eatinorders/domain/OrderStatus.java index fe0f76c7d..6869758da 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderStatus.java +++ b/src/main/java/kitchenpos/core/eatinorders/domain/OrderStatus.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.core.eatinorders.domain; public enum OrderStatus { WAITING, ACCEPTED, SERVED, DELIVERING, DELIVERED, COMPLETED diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderTable.java b/src/main/java/kitchenpos/core/eatinorders/domain/OrderTable.java similarity index 96% rename from src/main/java/kitchenpos/eatinorders/domain/OrderTable.java rename to src/main/java/kitchenpos/core/eatinorders/domain/OrderTable.java index bdd3dd23c..eedcdb165 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderTable.java +++ b/src/main/java/kitchenpos/core/eatinorders/domain/OrderTable.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.core.eatinorders.domain; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderTableRepository.java b/src/main/java/kitchenpos/core/eatinorders/domain/OrderTableRepository.java similarity index 84% rename from src/main/java/kitchenpos/eatinorders/domain/OrderTableRepository.java rename to src/main/java/kitchenpos/core/eatinorders/domain/OrderTableRepository.java index 1e9047d43..256113753 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderTableRepository.java +++ b/src/main/java/kitchenpos/core/eatinorders/domain/OrderTableRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.core.eatinorders.domain; import java.util.List; import java.util.Optional; diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderType.java b/src/main/java/kitchenpos/core/eatinorders/domain/OrderType.java similarity index 56% rename from src/main/java/kitchenpos/eatinorders/domain/OrderType.java rename to src/main/java/kitchenpos/core/eatinorders/domain/OrderType.java index 0e3133e69..655a5efae 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderType.java +++ b/src/main/java/kitchenpos/core/eatinorders/domain/OrderType.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.core.eatinorders.domain; public enum OrderType { DELIVERY, TAKEOUT, EAT_IN diff --git a/src/main/java/kitchenpos/menus/application/MenuGroupService.java b/src/main/java/kitchenpos/core/menus/application/MenuGroupService.java similarity index 86% rename from src/main/java/kitchenpos/menus/application/MenuGroupService.java rename to src/main/java/kitchenpos/core/menus/application/MenuGroupService.java index c468893a2..f501946f8 100644 --- a/src/main/java/kitchenpos/menus/application/MenuGroupService.java +++ b/src/main/java/kitchenpos/core/menus/application/MenuGroupService.java @@ -1,7 +1,7 @@ -package kitchenpos.menus.application; +package kitchenpos.core.menus.application; -import kitchenpos.menus.domain.MenuGroup; -import kitchenpos.menus.domain.MenuGroupRepository; +import kitchenpos.core.menus.domain.MenuGroup; +import kitchenpos.core.menus.domain.MenuGroupRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/kitchenpos/menus/application/MenuService.java b/src/main/java/kitchenpos/core/menus/application/MenuService.java similarity index 92% rename from src/main/java/kitchenpos/menus/application/MenuService.java rename to src/main/java/kitchenpos/core/menus/application/MenuService.java index 1dc8e0387..0cb98759d 100644 --- a/src/main/java/kitchenpos/menus/application/MenuService.java +++ b/src/main/java/kitchenpos/core/menus/application/MenuService.java @@ -1,13 +1,13 @@ -package kitchenpos.menus.application; +package kitchenpos.core.menus.application; -import kitchenpos.menus.domain.Menu; -import kitchenpos.menus.domain.MenuGroup; -import kitchenpos.menus.domain.MenuGroupRepository; -import kitchenpos.menus.domain.MenuProduct; -import kitchenpos.menus.domain.MenuRepository; -import kitchenpos.products.domain.Product; -import kitchenpos.products.domain.ProductRepository; -import kitchenpos.shared.domain.ProfanityChecker; +import kitchenpos.core.menus.domain.Menu; +import kitchenpos.core.menus.domain.MenuGroup; +import kitchenpos.core.menus.domain.MenuGroupRepository; +import kitchenpos.core.menus.domain.MenuProduct; +import kitchenpos.core.menus.domain.MenuRepository; +import kitchenpos.core.products.domain.Product; +import kitchenpos.core.products.domain.ProductRepository; +import kitchenpos.core.shared.domain.ProfanityChecker; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/kitchenpos/menus/domain/JpaMenuGroupRepository.java b/src/main/java/kitchenpos/core/menus/domain/JpaMenuGroupRepository.java similarity index 83% rename from src/main/java/kitchenpos/menus/domain/JpaMenuGroupRepository.java rename to src/main/java/kitchenpos/core/menus/domain/JpaMenuGroupRepository.java index 233488198..70dd3f37e 100644 --- a/src/main/java/kitchenpos/menus/domain/JpaMenuGroupRepository.java +++ b/src/main/java/kitchenpos/core/menus/domain/JpaMenuGroupRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.menus.domain; +package kitchenpos.core.menus.domain; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/kitchenpos/menus/domain/JpaMenuRepository.java b/src/main/java/kitchenpos/core/menus/domain/JpaMenuRepository.java similarity index 92% rename from src/main/java/kitchenpos/menus/domain/JpaMenuRepository.java rename to src/main/java/kitchenpos/core/menus/domain/JpaMenuRepository.java index 796499c30..a6f38d105 100644 --- a/src/main/java/kitchenpos/menus/domain/JpaMenuRepository.java +++ b/src/main/java/kitchenpos/core/menus/domain/JpaMenuRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.menus.domain; +package kitchenpos.core.menus.domain; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/kitchenpos/menus/domain/Menu.java b/src/main/java/kitchenpos/core/menus/domain/Menu.java similarity index 98% rename from src/main/java/kitchenpos/menus/domain/Menu.java rename to src/main/java/kitchenpos/core/menus/domain/Menu.java index 8d1cc1139..e47a7aaa3 100644 --- a/src/main/java/kitchenpos/menus/domain/Menu.java +++ b/src/main/java/kitchenpos/core/menus/domain/Menu.java @@ -1,4 +1,4 @@ -package kitchenpos.menus.domain; +package kitchenpos.core.menus.domain; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; diff --git a/src/main/java/kitchenpos/menus/domain/MenuGroup.java b/src/main/java/kitchenpos/core/menus/domain/MenuGroup.java similarity index 94% rename from src/main/java/kitchenpos/menus/domain/MenuGroup.java rename to src/main/java/kitchenpos/core/menus/domain/MenuGroup.java index 70e0b8e23..36af130b4 100644 --- a/src/main/java/kitchenpos/menus/domain/MenuGroup.java +++ b/src/main/java/kitchenpos/core/menus/domain/MenuGroup.java @@ -1,4 +1,4 @@ -package kitchenpos.menus.domain; +package kitchenpos.core.menus.domain; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/kitchenpos/menus/domain/MenuGroupRepository.java b/src/main/java/kitchenpos/core/menus/domain/MenuGroupRepository.java similarity index 86% rename from src/main/java/kitchenpos/menus/domain/MenuGroupRepository.java rename to src/main/java/kitchenpos/core/menus/domain/MenuGroupRepository.java index b25e6acbc..85a1d4028 100644 --- a/src/main/java/kitchenpos/menus/domain/MenuGroupRepository.java +++ b/src/main/java/kitchenpos/core/menus/domain/MenuGroupRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.menus.domain; +package kitchenpos.core.menus.domain; import java.util.List; import java.util.Optional; diff --git a/src/main/java/kitchenpos/menus/domain/MenuProduct.java b/src/main/java/kitchenpos/core/menus/domain/MenuProduct.java similarity index 94% rename from src/main/java/kitchenpos/menus/domain/MenuProduct.java rename to src/main/java/kitchenpos/core/menus/domain/MenuProduct.java index b47ca26cb..a5d5927f4 100644 --- a/src/main/java/kitchenpos/menus/domain/MenuProduct.java +++ b/src/main/java/kitchenpos/core/menus/domain/MenuProduct.java @@ -1,4 +1,4 @@ -package kitchenpos.menus.domain; +package kitchenpos.core.menus.domain; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -10,7 +10,7 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.Transient; -import kitchenpos.products.domain.Product; +import kitchenpos.core.products.domain.Product; import java.util.UUID; diff --git a/src/main/java/kitchenpos/menus/domain/MenuRepository.java b/src/main/java/kitchenpos/core/menus/domain/MenuRepository.java similarity index 88% rename from src/main/java/kitchenpos/menus/domain/MenuRepository.java rename to src/main/java/kitchenpos/core/menus/domain/MenuRepository.java index 5fbaab864..a0271adef 100644 --- a/src/main/java/kitchenpos/menus/domain/MenuRepository.java +++ b/src/main/java/kitchenpos/core/menus/domain/MenuRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.menus.domain; +package kitchenpos.core.menus.domain; import java.util.List; import java.util.Optional; diff --git a/src/main/java/kitchenpos/core/products/application/AddProduct.java b/src/main/java/kitchenpos/core/products/application/AddProduct.java new file mode 100644 index 000000000..9c889f6bb --- /dev/null +++ b/src/main/java/kitchenpos/core/products/application/AddProduct.java @@ -0,0 +1,7 @@ +package kitchenpos.core.products.application; + +import kitchenpos.core.shared.identifier.ProductId; + +public interface AddProduct { + ProductId add(CreateProductRequest request); +} diff --git a/src/main/java/kitchenpos/products/application/ProductService.java b/src/main/java/kitchenpos/core/products/application/CommandProductService.java similarity index 85% rename from src/main/java/kitchenpos/products/application/ProductService.java rename to src/main/java/kitchenpos/core/products/application/CommandProductService.java index 36f82826c..12d98158a 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/core/products/application/CommandProductService.java @@ -1,11 +1,11 @@ -package kitchenpos.products.application; +package kitchenpos.core.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.ProductRepository; -import kitchenpos.shared.domain.ProfanityChecker; +import kitchenpos.core.menus.domain.Menu; +import kitchenpos.core.menus.domain.MenuProduct; +import kitchenpos.core.menus.domain.MenuRepository; +import kitchenpos.core.products.domain.Product; +import kitchenpos.core.products.domain.ProductRepository; +import kitchenpos.core.shared.domain.ProfanityChecker; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -16,12 +16,12 @@ import java.util.UUID; @Service -public class ProductService { +public class CommandProductService { private final ProductRepository productRepository; private final MenuRepository menuRepository; private final ProfanityChecker profanityChecker; - public ProductService( + public CommandProductService( final ProductRepository productRepository, final MenuRepository menuRepository, final ProfanityChecker profanityChecker diff --git a/src/main/java/kitchenpos/core/products/application/CreateProductRequest.java b/src/main/java/kitchenpos/core/products/application/CreateProductRequest.java new file mode 100644 index 000000000..748e5cf9b --- /dev/null +++ b/src/main/java/kitchenpos/core/products/application/CreateProductRequest.java @@ -0,0 +1,12 @@ +package kitchenpos.core.products.application; + +import kitchenpos.core.products.tobe.domain.ProductName; +import kitchenpos.core.products.tobe.domain.ProductPrice; +import kitchenpos.core.shared.identifier.ProductId; + +public record CreateProductRequest( + ProductId id, + ProductName name, + ProductPrice price +) { +} diff --git a/src/main/java/kitchenpos/products/domain/JpaProductRepository.java b/src/main/java/kitchenpos/core/products/domain/JpaProductRepository.java similarity index 81% rename from src/main/java/kitchenpos/products/domain/JpaProductRepository.java rename to src/main/java/kitchenpos/core/products/domain/JpaProductRepository.java index 90b069779..990737430 100644 --- a/src/main/java/kitchenpos/products/domain/JpaProductRepository.java +++ b/src/main/java/kitchenpos/core/products/domain/JpaProductRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.products.domain; +package kitchenpos.core.products.domain; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/kitchenpos/products/domain/Product.java b/src/main/java/kitchenpos/core/products/domain/Product.java similarity index 95% rename from src/main/java/kitchenpos/products/domain/Product.java rename to src/main/java/kitchenpos/core/products/domain/Product.java index ee2a7dfa9..91a1700c7 100644 --- a/src/main/java/kitchenpos/products/domain/Product.java +++ b/src/main/java/kitchenpos/core/products/domain/Product.java @@ -1,4 +1,4 @@ -package kitchenpos.products.domain; +package kitchenpos.core.products.domain; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/kitchenpos/products/domain/ProductRepository.java b/src/main/java/kitchenpos/core/products/domain/ProductRepository.java similarity index 86% rename from src/main/java/kitchenpos/products/domain/ProductRepository.java rename to src/main/java/kitchenpos/core/products/domain/ProductRepository.java index 3637e4232..23c999d94 100644 --- a/src/main/java/kitchenpos/products/domain/ProductRepository.java +++ b/src/main/java/kitchenpos/core/products/domain/ProductRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.products.domain; +package kitchenpos.core.products.domain; import java.util.List; import java.util.Optional; diff --git a/src/main/java/kitchenpos/products/tobe/domain/Product.java b/src/main/java/kitchenpos/core/products/tobe/domain/Product.java similarity index 87% rename from src/main/java/kitchenpos/products/tobe/domain/Product.java rename to src/main/java/kitchenpos/core/products/tobe/domain/Product.java index 074efa1a2..99b57b364 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/Product.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/Product.java @@ -1,12 +1,10 @@ -package kitchenpos.products.tobe.domain; +package kitchenpos.core.products.tobe.domain; import jakarta.persistence.*; -import kitchenpos.shared.domain.DomainEntity; -import kitchenpos.shared.domain.Money; +import kitchenpos.core.shared.domain.DomainEntity; +import kitchenpos.core.shared.identifier.ProductId; -import java.math.BigDecimal; import java.util.Objects; -import java.util.UUID; @Table(name = "product_tobe") @Entity(name = "ProductTobe") diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/ProductIdGenerator.java b/src/main/java/kitchenpos/core/products/tobe/domain/ProductIdGenerator.java new file mode 100644 index 000000000..ed0865a5f --- /dev/null +++ b/src/main/java/kitchenpos/core/products/tobe/domain/ProductIdGenerator.java @@ -0,0 +1,7 @@ +package kitchenpos.core.products.tobe.domain; + +import kitchenpos.core.shared.identifier.ProductId; + +public interface ProductIdGenerator { + ProductId generateId(); +} diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/ProductName.java b/src/main/java/kitchenpos/core/products/tobe/domain/ProductName.java new file mode 100644 index 000000000..96bfab9c9 --- /dev/null +++ b/src/main/java/kitchenpos/core/products/tobe/domain/ProductName.java @@ -0,0 +1,39 @@ +package kitchenpos.core.products.tobe.domain; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.Transient; +import kitchenpos.core.products.tobe.domain.exception.InvalidProductNameException; +import kitchenpos.core.products.tobe.domain.support.ProductNameValidationResult; +import kitchenpos.core.shared.domain.ProfanityChecker; +import kitchenpos.core.shared.domain.ValueObject; + +@Embeddable +public class ProductName extends ValueObject { + + private String name; + + @SuppressWarnings("unused") + protected ProductName() {} + + private ProductName(String name) { + this.name = name; + } + + public static ProductName create(ProductNamePolicy policy, String name) { + ProductNameValidationResult result = policy.validate(name); + if (!result.valid()) { + throw new InvalidProductNameException((String.join("; ", result.errorMessages()))); + } + return new ProductName(name.strip()); + } + + public String getName() { + return name; + } + + @Override + @Transient + protected Object[] getEqualityFields() { + return new Object[] { name }; + } +} \ No newline at end of file diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/ProductNamePolicy.java b/src/main/java/kitchenpos/core/products/tobe/domain/ProductNamePolicy.java new file mode 100644 index 000000000..2cceacd34 --- /dev/null +++ b/src/main/java/kitchenpos/core/products/tobe/domain/ProductNamePolicy.java @@ -0,0 +1,7 @@ +package kitchenpos.core.products.tobe.domain; + +import kitchenpos.core.products.tobe.domain.support.ProductNameValidationResult; + +public interface ProductNamePolicy { + ProductNameValidationResult validate(String name); +} \ No newline at end of file diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java b/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java similarity index 73% rename from src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java rename to src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java index daeae196f..c7bff912a 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/ProductPrice.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java @@ -1,8 +1,8 @@ -package kitchenpos.products.tobe.domain; +package kitchenpos.core.products.tobe.domain; -import kitchenpos.products.tobe.domain.exception.InvalidProductPriceException; -import kitchenpos.shared.domain.Money; -import kitchenpos.shared.domain.ValueObject; +import kitchenpos.core.products.tobe.domain.exception.InvalidProductPriceException; +import kitchenpos.core.shared.value.Money; +import kitchenpos.core.shared.domain.ValueObject; public class ProductPrice extends ValueObject { private Money price; diff --git a/src/main/java/kitchenpos/products/tobe/domain/TobeProductRepository.java b/src/main/java/kitchenpos/core/products/tobe/domain/TobeProductRepository.java similarity index 80% rename from src/main/java/kitchenpos/products/tobe/domain/TobeProductRepository.java rename to src/main/java/kitchenpos/core/products/tobe/domain/TobeProductRepository.java index 93c9b52b8..631750b54 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/TobeProductRepository.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/TobeProductRepository.java @@ -1,5 +1,6 @@ -package kitchenpos.products.tobe.domain; +package kitchenpos.core.products.tobe.domain; +import kitchenpos.core.shared.identifier.ProductId; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductIdException.java b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductIdException.java similarity index 72% rename from src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductIdException.java rename to src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductIdException.java index a903e88cd..a79e4d3ee 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductIdException.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductIdException.java @@ -1,4 +1,4 @@ -package kitchenpos.products.tobe.domain.exception; +package kitchenpos.core.products.tobe.domain.exception; public class InvalidProductIdException extends IllegalArgumentException { public InvalidProductIdException(String s) { diff --git a/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductNameException.java b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductNameException.java similarity index 73% rename from src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductNameException.java rename to src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductNameException.java index f69088626..e18e5f966 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductNameException.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductNameException.java @@ -1,4 +1,4 @@ -package kitchenpos.products.tobe.domain.exception; +package kitchenpos.core.products.tobe.domain.exception; public class InvalidProductNameException extends IllegalArgumentException { public InvalidProductNameException(String s) { diff --git a/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductPriceException.java b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductPriceException.java similarity index 73% rename from src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductPriceException.java rename to src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductPriceException.java index 612778fa5..76adb7153 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/exception/InvalidProductPriceException.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductPriceException.java @@ -1,4 +1,4 @@ -package kitchenpos.products.tobe.domain.exception; +package kitchenpos.core.products.tobe.domain.exception; public class InvalidProductPriceException extends IllegalArgumentException { public InvalidProductPriceException(String s) { diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/support/DefaultProductNamePolicy.java b/src/main/java/kitchenpos/core/products/tobe/domain/support/DefaultProductNamePolicy.java new file mode 100644 index 000000000..2c56cfa7a --- /dev/null +++ b/src/main/java/kitchenpos/core/products/tobe/domain/support/DefaultProductNamePolicy.java @@ -0,0 +1,30 @@ +package kitchenpos.core.products.tobe.domain.support; + +import kitchenpos.core.products.tobe.domain.ProductNamePolicy; +import kitchenpos.core.shared.domain.ProfanityChecker; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class DefaultProductNamePolicy implements ProductNamePolicy { + + private final ProfanityChecker profanityChecker; + + public DefaultProductNamePolicy(ProfanityChecker profanityChecker) { + this.profanityChecker = profanityChecker; + } + + @Override + public ProductNameValidationResult validate(String name) { + List errors = new ArrayList<>(); + if (name == null || name.isBlank()) { + errors.add("Product name은 null 이거나 빈 값이 될 수 없습니다."); + } + if (name != null && profanityChecker.containsProfanity(name)) { + errors.add("Product name에 비속어가 포함될 수 없습니다."); + } + return new ProductNameValidationResult(errors.isEmpty(), errors); + } +} \ No newline at end of file diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/support/ProductNameValidationResult.java b/src/main/java/kitchenpos/core/products/tobe/domain/support/ProductNameValidationResult.java new file mode 100644 index 000000000..a2b20be4d --- /dev/null +++ b/src/main/java/kitchenpos/core/products/tobe/domain/support/ProductNameValidationResult.java @@ -0,0 +1,5 @@ +package kitchenpos.core.products.tobe.domain.support; + +import java.util.List; + +public record ProductNameValidationResult(boolean valid, List errorMessages) {} diff --git a/src/main/java/kitchenpos/products/tobe/domain/support/UUIDBasedProductIdGenerator.java b/src/main/java/kitchenpos/core/products/tobe/domain/support/UUIDBasedProductIdGenerator.java similarity index 61% rename from src/main/java/kitchenpos/products/tobe/domain/support/UUIDBasedProductIdGenerator.java rename to src/main/java/kitchenpos/core/products/tobe/domain/support/UUIDBasedProductIdGenerator.java index e5af40abd..27fb8808b 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/support/UUIDBasedProductIdGenerator.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/support/UUIDBasedProductIdGenerator.java @@ -1,7 +1,7 @@ -package kitchenpos.products.tobe.domain.support; +package kitchenpos.core.products.tobe.domain.support; -import kitchenpos.products.tobe.domain.ProductId; -import kitchenpos.products.tobe.domain.ProductIdGenerator; +import kitchenpos.core.shared.identifier.ProductId; +import kitchenpos.core.products.tobe.domain.ProductIdGenerator; import org.springframework.stereotype.Component; import java.util.UUID; diff --git a/src/main/java/kitchenpos/shared/infra/DefaultProfanityChecker.java b/src/main/java/kitchenpos/core/shared/data/DefaultProfanityChecker.java similarity index 90% rename from src/main/java/kitchenpos/shared/infra/DefaultProfanityChecker.java rename to src/main/java/kitchenpos/core/shared/data/DefaultProfanityChecker.java index b955ca38b..e1df5dd07 100644 --- a/src/main/java/kitchenpos/shared/infra/DefaultProfanityChecker.java +++ b/src/main/java/kitchenpos/core/shared/data/DefaultProfanityChecker.java @@ -1,6 +1,6 @@ -package kitchenpos.shared.infra; +package kitchenpos.core.shared.data; -import kitchenpos.shared.domain.ProfanityChecker; +import kitchenpos.core.shared.domain.ProfanityChecker; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; diff --git a/src/main/java/kitchenpos/shared/infra/jpa/MoneyConverter.java b/src/main/java/kitchenpos/core/shared/data/jpa/MoneyConverter.java similarity index 83% rename from src/main/java/kitchenpos/shared/infra/jpa/MoneyConverter.java rename to src/main/java/kitchenpos/core/shared/data/jpa/MoneyConverter.java index 7d8df4a94..10b43f2dc 100644 --- a/src/main/java/kitchenpos/shared/infra/jpa/MoneyConverter.java +++ b/src/main/java/kitchenpos/core/shared/data/jpa/MoneyConverter.java @@ -1,8 +1,8 @@ -package kitchenpos.shared.infra.jpa; +package kitchenpos.core.shared.data.jpa; import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; -import kitchenpos.shared.domain.Money; +import kitchenpos.core.shared.value.Money; @Converter(autoApply = true) public class MoneyConverter implements AttributeConverter { diff --git a/src/main/java/kitchenpos/shared/domain/DomainEntity.java b/src/main/java/kitchenpos/core/shared/domain/DomainEntity.java similarity index 95% rename from src/main/java/kitchenpos/shared/domain/DomainEntity.java rename to src/main/java/kitchenpos/core/shared/domain/DomainEntity.java index 0783574ac..a52614e62 100644 --- a/src/main/java/kitchenpos/shared/domain/DomainEntity.java +++ b/src/main/java/kitchenpos/core/shared/domain/DomainEntity.java @@ -1,4 +1,4 @@ -package kitchenpos.shared.domain; +package kitchenpos.core.shared.domain; import java.io.Serializable; diff --git a/src/main/java/kitchenpos/shared/domain/ProfanityChecker.java b/src/main/java/kitchenpos/core/shared/domain/ProfanityChecker.java similarity index 68% rename from src/main/java/kitchenpos/shared/domain/ProfanityChecker.java rename to src/main/java/kitchenpos/core/shared/domain/ProfanityChecker.java index d05b6c293..485154724 100644 --- a/src/main/java/kitchenpos/shared/domain/ProfanityChecker.java +++ b/src/main/java/kitchenpos/core/shared/domain/ProfanityChecker.java @@ -1,4 +1,4 @@ -package kitchenpos.shared.domain; +package kitchenpos.core.shared.domain; public interface ProfanityChecker { boolean containsProfanity(String text); diff --git a/src/main/java/kitchenpos/shared/domain/ValueObject.java b/src/main/java/kitchenpos/core/shared/domain/ValueObject.java similarity index 96% rename from src/main/java/kitchenpos/shared/domain/ValueObject.java rename to src/main/java/kitchenpos/core/shared/domain/ValueObject.java index 16f08257a..ab7961d6d 100644 --- a/src/main/java/kitchenpos/shared/domain/ValueObject.java +++ b/src/main/java/kitchenpos/core/shared/domain/ValueObject.java @@ -1,4 +1,4 @@ -package kitchenpos.shared.domain; +package kitchenpos.core.shared.domain; import java.io.Serializable; import java.util.Arrays; diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductId.java b/src/main/java/kitchenpos/core/shared/identifier/ProductId.java similarity index 83% rename from src/main/java/kitchenpos/products/tobe/domain/ProductId.java rename to src/main/java/kitchenpos/core/shared/identifier/ProductId.java index 3179f2d08..7a0213b40 100644 --- a/src/main/java/kitchenpos/products/tobe/domain/ProductId.java +++ b/src/main/java/kitchenpos/core/shared/identifier/ProductId.java @@ -1,7 +1,7 @@ -package kitchenpos.products.tobe.domain; +package kitchenpos.core.shared.identifier; import jakarta.persistence.Embeddable; -import kitchenpos.products.tobe.domain.exception.InvalidProductIdException; +import kitchenpos.core.products.tobe.domain.exception.InvalidProductIdException; import java.io.Serializable; diff --git a/src/main/java/kitchenpos/shared/domain/Money.java b/src/main/java/kitchenpos/core/shared/value/Money.java similarity index 60% rename from src/main/java/kitchenpos/shared/domain/Money.java rename to src/main/java/kitchenpos/core/shared/value/Money.java index db8c425f1..749d0aab6 100644 --- a/src/main/java/kitchenpos/shared/domain/Money.java +++ b/src/main/java/kitchenpos/core/shared/value/Money.java @@ -1,4 +1,6 @@ -package kitchenpos.shared.domain; +package kitchenpos.core.shared.value; + +import kitchenpos.core.shared.domain.ValueObject; import java.math.BigDecimal; @@ -11,13 +13,13 @@ public static Money wons(long amount) { return new Money(BigDecimal.valueOf(amount)); } - public static Money wons(double amount) { - return new Money(BigDecimal.valueOf(amount)); - } - private Money(BigDecimal amount) { - if(amount == null || amount.compareTo(BigDecimal.ZERO) < 0) { - throw new IllegalArgumentException("금액은 null 이거나 0보다 작을 수 없습니다."); + if (amount == null) { + throw new IllegalArgumentException("금액은 null이 될 수 없습니다."); + } + + if (amount.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("금액은 0보다 작을 수 없습니다. 입력된 금액: " + amount); } this.amount = amount; } @@ -26,14 +28,6 @@ public Money add(Money other) { return new Money(this.amount.add(other.amount)); } - public Money subtract(Money other) { - return new Money(this.amount.subtract(other.amount)); - } - - public boolean isGreaterThanOrEqual(Money other) { - return this.amount.compareTo(other.amount) >= 0; - } - public boolean isLessThan(Money other) { return this.amount.compareTo(other.amount) < 0; } @@ -52,4 +46,4 @@ public String toString() { return amount.toString() + "원"; } -} \ No newline at end of file +} diff --git a/src/main/java/kitchenpos/takeoutorders/empty.txt b/src/main/java/kitchenpos/core/takeoutorders/empty.txt similarity index 100% rename from src/main/java/kitchenpos/takeoutorders/empty.txt rename to src/main/java/kitchenpos/core/takeoutorders/empty.txt diff --git a/src/main/java/kitchenpos/deliveryorders/infra/DefaultKitchenridersClient.java b/src/main/java/kitchenpos/data/DefaultKitchenridersClient.java similarity index 88% rename from src/main/java/kitchenpos/deliveryorders/infra/DefaultKitchenridersClient.java rename to src/main/java/kitchenpos/data/DefaultKitchenridersClient.java index 31d6d8cee..bd7a74bd4 100644 --- a/src/main/java/kitchenpos/deliveryorders/infra/DefaultKitchenridersClient.java +++ b/src/main/java/kitchenpos/data/DefaultKitchenridersClient.java @@ -1,4 +1,4 @@ -package kitchenpos.deliveryorders.infra; +package kitchenpos.data; import org.springframework.stereotype.Component; diff --git a/src/main/java/kitchenpos/deliveryorders/infra/KitchenridersClient.java b/src/main/java/kitchenpos/data/KitchenridersClient.java similarity index 81% rename from src/main/java/kitchenpos/deliveryorders/infra/KitchenridersClient.java rename to src/main/java/kitchenpos/data/KitchenridersClient.java index 0c8278791..cd61969f9 100644 --- a/src/main/java/kitchenpos/deliveryorders/infra/KitchenridersClient.java +++ b/src/main/java/kitchenpos/data/KitchenridersClient.java @@ -1,4 +1,4 @@ -package kitchenpos.deliveryorders.infra; +package kitchenpos.data; import java.math.BigDecimal; import java.util.UUID; diff --git a/src/main/java/kitchenpos/data/jpa/TobeJpaProductRepository.java b/src/main/java/kitchenpos/data/jpa/TobeJpaProductRepository.java new file mode 100644 index 000000000..08dfc7ce4 --- /dev/null +++ b/src/main/java/kitchenpos/data/jpa/TobeJpaProductRepository.java @@ -0,0 +1,9 @@ +package kitchenpos.data.jpa; + +import kitchenpos.core.products.tobe.domain.Product; +import kitchenpos.core.shared.identifier.ProductId; +import kitchenpos.core.products.tobe.domain.TobeProductRepository; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TobeJpaProductRepository extends TobeProductRepository, JpaRepository { +} diff --git a/src/main/java/kitchenpos/products/infra/TobeJpaProductRepository.java b/src/main/java/kitchenpos/products/infra/TobeJpaProductRepository.java deleted file mode 100644 index 4370bc5f2..000000000 --- a/src/main/java/kitchenpos/products/infra/TobeJpaProductRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package kitchenpos.products.infra; - -import kitchenpos.products.tobe.domain.Product; -import kitchenpos.products.tobe.domain.ProductId; -import kitchenpos.products.tobe.domain.TobeProductRepository; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface TobeJpaProductRepository extends TobeProductRepository, JpaRepository { -} diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductIdGenerator.java b/src/main/java/kitchenpos/products/tobe/domain/ProductIdGenerator.java deleted file mode 100644 index 9e8478396..000000000 --- a/src/main/java/kitchenpos/products/tobe/domain/ProductIdGenerator.java +++ /dev/null @@ -1,5 +0,0 @@ -package kitchenpos.products.tobe.domain; - -public interface ProductIdGenerator { - ProductId generateId(); -} diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductName.java b/src/main/java/kitchenpos/products/tobe/domain/ProductName.java deleted file mode 100644 index 94de10d7f..000000000 --- a/src/main/java/kitchenpos/products/tobe/domain/ProductName.java +++ /dev/null @@ -1,42 +0,0 @@ -package kitchenpos.products.tobe.domain; - -import jakarta.persistence.Embeddable; -import jakarta.persistence.Transient; -import kitchenpos.products.tobe.domain.exception.InvalidProductNameException; -import kitchenpos.shared.domain.ProfanityChecker; -import kitchenpos.shared.domain.ValueObject; - -@Embeddable -public class ProductName extends ValueObject { - - private String name; - - @SuppressWarnings("unused") - protected ProductName() {} - - // 외부에서 직접 호출할 수 없는 private 생성자: 검증된 이름만 받음 - private ProductName(String name) { - this.name = name; - } - - public static ProductName create(ProfanityChecker profanityChecker, String name) { - if (name == null || name.isBlank()) { - throw new InvalidProductNameException("Product name 은 null 이거나 빈 값이 될 수 없습니다."); - } - - if (profanityChecker.containsProfanity(name)) { - throw new InvalidProductNameException("Product name 에 비속어가 포함될 수 없습니다."); - } - return new ProductName(name.strip()); - } - - public String getName() { - return name; - } - - @Override - @Transient - protected Object[] getEqualityFields() { - return new Object[] { name }; - } -} \ No newline at end of file diff --git a/src/main/java/kitchenpos/menus/ui/MenuGroupRestController.java b/src/main/java/kitchenpos/web/MenuGroupRestController.java similarity index 89% rename from src/main/java/kitchenpos/menus/ui/MenuGroupRestController.java rename to src/main/java/kitchenpos/web/MenuGroupRestController.java index 30c38b4a1..6b4387a0b 100644 --- a/src/main/java/kitchenpos/menus/ui/MenuGroupRestController.java +++ b/src/main/java/kitchenpos/web/MenuGroupRestController.java @@ -1,7 +1,7 @@ -package kitchenpos.menus.ui; +package kitchenpos.web; -import kitchenpos.menus.application.MenuGroupService; -import kitchenpos.menus.domain.MenuGroup; +import kitchenpos.core.menus.application.MenuGroupService; +import kitchenpos.core.menus.domain.MenuGroup; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; diff --git a/src/main/java/kitchenpos/menus/ui/MenuRestController.java b/src/main/java/kitchenpos/web/MenuRestController.java similarity index 93% rename from src/main/java/kitchenpos/menus/ui/MenuRestController.java rename to src/main/java/kitchenpos/web/MenuRestController.java index 626b214fc..0fab1ddf0 100644 --- a/src/main/java/kitchenpos/menus/ui/MenuRestController.java +++ b/src/main/java/kitchenpos/web/MenuRestController.java @@ -1,7 +1,7 @@ -package kitchenpos.menus.ui; +package kitchenpos.web; -import kitchenpos.menus.application.MenuService; -import kitchenpos.menus.domain.Menu; +import kitchenpos.core.menus.application.MenuService; +import kitchenpos.core.menus.domain.Menu; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; diff --git a/src/main/java/kitchenpos/eatinorders/ui/OrderRestController.java b/src/main/java/kitchenpos/web/OrderRestController.java similarity index 94% rename from src/main/java/kitchenpos/eatinorders/ui/OrderRestController.java rename to src/main/java/kitchenpos/web/OrderRestController.java index dbb5568ad..5dce78a22 100644 --- a/src/main/java/kitchenpos/eatinorders/ui/OrderRestController.java +++ b/src/main/java/kitchenpos/web/OrderRestController.java @@ -1,7 +1,7 @@ -package kitchenpos.eatinorders.ui; +package kitchenpos.web; -import kitchenpos.eatinorders.application.OrderService; -import kitchenpos.eatinorders.domain.Order; +import kitchenpos.core.eatinorders.application.OrderService; +import kitchenpos.core.eatinorders.domain.Order; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; diff --git a/src/main/java/kitchenpos/eatinorders/ui/OrderTableRestController.java b/src/main/java/kitchenpos/web/OrderTableRestController.java similarity index 93% rename from src/main/java/kitchenpos/eatinorders/ui/OrderTableRestController.java rename to src/main/java/kitchenpos/web/OrderTableRestController.java index 1ceabe86f..af9f72089 100644 --- a/src/main/java/kitchenpos/eatinorders/ui/OrderTableRestController.java +++ b/src/main/java/kitchenpos/web/OrderTableRestController.java @@ -1,7 +1,7 @@ -package kitchenpos.eatinorders.ui; +package kitchenpos.web; -import kitchenpos.eatinorders.application.OrderTableService; -import kitchenpos.eatinorders.domain.OrderTable; +import kitchenpos.core.eatinorders.application.OrderTableService; +import kitchenpos.core.eatinorders.domain.OrderTable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; diff --git a/src/main/java/kitchenpos/products/ui/ProductRestController.java b/src/main/java/kitchenpos/web/ProductRestController.java similarity index 83% rename from src/main/java/kitchenpos/products/ui/ProductRestController.java rename to src/main/java/kitchenpos/web/ProductRestController.java index c71c795a4..cfcd67c49 100644 --- a/src/main/java/kitchenpos/products/ui/ProductRestController.java +++ b/src/main/java/kitchenpos/web/ProductRestController.java @@ -1,7 +1,7 @@ -package kitchenpos.products.ui; +package kitchenpos.web; -import kitchenpos.products.application.ProductService; -import kitchenpos.products.domain.Product; +import kitchenpos.core.products.application.CommandProductService; +import kitchenpos.core.products.domain.Product; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -18,9 +18,9 @@ @RequestMapping("/api/products") @RestController public class ProductRestController { - private final ProductService productService; + private final CommandProductService productService; - public ProductRestController(final ProductService productService) { + public ProductRestController(final CommandProductService productService) { this.productService = productService; } diff --git a/src/test/java/kitchenpos/Fixtures.java b/src/test/java/kitchenpos/Fixtures.java index 434768a52..6748197ed 100644 --- a/src/test/java/kitchenpos/Fixtures.java +++ b/src/test/java/kitchenpos/Fixtures.java @@ -1,14 +1,14 @@ package kitchenpos; -import kitchenpos.eatinorders.domain.Order; -import kitchenpos.eatinorders.domain.OrderLineItem; -import kitchenpos.eatinorders.domain.OrderStatus; -import kitchenpos.eatinorders.domain.OrderTable; -import kitchenpos.eatinorders.domain.OrderType; -import kitchenpos.menus.domain.Menu; -import kitchenpos.menus.domain.MenuGroup; -import kitchenpos.menus.domain.MenuProduct; -import kitchenpos.products.domain.Product; +import kitchenpos.core.eatinorders.domain.Order; +import kitchenpos.core.eatinorders.domain.OrderLineItem; +import kitchenpos.core.eatinorders.domain.OrderStatus; +import kitchenpos.core.eatinorders.domain.OrderTable; +import kitchenpos.core.eatinorders.domain.OrderType; +import kitchenpos.core.menus.domain.Menu; +import kitchenpos.core.menus.domain.MenuGroup; +import kitchenpos.core.menus.domain.MenuProduct; +import kitchenpos.core.products.domain.Product; import java.math.BigDecimal; import java.time.LocalDateTime; diff --git a/src/test/java/kitchenpos/eatinorders/application/FakeKitchenridersClient.java b/src/test/java/kitchenpos/eatinorders/application/FakeKitchenridersClient.java index 301e1377b..5221ba4bd 100644 --- a/src/test/java/kitchenpos/eatinorders/application/FakeKitchenridersClient.java +++ b/src/test/java/kitchenpos/eatinorders/application/FakeKitchenridersClient.java @@ -1,6 +1,6 @@ package kitchenpos.eatinorders.application; -import kitchenpos.deliveryorders.infra.KitchenridersClient; +import kitchenpos.data.KitchenridersClient; import java.math.BigDecimal; import java.util.UUID; diff --git a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderRepository.java b/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderRepository.java index 85f27e38d..fc333e546 100644 --- a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderRepository.java +++ b/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderRepository.java @@ -1,9 +1,9 @@ package kitchenpos.eatinorders.application; -import kitchenpos.eatinorders.domain.Order; -import kitchenpos.eatinorders.domain.OrderRepository; -import kitchenpos.eatinorders.domain.OrderStatus; -import kitchenpos.eatinorders.domain.OrderTable; +import kitchenpos.core.eatinorders.domain.Order; +import kitchenpos.core.eatinorders.domain.OrderRepository; +import kitchenpos.core.eatinorders.domain.OrderStatus; +import kitchenpos.core.eatinorders.domain.OrderTable; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderTableRepository.java b/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderTableRepository.java index 663de6289..73c13c1ee 100644 --- a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderTableRepository.java +++ b/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderTableRepository.java @@ -1,7 +1,7 @@ package kitchenpos.eatinorders.application; -import kitchenpos.eatinorders.domain.OrderTable; -import kitchenpos.eatinorders.domain.OrderTableRepository; +import kitchenpos.core.eatinorders.domain.OrderTable; +import kitchenpos.core.eatinorders.domain.OrderTableRepository; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java b/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java index 8701fcaef..4887f36df 100644 --- a/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java +++ b/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java @@ -1,14 +1,15 @@ package kitchenpos.eatinorders.application; -import kitchenpos.eatinorders.domain.Order; -import kitchenpos.eatinorders.domain.OrderLineItem; -import kitchenpos.eatinorders.domain.OrderRepository; -import kitchenpos.eatinorders.domain.OrderStatus; -import kitchenpos.eatinorders.domain.OrderTable; -import kitchenpos.eatinorders.domain.OrderTableRepository; -import kitchenpos.eatinorders.domain.OrderType; +import kitchenpos.core.eatinorders.application.OrderService; +import kitchenpos.core.eatinorders.domain.Order; +import kitchenpos.core.eatinorders.domain.OrderLineItem; +import kitchenpos.core.eatinorders.domain.OrderRepository; +import kitchenpos.core.eatinorders.domain.OrderStatus; +import kitchenpos.core.eatinorders.domain.OrderTable; +import kitchenpos.core.eatinorders.domain.OrderTableRepository; +import kitchenpos.core.eatinorders.domain.OrderType; import kitchenpos.menus.application.InMemoryMenuRepository; -import kitchenpos.menus.domain.MenuRepository; +import kitchenpos.core.menus.domain.MenuRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java b/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java index 01551a8d4..189497553 100644 --- a/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java +++ b/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java @@ -1,9 +1,10 @@ package kitchenpos.eatinorders.application; -import kitchenpos.eatinorders.domain.OrderRepository; -import kitchenpos.eatinorders.domain.OrderStatus; -import kitchenpos.eatinorders.domain.OrderTable; -import kitchenpos.eatinorders.domain.OrderTableRepository; +import kitchenpos.core.eatinorders.application.OrderTableService; +import kitchenpos.core.eatinorders.domain.OrderRepository; +import kitchenpos.core.eatinorders.domain.OrderStatus; +import kitchenpos.core.eatinorders.domain.OrderTable; +import kitchenpos.core.eatinorders.domain.OrderTableRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/kitchenpos/menus/application/InMemoryMenuGroupRepository.java b/src/test/java/kitchenpos/menus/application/InMemoryMenuGroupRepository.java index 85962e6dc..9f7cae8d9 100644 --- a/src/test/java/kitchenpos/menus/application/InMemoryMenuGroupRepository.java +++ b/src/test/java/kitchenpos/menus/application/InMemoryMenuGroupRepository.java @@ -1,7 +1,7 @@ package kitchenpos.menus.application; -import kitchenpos.menus.domain.MenuGroup; -import kitchenpos.menus.domain.MenuGroupRepository; +import kitchenpos.core.menus.domain.MenuGroup; +import kitchenpos.core.menus.domain.MenuGroupRepository; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java b/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java index ca30b679c..2336a1790 100644 --- a/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java +++ b/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java @@ -1,7 +1,7 @@ package kitchenpos.menus.application; -import kitchenpos.menus.domain.Menu; -import kitchenpos.menus.domain.MenuRepository; +import kitchenpos.core.menus.domain.Menu; +import kitchenpos.core.menus.domain.MenuRepository; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java b/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java index a5fbc71d1..462c80b2d 100644 --- a/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java +++ b/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java @@ -1,7 +1,8 @@ package kitchenpos.menus.application; -import kitchenpos.menus.domain.MenuGroup; -import kitchenpos.menus.domain.MenuGroupRepository; +import kitchenpos.core.menus.application.MenuGroupService; +import kitchenpos.core.menus.domain.MenuGroup; +import kitchenpos.core.menus.domain.MenuGroupRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java index 64f2222d8..b475e1236 100644 --- a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java +++ b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java @@ -1,14 +1,15 @@ package kitchenpos.menus.application; -import kitchenpos.menus.domain.Menu; -import kitchenpos.menus.domain.MenuGroupRepository; -import kitchenpos.menus.domain.MenuProduct; -import kitchenpos.menus.domain.MenuRepository; +import kitchenpos.core.menus.application.MenuService; +import kitchenpos.core.menus.domain.Menu; +import kitchenpos.core.menus.domain.MenuGroupRepository; +import kitchenpos.core.menus.domain.MenuProduct; +import kitchenpos.core.menus.domain.MenuRepository; import kitchenpos.products.application.FakeProfanityChecker; import kitchenpos.products.application.InMemoryProductRepository; -import kitchenpos.products.domain.Product; -import kitchenpos.products.domain.ProductRepository; -import kitchenpos.shared.domain.ProfanityChecker; +import kitchenpos.core.products.domain.Product; +import kitchenpos.core.products.domain.ProductRepository; +import kitchenpos.core.shared.domain.ProfanityChecker; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/kitchenpos/products/application/FakeProfanityChecker.java b/src/test/java/kitchenpos/products/application/FakeProfanityChecker.java index d70e6c089..cd8f1a007 100644 --- a/src/test/java/kitchenpos/products/application/FakeProfanityChecker.java +++ b/src/test/java/kitchenpos/products/application/FakeProfanityChecker.java @@ -1,6 +1,6 @@ package kitchenpos.products.application; -import kitchenpos.shared.domain.ProfanityChecker; +import kitchenpos.core.shared.domain.ProfanityChecker; import java.util.Arrays; import java.util.List; diff --git a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java index b55c5ec5e..8adc5be25 100644 --- a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java +++ b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java @@ -1,7 +1,7 @@ package kitchenpos.products.application; -import kitchenpos.products.domain.Product; -import kitchenpos.products.domain.ProductRepository; +import kitchenpos.core.products.domain.Product; +import kitchenpos.core.products.domain.ProductRepository; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/test/java/kitchenpos/products/application/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/ProductServiceTest.java index 826c2ada0..b263bd68b 100644 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/application/ProductServiceTest.java @@ -1,11 +1,12 @@ package kitchenpos.products.application; +import kitchenpos.core.products.application.CommandProductService; import kitchenpos.menus.application.InMemoryMenuRepository; -import kitchenpos.menus.domain.Menu; -import kitchenpos.menus.domain.MenuRepository; -import kitchenpos.products.domain.Product; -import kitchenpos.products.domain.ProductRepository; -import kitchenpos.shared.domain.ProfanityChecker; +import kitchenpos.core.menus.domain.Menu; +import kitchenpos.core.menus.domain.MenuRepository; +import kitchenpos.core.products.domain.Product; +import kitchenpos.core.products.domain.ProductRepository; +import kitchenpos.core.shared.domain.ProfanityChecker; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -28,14 +29,14 @@ class ProductServiceTest { private ProductRepository productRepository; private MenuRepository menuRepository; private ProfanityChecker profanityChecker; - private ProductService productService; + private CommandProductService productService; @BeforeEach void setUp() { productRepository = new InMemoryProductRepository(); menuRepository = new InMemoryMenuRepository(); profanityChecker = new FakeProfanityChecker(); - productService = new ProductService(productRepository, menuRepository, profanityChecker); + productService = new CommandProductService(productRepository, menuRepository, profanityChecker); } @DisplayName("상품을 등록할 수 있다.") From 956a3f4f575e031d00b46d0ce77a1c4936f881ac Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Fri, 28 Feb 2025 17:08:15 +0900 Subject: [PATCH 09/14] =?UTF-8?q?refactor(product):=20Service=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../foundation/exception/SystemException.java | 34 ++++++++ .../kitchenpos/core/menus/domain/Menu.java | 13 ++- .../core/menus/domain/MenuProduct.java | 19 +++-- .../core/menus/domain/MenuRepository.java | 4 +- .../core/products/application/AddProduct.java | 5 +- .../application/ChangeProductPrice.java | 10 +++ .../application/CommandProductService.java | 84 ++++++------------- .../products/application/FindProducts.java | 9 ++ .../application/QueryProductService.java | 22 +++++ .../{ => dto}/CreateProductRequest.java | 2 +- .../core/products/tobe/domain/Product.java | 12 ++- .../products/tobe/domain/ProductPrice.java | 10 +++ .../exception/InvalidProductIdException.java | 6 +- .../InvalidProductNameException.java | 2 +- .../InvalidProductPriceException.java | 2 +- .../domain/exception/ProductException.java | 14 ++++ .../exception/ProductNotFoundException.java | 17 ++++ .../kitchenpos/core/shared/value/Money.java | 7 ++ .../core/shared/value/Quantity.java | 31 +++++++ 19 files changed, 225 insertions(+), 78 deletions(-) create mode 100644 src/main/java/kitchenpos/core/foundation/exception/SystemException.java create mode 100644 src/main/java/kitchenpos/core/products/application/ChangeProductPrice.java create mode 100644 src/main/java/kitchenpos/core/products/application/FindProducts.java create mode 100644 src/main/java/kitchenpos/core/products/application/QueryProductService.java rename src/main/java/kitchenpos/core/products/application/{ => dto}/CreateProductRequest.java (85%) create mode 100644 src/main/java/kitchenpos/core/products/tobe/domain/exception/ProductException.java create mode 100644 src/main/java/kitchenpos/core/products/tobe/domain/exception/ProductNotFoundException.java create mode 100644 src/main/java/kitchenpos/core/shared/value/Quantity.java diff --git a/src/main/java/kitchenpos/core/foundation/exception/SystemException.java b/src/main/java/kitchenpos/core/foundation/exception/SystemException.java new file mode 100644 index 000000000..938f353cb --- /dev/null +++ b/src/main/java/kitchenpos/core/foundation/exception/SystemException.java @@ -0,0 +1,34 @@ +package kitchenpos.core.foundation.exception; + +import org.springframework.context.MessageSourceResolvable; + +public class SystemException extends RuntimeException implements MessageSourceResolvable { + + public SystemException(String format, Object... args) { + super(String.format(format, args)); + } + + public SystemException(Throwable cause) { + super(cause); + } + + public SystemException(String message, Throwable cause) { + super(message, cause); + } + + @Override + public String[] getCodes() { + return new String[]{"Exception." + getClass().getSimpleName()}; + } + + @Override + public Object[] getArguments() { + return new Object[0]; + } + + @Override + public String getDefaultMessage() { + return getMessage(); + } + +} diff --git a/src/main/java/kitchenpos/core/menus/domain/Menu.java b/src/main/java/kitchenpos/core/menus/domain/Menu.java index e47a7aaa3..192ecba5e 100644 --- a/src/main/java/kitchenpos/core/menus/domain/Menu.java +++ b/src/main/java/kitchenpos/core/menus/domain/Menu.java @@ -10,6 +10,7 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import jakarta.persistence.Transient; +import kitchenpos.core.shared.value.Money; import java.math.BigDecimal; import java.util.List; @@ -26,7 +27,7 @@ public class Menu { private String name; @Column(name = "price", nullable = false) - private BigDecimal price; + private Money price; @ManyToOne(optional = false) @JoinColumn( @@ -54,6 +55,16 @@ public class Menu { public Menu() { } + public void recalculateDisplayStatus() { + Money sum = menuProducts.stream() + .map(MenuProduct::calculatePrice) + .reduce(Money.ZERO, Money::add); + // 메뉴 가격이 구성 상품의 총합보다 높으면 표시 안 함 + if (this.price.isBiggerThan(sum)) { + this.displayed = false; + } + } + public UUID getId() { return id; } diff --git a/src/main/java/kitchenpos/core/menus/domain/MenuProduct.java b/src/main/java/kitchenpos/core/menus/domain/MenuProduct.java index a5d5927f4..19d4042f0 100644 --- a/src/main/java/kitchenpos/core/menus/domain/MenuProduct.java +++ b/src/main/java/kitchenpos/core/menus/domain/MenuProduct.java @@ -10,8 +10,12 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.Transient; -import kitchenpos.core.products.domain.Product; +import kitchenpos.core.products.tobe.domain.Product; +import kitchenpos.core.products.tobe.domain.ProductPrice; +import kitchenpos.core.shared.value.Money; +import kitchenpos.core.shared.value.Quantity; +import java.math.BigDecimal; import java.util.UUID; @Table(name = "menu_product") @@ -28,10 +32,10 @@ public class MenuProduct { columnDefinition = "binary(16)", foreignKey = @ForeignKey(name = "fk_menu_product_to_product") ) - private Product product; + private kitchenpos.core.products.tobe.domain.Product product; @Column(name = "quantity", nullable = false) - private long quantity; + private Quantity quantity; @Transient private UUID productId; @@ -55,11 +59,11 @@ public void setProduct(final Product product) { this.product = product; } - public long getQuantity() { + public Quantity getQuantity() { return quantity; } - public void setQuantity(final long quantity) { + public void setQuantity(final Quantity quantity) { this.quantity = quantity; } @@ -70,4 +74,9 @@ public UUID getProductId() { public void setProductId(final UUID productId) { this.productId = productId; } + + public Money calculatePrice() { + return product.getPrice().multiply(quantity); + } + } diff --git a/src/main/java/kitchenpos/core/menus/domain/MenuRepository.java b/src/main/java/kitchenpos/core/menus/domain/MenuRepository.java index a0271adef..7ebb077c9 100644 --- a/src/main/java/kitchenpos/core/menus/domain/MenuRepository.java +++ b/src/main/java/kitchenpos/core/menus/domain/MenuRepository.java @@ -1,5 +1,7 @@ package kitchenpos.core.menus.domain; +import kitchenpos.core.shared.identifier.ProductId; + import java.util.List; import java.util.Optional; import java.util.UUID; @@ -13,6 +15,6 @@ public interface MenuRepository { List findAllByIdIn(List ids); - List findAllByProductId(UUID productId); + List findAllByProductId(ProductId productId); } diff --git a/src/main/java/kitchenpos/core/products/application/AddProduct.java b/src/main/java/kitchenpos/core/products/application/AddProduct.java index 9c889f6bb..4d28e176b 100644 --- a/src/main/java/kitchenpos/core/products/application/AddProduct.java +++ b/src/main/java/kitchenpos/core/products/application/AddProduct.java @@ -1,7 +1,8 @@ package kitchenpos.core.products.application; -import kitchenpos.core.shared.identifier.ProductId; +import kitchenpos.core.products.application.dto.CreateProductRequest; +import kitchenpos.core.products.tobe.domain.Product; public interface AddProduct { - ProductId add(CreateProductRequest request); + Product addProduct(CreateProductRequest request); } diff --git a/src/main/java/kitchenpos/core/products/application/ChangeProductPrice.java b/src/main/java/kitchenpos/core/products/application/ChangeProductPrice.java new file mode 100644 index 000000000..efd4f8825 --- /dev/null +++ b/src/main/java/kitchenpos/core/products/application/ChangeProductPrice.java @@ -0,0 +1,10 @@ +package kitchenpos.core.products.application; + +import kitchenpos.core.products.tobe.domain.Product; +import kitchenpos.core.products.tobe.domain.ProductPrice; +import kitchenpos.core.products.tobe.domain.exception.ProductNotFoundException; +import kitchenpos.core.shared.identifier.ProductId; + +public interface ChangeProductPrice { + Product changePrice(final ProductId productId, final ProductPrice request) throws ProductNotFoundException; +} diff --git a/src/main/java/kitchenpos/core/products/application/CommandProductService.java b/src/main/java/kitchenpos/core/products/application/CommandProductService.java index 12d98158a..ec33cce73 100644 --- a/src/main/java/kitchenpos/core/products/application/CommandProductService.java +++ b/src/main/java/kitchenpos/core/products/application/CommandProductService.java @@ -1,81 +1,49 @@ package kitchenpos.core.products.application; import kitchenpos.core.menus.domain.Menu; -import kitchenpos.core.menus.domain.MenuProduct; import kitchenpos.core.menus.domain.MenuRepository; -import kitchenpos.core.products.domain.Product; -import kitchenpos.core.products.domain.ProductRepository; -import kitchenpos.core.shared.domain.ProfanityChecker; +import kitchenpos.core.products.application.dto.CreateProductRequest; +import kitchenpos.core.products.tobe.domain.Product; +import kitchenpos.core.products.tobe.domain.ProductPrice; +import kitchenpos.core.products.tobe.domain.TobeProductRepository; +import kitchenpos.core.products.tobe.domain.exception.ProductNotFoundException; +import kitchenpos.core.shared.identifier.ProductId; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.math.BigDecimal; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.UUID; - @Service -public class CommandProductService { - private final ProductRepository productRepository; +public class CommandProductService implements AddProduct, ChangeProductPrice { + private final TobeProductRepository tobeProductRepository; private final MenuRepository menuRepository; - private final ProfanityChecker profanityChecker; public CommandProductService( - final ProductRepository productRepository, - final MenuRepository menuRepository, - final ProfanityChecker profanityChecker + final TobeProductRepository tobeProductRepository, + final MenuRepository menuRepository ) { - this.productRepository = productRepository; + this.tobeProductRepository = tobeProductRepository; this.menuRepository = menuRepository; - this.profanityChecker = profanityChecker; } @Transactional - 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) || profanityChecker.containsProfanity(name)) { - throw new IllegalArgumentException(); - } - final Product product = new Product(); - product.setId(UUID.randomUUID()); - product.setName(name); - product.setPrice(price); - return productRepository.save(product); + public kitchenpos.core.products.tobe.domain.Product addProduct(final CreateProductRequest request) { + return tobeProductRepository.save(kitchenpos.core.products.tobe.domain.Product.create( + request.id(), + request.name(), + request.price() + )); } @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 Product product = productRepository.findById(productId) - .orElseThrow(NoSuchElementException::new); - product.setPrice(price); - 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); - } - } + public Product changePrice(final ProductId productId, final ProductPrice request) { + + Product product = tobeProductRepository.findById(productId) + .map(p -> p.changePrice(request)) + .orElseThrow(() -> new ProductNotFoundException(productId)); + + menuRepository.findAllByProductId(productId) + .forEach(Menu::recalculateDisplayStatus); return product; } - @Transactional(readOnly = true) - public List findAll() { - return productRepository.findAll(); - } + } diff --git a/src/main/java/kitchenpos/core/products/application/FindProducts.java b/src/main/java/kitchenpos/core/products/application/FindProducts.java new file mode 100644 index 000000000..61ad32f88 --- /dev/null +++ b/src/main/java/kitchenpos/core/products/application/FindProducts.java @@ -0,0 +1,9 @@ +package kitchenpos.core.products.application; + +import kitchenpos.core.products.tobe.domain.Product; + +import java.util.List; + +public interface FindProducts { + List findProducts(); +} diff --git a/src/main/java/kitchenpos/core/products/application/QueryProductService.java b/src/main/java/kitchenpos/core/products/application/QueryProductService.java new file mode 100644 index 000000000..7644d7faa --- /dev/null +++ b/src/main/java/kitchenpos/core/products/application/QueryProductService.java @@ -0,0 +1,22 @@ +package kitchenpos.core.products.application; + +import kitchenpos.core.products.tobe.domain.Product; +import kitchenpos.core.products.tobe.domain.TobeProductRepository; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +public class QueryProductService implements FindProducts { + + private final TobeProductRepository tobeProductRepository; + + public QueryProductService(final TobeProductRepository tobeProductRepository) { + this.tobeProductRepository = tobeProductRepository; + } + + @Override + @Transactional(readOnly = true) + public List findProducts() { + return tobeProductRepository.findAll(); + } +} diff --git a/src/main/java/kitchenpos/core/products/application/CreateProductRequest.java b/src/main/java/kitchenpos/core/products/application/dto/CreateProductRequest.java similarity index 85% rename from src/main/java/kitchenpos/core/products/application/CreateProductRequest.java rename to src/main/java/kitchenpos/core/products/application/dto/CreateProductRequest.java index 748e5cf9b..64d07c503 100644 --- a/src/main/java/kitchenpos/core/products/application/CreateProductRequest.java +++ b/src/main/java/kitchenpos/core/products/application/dto/CreateProductRequest.java @@ -1,4 +1,4 @@ -package kitchenpos.core.products.application; +package kitchenpos.core.products.application.dto; import kitchenpos.core.products.tobe.domain.ProductName; import kitchenpos.core.products.tobe.domain.ProductPrice; diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/Product.java b/src/main/java/kitchenpos/core/products/tobe/domain/Product.java index 99b57b364..7daed924f 100644 --- a/src/main/java/kitchenpos/core/products/tobe/domain/Product.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/Product.java @@ -26,12 +26,12 @@ public class Product extends DomainEntity { protected Product() {} private Product(ProductId id, ProductName name, ProductPrice price) { - this.id = Objects.requireNonNull(id, "id must not be null"); - this.name = Objects.requireNonNull(name, "name must not be null"); - this.price = Objects.requireNonNull(price, "price must not be null"); + this.id = Objects.requireNonNull(id, "id는 null이 될 수 없습니다."); + this.name = Objects.requireNonNull(name, "name은 null이 될 수 없습니다."); + this.price = Objects.requireNonNull(price, "price은 null이 될 수 없습니다."); } - public static Product create(ProductId id, ProductName name, ProductPrice price) { + public static Product create(final ProductId id, final ProductName name, final ProductPrice price) { return new Product(id, name, price); } @@ -47,4 +47,8 @@ public ProductId getId() { public ProductPrice getPrice() { return price; } + public Product changePrice(final ProductPrice newPrice) { + this.price = newPrice; + return this; + } } diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java b/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java index c7bff912a..537cf3dc0 100644 --- a/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java @@ -3,6 +3,9 @@ import kitchenpos.core.products.tobe.domain.exception.InvalidProductPriceException; import kitchenpos.core.shared.value.Money; import kitchenpos.core.shared.domain.ValueObject; +import kitchenpos.core.shared.value.Quantity; + +import java.math.BigDecimal; public class ProductPrice extends ValueObject { private Money price; @@ -23,8 +26,15 @@ public Money getPrice() { return price; } + //quantity 곱하는 로직 + + @Override protected Object[] getEqualityFields() { return new Object[] { price }; } + + public Money multiply(Quantity quantity) { + return price.multiply(quantity.getValue()); + } } diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductIdException.java b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductIdException.java index a79e4d3ee..21fc4e487 100644 --- a/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductIdException.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductIdException.java @@ -1,7 +1,5 @@ package kitchenpos.core.products.tobe.domain.exception; -public class InvalidProductIdException extends IllegalArgumentException { - public InvalidProductIdException(String s) { - super(s); - } +public class InvalidProductIdException extends ProductException { + public InvalidProductIdException(String s) { super(s); } } diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductNameException.java b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductNameException.java index e18e5f966..04113090d 100644 --- a/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductNameException.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductNameException.java @@ -1,6 +1,6 @@ package kitchenpos.core.products.tobe.domain.exception; -public class InvalidProductNameException extends IllegalArgumentException { +public class InvalidProductNameException extends ProductException { public InvalidProductNameException(String s) { super(s); } diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductPriceException.java b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductPriceException.java index 76adb7153..a784344d5 100644 --- a/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductPriceException.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/exception/InvalidProductPriceException.java @@ -1,6 +1,6 @@ package kitchenpos.core.products.tobe.domain.exception; -public class InvalidProductPriceException extends IllegalArgumentException { +public class InvalidProductPriceException extends ProductException { public InvalidProductPriceException(String s) { super(s); } diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/exception/ProductException.java b/src/main/java/kitchenpos/core/products/tobe/domain/exception/ProductException.java new file mode 100644 index 000000000..33f889882 --- /dev/null +++ b/src/main/java/kitchenpos/core/products/tobe/domain/exception/ProductException.java @@ -0,0 +1,14 @@ +package kitchenpos.core.products.tobe.domain.exception; + +import kitchenpos.core.foundation.exception.SystemException; + +public class ProductException extends SystemException { + public ProductException(String format, Object... args) { + super(format, args); + } + + public ProductException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/exception/ProductNotFoundException.java b/src/main/java/kitchenpos/core/products/tobe/domain/exception/ProductNotFoundException.java new file mode 100644 index 000000000..98a731c80 --- /dev/null +++ b/src/main/java/kitchenpos/core/products/tobe/domain/exception/ProductNotFoundException.java @@ -0,0 +1,17 @@ +package kitchenpos.core.products.tobe.domain.exception; + +import kitchenpos.core.shared.identifier.ProductId; + +public class ProductNotFoundException extends ProductException { + + private final ProductId id; + + public ProductNotFoundException(ProductId id) { + super("해당 id를 가진 Product를 찾을 수 없습니다. id: " + id.getValue()); + this.id = id; + } + + public ProductId getId() { + return id; + } +} diff --git a/src/main/java/kitchenpos/core/shared/value/Money.java b/src/main/java/kitchenpos/core/shared/value/Money.java index 749d0aab6..5b3d7d958 100644 --- a/src/main/java/kitchenpos/core/shared/value/Money.java +++ b/src/main/java/kitchenpos/core/shared/value/Money.java @@ -46,4 +46,11 @@ public String toString() { return amount.toString() + "원"; } + public Money multiply(long value) { + return new Money(amount.multiply(BigDecimal.valueOf(value))); + } + + public boolean isBiggerThan(Money other) { + return this.amount.compareTo(other.amount) > 0; + } } diff --git a/src/main/java/kitchenpos/core/shared/value/Quantity.java b/src/main/java/kitchenpos/core/shared/value/Quantity.java new file mode 100644 index 000000000..f9c14f838 --- /dev/null +++ b/src/main/java/kitchenpos/core/shared/value/Quantity.java @@ -0,0 +1,31 @@ +package kitchenpos.core.shared.value; + +import kitchenpos.core.shared.domain.ValueObject; + +public class Quantity extends ValueObject { + private final long value; + + public Quantity(long value) { + if (value < 0) { + throw new IllegalArgumentException("수량은 음수가 될 수 없습니다."); + } + this.value = value; + } + + public static Quantity of(long value) { + return new Quantity(value); + } + + public long getValue() { + return value; + } + + @Override + protected Object[] getEqualityFields() { + return new Object[]{value}; + } + @Override + public String toString() { + return value + " 개"; + } +} From 4061ecbded82c34af772dfec343885ab9ac6c9a1 Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Sun, 2 Mar 2025 01:15:35 +0900 Subject: [PATCH 10/14] =?UTF-8?q?refactor(product):=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20+=20=EA=B4=80=EB=A0=A8=EB=90=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=20=EC=9D=B4=ED=8E=99=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A0=95?= =?UTF-8?q?=EC=83=81=20=EC=9E=91=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatinorders/application/OrderService.java | 9 +- .../core/menus/application/MenuService.java | 39 ++--- .../core/menus/domain/JpaMenuRepository.java | 3 +- .../kitchenpos/core/menus/domain/Menu.java | 4 +- .../core/menus/domain/MenuProduct.java | 7 +- .../application/QueryProductService.java | 2 + .../tobe/domain/TobeProductRepository.java | 3 +- .../kitchenpos/core/shared/value/Money.java | 10 +- .../data/jpa/TobeJpaProductRepository.java | 2 +- .../kitchenpos/web/ProductRestController.java | 24 ++-- .../application/OrderServiceTest.java | 10 +- .../application/OrderTableServiceTest.java | 4 +- .../kitchenpos/{ => fixture}/Fixtures.java | 28 ++-- .../kitchenpos/fixture/ProductFixtures.java | 37 +++++ .../application/InMemoryMenuRepository.java | 3 +- .../application/MenuGroupServiceTest.java | 2 +- .../menus/application/MenuServiceTest.java | 47 ++++--- .../CommandProductServiceTest.java | 95 +++++++++++++ .../application/FakeProductNamePolicy.java | 20 +++ .../application/FakeProfanityChecker.java | 4 + .../InMemoryProductRepository.java | 14 +- .../application/ProductServiceTest.java | 133 ------------------ .../application/QueryProductServiceTest.java | 37 +++++ .../TobeInMemoryProductRepository.java | 36 +++++ 24 files changed, 342 insertions(+), 231 deletions(-) rename src/test/java/kitchenpos/{ => fixture}/Fixtures.java (86%) create mode 100644 src/test/java/kitchenpos/fixture/ProductFixtures.java create mode 100644 src/test/java/kitchenpos/products/application/CommandProductServiceTest.java create mode 100644 src/test/java/kitchenpos/products/application/FakeProductNamePolicy.java delete mode 100644 src/test/java/kitchenpos/products/application/ProductServiceTest.java create mode 100644 src/test/java/kitchenpos/products/application/QueryProductServiceTest.java create mode 100644 src/test/java/kitchenpos/products/application/TobeInMemoryProductRepository.java diff --git a/src/main/java/kitchenpos/core/eatinorders/application/OrderService.java b/src/main/java/kitchenpos/core/eatinorders/application/OrderService.java index bddf30f80..729736fbf 100644 --- a/src/main/java/kitchenpos/core/eatinorders/application/OrderService.java +++ b/src/main/java/kitchenpos/core/eatinorders/application/OrderService.java @@ -1,5 +1,6 @@ package kitchenpos.core.eatinorders.application; +import kitchenpos.core.shared.value.Money; import kitchenpos.data.KitchenridersClient; import kitchenpos.core.eatinorders.domain.Order; import kitchenpos.core.eatinorders.domain.OrderLineItem; @@ -71,7 +72,7 @@ public Order create(final Order request) { if (!menu.isDisplayed()) { throw new IllegalStateException(); } - if (menu.getPrice().compareTo(orderLineItemRequest.getPrice()) != 0) { + if (!menu.getPrice().isEqual(Money.wons(orderLineItemRequest.getPrice()))) { throw new IllegalArgumentException(); } final OrderLineItem orderLineItem = new OrderLineItem(); @@ -111,13 +112,13 @@ public Order accept(final UUID orderId) { throw new IllegalStateException(); } if (order.getType() == OrderType.DELIVERY) { - BigDecimal sum = BigDecimal.ZERO; + Money sum = Money.ZERO; for (final OrderLineItem orderLineItem : order.getOrderLineItems()) { sum = orderLineItem.getMenu() .getPrice() - .multiply(BigDecimal.valueOf(orderLineItem.getQuantity())); + .multiply(orderLineItem.getQuantity()); } - kitchenridersClient.requestDelivery(orderId, sum, order.getDeliveryAddress()); + kitchenridersClient.requestDelivery(orderId, sum.getAmount(), order.getDeliveryAddress()); } order.setStatus(OrderStatus.ACCEPTED); return order; diff --git a/src/main/java/kitchenpos/core/menus/application/MenuService.java b/src/main/java/kitchenpos/core/menus/application/MenuService.java index 0cb98759d..3fb7a5558 100644 --- a/src/main/java/kitchenpos/core/menus/application/MenuService.java +++ b/src/main/java/kitchenpos/core/menus/application/MenuService.java @@ -5,9 +5,12 @@ import kitchenpos.core.menus.domain.MenuGroupRepository; import kitchenpos.core.menus.domain.MenuProduct; import kitchenpos.core.menus.domain.MenuRepository; -import kitchenpos.core.products.domain.Product; import kitchenpos.core.products.domain.ProductRepository; +import kitchenpos.core.products.tobe.domain.Product; +import kitchenpos.core.products.tobe.domain.TobeProductRepository; import kitchenpos.core.shared.domain.ProfanityChecker; +import kitchenpos.core.shared.value.Money; +import kitchenpos.core.shared.value.Quantity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,14 +25,14 @@ public class MenuService { private final MenuRepository menuRepository; private final MenuGroupRepository menuGroupRepository; - private final ProductRepository productRepository; + private final TobeProductRepository productRepository; private final ProfanityChecker profanityChecker; public MenuService( - final MenuRepository menuRepository, - final MenuGroupRepository menuGroupRepository, - final ProductRepository productRepository, - final ProfanityChecker profanityChecker + final MenuRepository menuRepository, + final MenuGroupRepository menuGroupRepository, + final TobeProductRepository productRepository, + final ProfanityChecker profanityChecker ) { this.menuRepository = menuRepository; this.menuGroupRepository = menuGroupRepository; @@ -39,7 +42,7 @@ public MenuService( @Transactional public Menu create(final Menu request) { - final BigDecimal price = request.getPrice(); + final BigDecimal price = request.getPrice().getAmount(); if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException(); } @@ -60,19 +63,19 @@ public Menu create(final Menu request) { final List menuProducts = new ArrayList<>(); BigDecimal sum = BigDecimal.ZERO; for (final MenuProduct menuProductRequest : menuProductRequests) { - final long quantity = menuProductRequest.getQuantity(); + final long quantity = menuProductRequest.getQuantity().getValue(); if (quantity < 0) { throw new IllegalArgumentException(); } final Product product = productRepository.findById(menuProductRequest.getProductId()) .orElseThrow(NoSuchElementException::new); sum = sum.add( - product.getPrice() + product.getPrice().getPrice().getAmount() .multiply(BigDecimal.valueOf(quantity)) ); final MenuProduct menuProduct = new MenuProduct(); menuProduct.setProduct(product); - menuProduct.setQuantity(quantity); + menuProduct.setQuantity(Quantity.of(quantity)); menuProducts.add(menuProduct); } if (price.compareTo(sum) > 0) { @@ -85,7 +88,7 @@ public Menu create(final Menu request) { final Menu menu = new Menu(); menu.setId(UUID.randomUUID()); menu.setName(name); - menu.setPrice(price); + menu.setPrice(Money.wons(price)); menu.setMenuGroup(menuGroup); menu.setDisplayed(request.isDisplayed()); menu.setMenuProducts(menuProducts); @@ -94,7 +97,7 @@ public Menu create(final Menu request) { @Transactional public Menu changePrice(final UUID menuId, final Menu request) { - final BigDecimal price = request.getPrice(); + final BigDecimal price = request.getPrice().getAmount(); if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException(); } @@ -104,14 +107,14 @@ public Menu changePrice(final UUID menuId, final Menu request) { for (final MenuProduct menuProduct : menu.getMenuProducts()) { sum = sum.add( menuProduct.getProduct() - .getPrice() - .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) + .getPrice().getPrice().getAmount() + .multiply(BigDecimal.valueOf(menuProduct.getQuantity().getValue())) ); } if (price.compareTo(sum) > 0) { throw new IllegalArgumentException(); } - menu.setPrice(price); + menu.setPrice(Money.wons(price)); return menu; } @@ -123,11 +126,11 @@ public Menu display(final UUID menuId) { for (final MenuProduct menuProduct : menu.getMenuProducts()) { sum = sum.add( menuProduct.getProduct() - .getPrice() - .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) + .getPrice().getPrice().getAmount() + .multiply(BigDecimal.valueOf(menuProduct.getQuantity().getValue())) ); } - if (menu.getPrice().compareTo(sum) > 0) { + if (menu.getPrice().getAmount().compareTo(sum) > 0) { throw new IllegalStateException(); } menu.setDisplayed(true); diff --git a/src/main/java/kitchenpos/core/menus/domain/JpaMenuRepository.java b/src/main/java/kitchenpos/core/menus/domain/JpaMenuRepository.java index a6f38d105..2cbedbead 100644 --- a/src/main/java/kitchenpos/core/menus/domain/JpaMenuRepository.java +++ b/src/main/java/kitchenpos/core/menus/domain/JpaMenuRepository.java @@ -1,5 +1,6 @@ package kitchenpos.core.menus.domain; +import kitchenpos.core.shared.identifier.ProductId; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -10,5 +11,5 @@ public interface JpaMenuRepository extends MenuRepository, JpaRepository { @Query("select m from Menu m join m.menuProducts mp where mp.product.id = :productId") @Override - List findAllByProductId(@Param("productId") UUID productId); + List findAllByProductId(@Param("productId") ProductId productId); } diff --git a/src/main/java/kitchenpos/core/menus/domain/Menu.java b/src/main/java/kitchenpos/core/menus/domain/Menu.java index 192ecba5e..eb6458a13 100644 --- a/src/main/java/kitchenpos/core/menus/domain/Menu.java +++ b/src/main/java/kitchenpos/core/menus/domain/Menu.java @@ -81,11 +81,11 @@ public void setName(final String name) { this.name = name; } - public BigDecimal getPrice() { + public Money getPrice() { return price; } - public void setPrice(final BigDecimal price) { + public void setPrice(final Money price) { this.price = price; } diff --git a/src/main/java/kitchenpos/core/menus/domain/MenuProduct.java b/src/main/java/kitchenpos/core/menus/domain/MenuProduct.java index 19d4042f0..7ce3001f1 100644 --- a/src/main/java/kitchenpos/core/menus/domain/MenuProduct.java +++ b/src/main/java/kitchenpos/core/menus/domain/MenuProduct.java @@ -12,6 +12,7 @@ import jakarta.persistence.Transient; import kitchenpos.core.products.tobe.domain.Product; import kitchenpos.core.products.tobe.domain.ProductPrice; +import kitchenpos.core.shared.identifier.ProductId; import kitchenpos.core.shared.value.Money; import kitchenpos.core.shared.value.Quantity; @@ -38,7 +39,7 @@ public class MenuProduct { private Quantity quantity; @Transient - private UUID productId; + private ProductId productId; public MenuProduct() { } @@ -67,11 +68,11 @@ public void setQuantity(final Quantity quantity) { this.quantity = quantity; } - public UUID getProductId() { + public ProductId getProductId() { return productId; } - public void setProductId(final UUID productId) { + public void setProductId(final ProductId productId) { this.productId = productId; } diff --git a/src/main/java/kitchenpos/core/products/application/QueryProductService.java b/src/main/java/kitchenpos/core/products/application/QueryProductService.java index 7644d7faa..b3c4b35cc 100644 --- a/src/main/java/kitchenpos/core/products/application/QueryProductService.java +++ b/src/main/java/kitchenpos/core/products/application/QueryProductService.java @@ -2,10 +2,12 @@ import kitchenpos.core.products.tobe.domain.Product; import kitchenpos.core.products.tobe.domain.TobeProductRepository; +import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; +@Service public class QueryProductService implements FindProducts { private final TobeProductRepository tobeProductRepository; diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/TobeProductRepository.java b/src/main/java/kitchenpos/core/products/tobe/domain/TobeProductRepository.java index 631750b54..752037690 100644 --- a/src/main/java/kitchenpos/core/products/tobe/domain/TobeProductRepository.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/TobeProductRepository.java @@ -7,8 +7,7 @@ import java.util.List; import java.util.Optional; -@Repository -@Transactional +@Transactional(readOnly = true) public interface TobeProductRepository { Product save(Product product); diff --git a/src/main/java/kitchenpos/core/shared/value/Money.java b/src/main/java/kitchenpos/core/shared/value/Money.java index 5b3d7d958..47c28113a 100644 --- a/src/main/java/kitchenpos/core/shared/value/Money.java +++ b/src/main/java/kitchenpos/core/shared/value/Money.java @@ -5,7 +5,7 @@ import java.math.BigDecimal; public class Money extends ValueObject { - public static final Money ZERO = Money.wons(0); + public static final Money ZERO = Money.wons(0L); private final BigDecimal amount; @@ -13,6 +13,10 @@ public static Money wons(long amount) { return new Money(BigDecimal.valueOf(amount)); } + public static Money wons(BigDecimal amount) { + return new Money(amount); + } + private Money(BigDecimal amount) { if (amount == null) { throw new IllegalArgumentException("금액은 null이 될 수 없습니다."); @@ -53,4 +57,8 @@ public Money multiply(long value) { public boolean isBiggerThan(Money other) { return this.amount.compareTo(other.amount) > 0; } + + public boolean isEqual(Money other) { + return this.amount.compareTo(other.amount) == 0; + } } diff --git a/src/main/java/kitchenpos/data/jpa/TobeJpaProductRepository.java b/src/main/java/kitchenpos/data/jpa/TobeJpaProductRepository.java index 08dfc7ce4..e15cc125e 100644 --- a/src/main/java/kitchenpos/data/jpa/TobeJpaProductRepository.java +++ b/src/main/java/kitchenpos/data/jpa/TobeJpaProductRepository.java @@ -5,5 +5,5 @@ import kitchenpos.core.products.tobe.domain.TobeProductRepository; import org.springframework.data.jpa.repository.JpaRepository; -public interface TobeJpaProductRepository extends TobeProductRepository, JpaRepository { +interface TobeJpaProductRepository extends TobeProductRepository, JpaRepository { } diff --git a/src/main/java/kitchenpos/web/ProductRestController.java b/src/main/java/kitchenpos/web/ProductRestController.java index cfcd67c49..e28c588c6 100644 --- a/src/main/java/kitchenpos/web/ProductRestController.java +++ b/src/main/java/kitchenpos/web/ProductRestController.java @@ -1,7 +1,10 @@ package kitchenpos.web; import kitchenpos.core.products.application.CommandProductService; -import kitchenpos.core.products.domain.Product; +import kitchenpos.core.products.application.QueryProductService; +import kitchenpos.core.products.application.dto.CreateProductRequest; +import kitchenpos.core.products.tobe.domain.Product; +import kitchenpos.core.shared.identifier.ProductId; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -13,31 +16,32 @@ import java.net.URI; import java.util.List; -import java.util.UUID; @RequestMapping("/api/products") @RestController public class ProductRestController { - private final CommandProductService productService; + private final CommandProductService commandProductService; + private final QueryProductService queryProductService; - public ProductRestController(final CommandProductService productService) { - this.productService = productService; + public ProductRestController(final CommandProductService commandProductService, QueryProductService queryProductService) { + this.commandProductService = commandProductService; + this.queryProductService = queryProductService; } @PostMapping - public ResponseEntity create(@RequestBody final Product request) { - final Product response = productService.create(request); + public ResponseEntity create(@RequestBody final CreateProductRequest request) { + kitchenpos.core.products.tobe.domain.Product response = commandProductService.addProduct(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) { - return ResponseEntity.ok(productService.changePrice(productId, request)); + public ResponseEntity changePrice(@PathVariable final ProductId productId, @RequestBody final Product request) { + return ResponseEntity.ok(commandProductService.changePrice(productId, request.getPrice())); } @GetMapping public ResponseEntity> findAll() { - return ResponseEntity.ok(productService.findAll()); + return ResponseEntity.ok(queryProductService.findProducts()); } } diff --git a/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java b/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java index 4887f36df..3449a2a13 100644 --- a/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java +++ b/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java @@ -28,11 +28,11 @@ import java.util.Random; import java.util.UUID; -import static kitchenpos.Fixtures.INVALID_ID; -import static kitchenpos.Fixtures.menu; -import static kitchenpos.Fixtures.menuProduct; -import static kitchenpos.Fixtures.order; -import static kitchenpos.Fixtures.orderTable; +import static kitchenpos.fixture.Fixtures.INVALID_ID; +import static kitchenpos.fixture.Fixtures.menu; +import static kitchenpos.fixture.Fixtures.menuProduct; +import static kitchenpos.fixture.Fixtures.order; +import static kitchenpos.fixture.Fixtures.orderTable; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; diff --git a/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java b/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java index 189497553..34afd61c3 100644 --- a/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java +++ b/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java @@ -15,8 +15,8 @@ import java.util.List; import java.util.UUID; -import static kitchenpos.Fixtures.order; -import static kitchenpos.Fixtures.orderTable; +import static kitchenpos.fixture.Fixtures.order; +import static kitchenpos.fixture.Fixtures.orderTable; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; diff --git a/src/test/java/kitchenpos/Fixtures.java b/src/test/java/kitchenpos/fixture/Fixtures.java similarity index 86% rename from src/test/java/kitchenpos/Fixtures.java rename to src/test/java/kitchenpos/fixture/Fixtures.java index 6748197ed..a65c0684d 100644 --- a/src/test/java/kitchenpos/Fixtures.java +++ b/src/test/java/kitchenpos/fixture/Fixtures.java @@ -1,4 +1,4 @@ -package kitchenpos; +package kitchenpos.fixture; import kitchenpos.core.eatinorders.domain.Order; import kitchenpos.core.eatinorders.domain.OrderLineItem; @@ -8,15 +8,18 @@ import kitchenpos.core.menus.domain.Menu; import kitchenpos.core.menus.domain.MenuGroup; import kitchenpos.core.menus.domain.MenuProduct; -import kitchenpos.core.products.domain.Product; -import java.math.BigDecimal; +import kitchenpos.core.products.tobe.domain.*; +import kitchenpos.core.shared.value.Money; +import kitchenpos.core.shared.value.Quantity; + import java.time.LocalDateTime; import java.util.Arrays; import java.util.Random; import java.util.UUID; public class Fixtures { + public static final UUID INVALID_ID = new UUID(0L, 0L); public static Menu menu() { @@ -31,7 +34,7 @@ public static Menu menu(final long price, final boolean displayed, final MenuPro final Menu menu = new Menu(); menu.setId(UUID.randomUUID()); menu.setName("후라이드+후라이드"); - menu.setPrice(BigDecimal.valueOf(price)); + menu.setPrice(Money.wons(price)); menu.setMenuGroup(menuGroup()); menu.setDisplayed(displayed); menu.setMenuProducts(Arrays.asList(menuProducts)); @@ -52,8 +55,8 @@ 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.setQuantity(2L); + menuProduct.setProduct(ProductFixtures.product()); + menuProduct.setQuantity(Quantity.of(2L)); return menuProduct; } @@ -61,7 +64,7 @@ public static MenuProduct menuProduct(final Product product, final long quantity final MenuProduct menuProduct = new MenuProduct(); menuProduct.setSeq(new Random().nextLong()); menuProduct.setProduct(product); - menuProduct.setQuantity(quantity); + menuProduct.setQuantity(Quantity.of(quantity)); return menuProduct; } @@ -117,15 +120,4 @@ public static OrderTable orderTable(final boolean occupied, final int numberOfGu return orderTable; } - public static Product product() { - return product("후라이드", 16_000L); - } - - public static Product product(final String name, final long price) { - final Product product = new Product(); - product.setId(UUID.randomUUID()); - product.setName(name); - product.setPrice(BigDecimal.valueOf(price)); - return product; - } } diff --git a/src/test/java/kitchenpos/fixture/ProductFixtures.java b/src/test/java/kitchenpos/fixture/ProductFixtures.java new file mode 100644 index 000000000..420f319a2 --- /dev/null +++ b/src/test/java/kitchenpos/fixture/ProductFixtures.java @@ -0,0 +1,37 @@ +package kitchenpos.fixture; + +import kitchenpos.core.products.application.dto.CreateProductRequest; +import kitchenpos.core.products.tobe.domain.*; +import kitchenpos.core.products.tobe.domain.support.DefaultProductNamePolicy; +import kitchenpos.core.products.tobe.domain.support.UUIDBasedProductIdGenerator; +import kitchenpos.core.shared.domain.ProfanityChecker; +import kitchenpos.core.shared.value.Money; +import kitchenpos.products.application.FakeProfanityChecker; + +public class ProductFixtures { + + private static ProductIdGenerator productIdGenerator = new UUIDBasedProductIdGenerator(); + private static ProfanityChecker profanityChecker = new FakeProfanityChecker(); + private static ProductNamePolicy productNamePolicy = new DefaultProductNamePolicy(profanityChecker); + + public static Product product() { + return product("후라이드", 16_000L); + } + + public static Product product(final String name, final long price) { + final Product product = Product.create( + productIdGenerator.generateId(), + ProductName.create(productNamePolicy, name), + ProductPrice.of(Money.wons(price)) + ); + return product; + } + + public static CreateProductRequest createProductRequest(final String name, final long price) { + return new CreateProductRequest( + productIdGenerator.generateId(), + ProductName.create(productNamePolicy, name), + ProductPrice.of(Money.wons(price))); + } + +} diff --git a/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java b/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java index 2336a1790..bf75a91f9 100644 --- a/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java +++ b/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java @@ -2,6 +2,7 @@ import kitchenpos.core.menus.domain.Menu; import kitchenpos.core.menus.domain.MenuRepository; +import kitchenpos.core.shared.identifier.ProductId; import java.util.ArrayList; import java.util.HashMap; @@ -38,7 +39,7 @@ public List findAllByIdIn(final List ids) { } @Override - public List findAllByProductId(final UUID productId) { + public List findAllByProductId(final ProductId productId) { return menus.values() .stream() .filter(menu -> menu.getMenuProducts().stream().anyMatch(menuProduct -> menuProduct.getProduct().getId().equals(productId))) diff --git a/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java b/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java index 462c80b2d..908cd3b71 100644 --- a/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java +++ b/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java @@ -11,7 +11,7 @@ import java.util.List; -import static kitchenpos.Fixtures.menuGroup; +import static kitchenpos.fixture.Fixtures.menuGroup; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; diff --git a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java index b475e1236..89160d191 100644 --- a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java +++ b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java @@ -5,10 +5,13 @@ import kitchenpos.core.menus.domain.MenuGroupRepository; import kitchenpos.core.menus.domain.MenuProduct; import kitchenpos.core.menus.domain.MenuRepository; +import kitchenpos.core.products.tobe.domain.Product; +import kitchenpos.core.products.tobe.domain.TobeProductRepository; +import kitchenpos.core.shared.identifier.ProductId; +import kitchenpos.core.shared.value.Money; +import kitchenpos.core.shared.value.Quantity; import kitchenpos.products.application.FakeProfanityChecker; import kitchenpos.products.application.InMemoryProductRepository; -import kitchenpos.core.products.domain.Product; -import kitchenpos.core.products.domain.ProductRepository; import kitchenpos.core.shared.domain.ProfanityChecker; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -26,11 +29,11 @@ import java.util.NoSuchElementException; import java.util.UUID; -import static kitchenpos.Fixtures.INVALID_ID; -import static kitchenpos.Fixtures.menu; -import static kitchenpos.Fixtures.menuGroup; -import static kitchenpos.Fixtures.menuProduct; -import static kitchenpos.Fixtures.product; +import static kitchenpos.fixture.Fixtures.INVALID_ID; +import static kitchenpos.fixture.Fixtures.menu; +import static kitchenpos.fixture.Fixtures.menuGroup; +import static kitchenpos.fixture.Fixtures.menuProduct; +import static kitchenpos.fixture.ProductFixtures.product; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; @@ -38,7 +41,7 @@ class MenuServiceTest { private MenuRepository menuRepository; private MenuGroupRepository menuGroupRepository; - private ProductRepository productRepository; + private TobeProductRepository productRepository; private ProfanityChecker profanityChecker; private MenuService menuService; private UUID menuGroupId; @@ -86,17 +89,16 @@ private static List menuProducts() { return Arrays.asList( null, Arguments.of(Collections.emptyList()), - Arguments.of(Arrays.asList(createMenuProductRequest(INVALID_ID, 2L))) + Arguments.of(Arrays.asList(createMenuProductRequest(ProductId.of(INVALID_ID.toString()), 2L))) ); } @DisplayName("메뉴에 속한 상품의 수량은 0개 이상이어야 한다.") @Test void createNegativeQuantity() { - final Menu expected = createMenuRequest( - "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), -1L) - ); - assertThatThrownBy(() -> menuService.create(expected)) + assertThatThrownBy(() -> menuService.create(createMenuRequest( + "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), -1L) + ))) .isInstanceOf(IllegalArgumentException.class); } @@ -105,10 +107,10 @@ void createNegativeQuantity() { @NullSource @ParameterizedTest void create(final BigDecimal price) { - final Menu expected = createMenuRequest( - "후라이드+후라이드", price, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) - ); - assertThatThrownBy(() -> menuService.create(expected)) + assertThatThrownBy(() -> + menuService.create(createMenuRequest( + "후라이드+후라이드", price, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) + ))) .isInstanceOf(IllegalArgumentException.class); } @@ -160,8 +162,7 @@ void changePrice() { @ParameterizedTest void changePrice(final BigDecimal price) { final UUID menuId = menuRepository.save(menu(19_000L, menuProduct(product, 2L))).getId(); - final Menu expected = changePriceRequest(price); - assertThatThrownBy(() -> menuService.changePrice(menuId, expected)) + assertThatThrownBy(() -> menuService.changePrice(menuId, changePriceRequest(price))) .isInstanceOf(IllegalArgumentException.class); } @@ -245,17 +246,17 @@ private Menu createMenuRequest( ) { final Menu menu = new Menu(); menu.setName(name); - menu.setPrice(price); + menu.setPrice(Money.wons(price)); menu.setMenuGroupId(menuGroupId); menu.setDisplayed(displayed); menu.setMenuProducts(menuProducts); return menu; } - private static MenuProduct createMenuProductRequest(final UUID productId, final long quantity) { + private static MenuProduct createMenuProductRequest(final ProductId productId, final long quantity) { final MenuProduct menuProduct = new MenuProduct(); menuProduct.setProductId(productId); - menuProduct.setQuantity(quantity); + menuProduct.setQuantity(Quantity.of(quantity)); return menuProduct; } @@ -265,7 +266,7 @@ private Menu changePriceRequest(final long price) { private Menu changePriceRequest(final BigDecimal price) { final Menu menu = new Menu(); - menu.setPrice(price); + menu.setPrice(Money.wons(price)); return menu; } } diff --git a/src/test/java/kitchenpos/products/application/CommandProductServiceTest.java b/src/test/java/kitchenpos/products/application/CommandProductServiceTest.java new file mode 100644 index 000000000..e02d834b0 --- /dev/null +++ b/src/test/java/kitchenpos/products/application/CommandProductServiceTest.java @@ -0,0 +1,95 @@ +package kitchenpos.products.application; + +import kitchenpos.fixture.ProductFixtures; +import kitchenpos.core.products.application.CommandProductService; +import kitchenpos.core.products.application.dto.CreateProductRequest; +import kitchenpos.core.products.tobe.domain.*; +import kitchenpos.core.products.tobe.domain.exception.InvalidProductNameException; +import kitchenpos.core.shared.identifier.ProductId; +import kitchenpos.core.shared.value.Money; +import kitchenpos.menus.application.InMemoryMenuRepository; +import kitchenpos.core.menus.domain.Menu; +import kitchenpos.core.menus.domain.MenuRepository; +import org.junit.jupiter.api.BeforeEach; +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 static kitchenpos.fixture.Fixtures.menu; +import static kitchenpos.fixture.Fixtures.menuProduct; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class CommandProductServiceTest { + private TobeProductRepository productRepository; + private MenuRepository menuRepository; + private CommandProductService productService; + + + @BeforeEach + void setUp() { + productRepository = new InMemoryProductRepository(); + menuRepository = new InMemoryMenuRepository(); + productService = new CommandProductService(productRepository, menuRepository); + } + + @DisplayName("상품을 등록할 수 있다.") + @Test + void create() { + CreateProductRequest expected = ProductFixtures.createProductRequest("후라이드", 16_000L); + final Product actual = productService.addProduct(expected); + assertThat(actual).isNotNull(); + assertAll( + () -> assertThat(actual.getId()).isNotNull(), + () -> assertThat(actual.getName()).isEqualTo(expected.name()), + () -> assertThat(actual.getPrice()).isEqualTo(expected.price()) + ); + } + + @DisplayName("상품의 가격이 올바르지 않으면 등록할 수 없다.") + @ValueSource(longs = -1000) + @ParameterizedTest + void create(final Long price) { + assertThrows(IllegalArgumentException.class, () -> productService.addProduct(ProductFixtures.createProductRequest("후라이드", price))); + } + + @DisplayName("상품의 이름이 올바르지 않으면 등록할 수 없다.") + @ValueSource(strings = {"비속어", "욕설이 포함된 이름"}) + @NullSource + @ParameterizedTest + void create(final String name) { + assertThatThrownBy(() -> productService.addProduct(ProductFixtures.createProductRequest(name, 16_000L))) + .isInstanceOf(InvalidProductNameException.class); + } + + @DisplayName("상품의 가격을 변경할 수 있다.") + @Test + void changePrice() { + final ProductId productId = productRepository.save(ProductFixtures.product("후라이드", 16_000L)).getId(); + final ProductPrice expected = ProductPrice.of(Money.wons(15_000L)); + final Product actual = productService.changePrice(productId, expected); + assertThat(actual.getPrice()).isEqualTo(expected); + } + + @DisplayName("상품의 가격이 올바르지 않으면 변경할 수 없다.") + @ValueSource(longs = -1000) + @ParameterizedTest + void changePrice(final long price) { + final ProductId productId = productRepository.save(ProductFixtures.product("후라이드", 16_000L)).getId(); + assertThatThrownBy(() -> productService.changePrice(productId, ProductPrice.of(Money.wons(price)))) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("상품의 가격이 변경될 때 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 크면 메뉴가 숨겨진다.") + @Test + void changePriceInMenu() { + final Product product = productRepository.save(ProductFixtures.product("후라이드", 16_000L)); + final Menu menu = menuRepository.save(menu(19_000L, true, menuProduct(product, 2L))); + productService.changePrice(product.getId(), ProductPrice.of(Money.wons(8_000L))); + assertThat(menuRepository.findById(menu.getId()).get().isDisplayed()).isFalse(); + } +} diff --git a/src/test/java/kitchenpos/products/application/FakeProductNamePolicy.java b/src/test/java/kitchenpos/products/application/FakeProductNamePolicy.java new file mode 100644 index 000000000..2cf7a7f2d --- /dev/null +++ b/src/test/java/kitchenpos/products/application/FakeProductNamePolicy.java @@ -0,0 +1,20 @@ +package kitchenpos.products.application; + +import kitchenpos.core.products.tobe.domain.ProductNamePolicy; +import kitchenpos.core.products.tobe.domain.support.ProductNameValidationResult; +import kitchenpos.core.shared.domain.ProfanityChecker; + +import java.util.List; + +public class FakeProductNamePolicy implements ProductNamePolicy { + + private final ProfanityChecker profanityChecker = new FakeProfanityChecker(); + + @Override + public ProductNameValidationResult validate(String name) { + if (profanityChecker.containsProfanity(name)) { + return new ProductNameValidationResult(false, List.of("비속어가 포함되어 있습니다.")); + } + return new ProductNameValidationResult(true, List.of()); + } +} diff --git a/src/test/java/kitchenpos/products/application/FakeProfanityChecker.java b/src/test/java/kitchenpos/products/application/FakeProfanityChecker.java index cd8f1a007..e7c99b5ea 100644 --- a/src/test/java/kitchenpos/products/application/FakeProfanityChecker.java +++ b/src/test/java/kitchenpos/products/application/FakeProfanityChecker.java @@ -1,6 +1,7 @@ package kitchenpos.products.application; import kitchenpos.core.shared.domain.ProfanityChecker; +import org.thymeleaf.util.StringUtils; import java.util.Arrays; import java.util.List; @@ -14,6 +15,9 @@ public class FakeProfanityChecker implements ProfanityChecker { @Override public boolean containsProfanity(final String text) { + if(StringUtils.isEmpty(text)) { + return false; + } return profanities.stream() .anyMatch(profanity -> text.contains(profanity)); } diff --git a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java index 8adc5be25..c7f641566 100644 --- a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java +++ b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java @@ -1,7 +1,9 @@ package kitchenpos.products.application; -import kitchenpos.core.products.domain.Product; -import kitchenpos.core.products.domain.ProductRepository; + +import kitchenpos.core.products.tobe.domain.Product; +import kitchenpos.core.products.tobe.domain.TobeProductRepository; +import kitchenpos.core.shared.identifier.ProductId; import java.util.ArrayList; import java.util.HashMap; @@ -10,8 +12,8 @@ import java.util.Optional; import java.util.UUID; -public class InMemoryProductRepository implements ProductRepository { - private final Map products = new HashMap<>(); +public class InMemoryProductRepository implements TobeProductRepository { + private final Map products = new HashMap<>(); @Override public Product save(final Product product) { @@ -20,7 +22,7 @@ public Product save(final Product product) { } @Override - public Optional findById(final UUID id) { + public Optional findById(final ProductId id) { return Optional.ofNullable(products.get(id)); } @@ -30,7 +32,7 @@ public List findAll() { } @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 deleted file mode 100644 index b263bd68b..000000000 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ /dev/null @@ -1,133 +0,0 @@ -package kitchenpos.products.application; - -import kitchenpos.core.products.application.CommandProductService; -import kitchenpos.menus.application.InMemoryMenuRepository; -import kitchenpos.core.menus.domain.Menu; -import kitchenpos.core.menus.domain.MenuRepository; -import kitchenpos.core.products.domain.Product; -import kitchenpos.core.products.domain.ProductRepository; -import kitchenpos.core.shared.domain.ProfanityChecker; -import org.junit.jupiter.api.BeforeEach; -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; -import java.util.List; -import java.util.UUID; - -import static kitchenpos.Fixtures.menu; -import static kitchenpos.Fixtures.menuProduct; -import static kitchenpos.Fixtures.product; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - -class ProductServiceTest { - private ProductRepository productRepository; - private MenuRepository menuRepository; - private ProfanityChecker profanityChecker; - private CommandProductService productService; - - @BeforeEach - void setUp() { - productRepository = new InMemoryProductRepository(); - menuRepository = new InMemoryMenuRepository(); - profanityChecker = new FakeProfanityChecker(); - productService = new CommandProductService(productRepository, menuRepository, profanityChecker); - } - - @DisplayName("상품을 등록할 수 있다.") - @Test - void create() { - final Product expected = createProductRequest("후라이드", 16_000L); - final Product actual = productService.create(expected); - assertThat(actual).isNotNull(); - assertAll( - () -> assertThat(actual.getId()).isNotNull(), - () -> assertThat(actual.getName()).isEqualTo(expected.getName()), - () -> assertThat(actual.getPrice()).isEqualTo(expected.getPrice()) - ); - } - - @DisplayName("상품의 가격이 올바르지 않으면 등록할 수 없다.") - @ValueSource(strings = "-1000") - @NullSource - @ParameterizedTest - void create(final BigDecimal price) { - final Product expected = createProductRequest("후라이드", price); - assertThatThrownBy(() -> productService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); - } - - @DisplayName("상품의 이름이 올바르지 않으면 등록할 수 없다.") - @ValueSource(strings = {"비속어", "욕설이 포함된 이름"}) - @NullSource - @ParameterizedTest - void create(final String name) { - final Product expected = createProductRequest(name, 16_000L); - assertThatThrownBy(() -> productService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); - } - - @DisplayName("상품의 가격을 변경할 수 있다.") - @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); - assertThat(actual.getPrice()).isEqualTo(expected.getPrice()); - } - - @DisplayName("상품의 가격이 올바르지 않으면 변경할 수 없다.") - @ValueSource(strings = "-1000") - @NullSource - @ParameterizedTest - void changePrice(final BigDecimal price) { - final UUID productId = productRepository.save(product("후라이드", 16_000L)).getId(); - final Product expected = changePriceRequest(price); - assertThatThrownBy(() -> productService.changePrice(productId, expected)) - .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))); - productService.changePrice(product.getId(), changePriceRequest(8_000L)); - assertThat(menuRepository.findById(menu.getId()).get().isDisplayed()).isFalse(); - } - - @DisplayName("상품의 목록을 조회할 수 있다.") - @Test - void findAll() { - productRepository.save(product("후라이드", 16_000L)); - productRepository.save(product("양념치킨", 16_000L)); - final List actual = productService.findAll(); - assertThat(actual).hasSize(2); - } - - private Product 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(); - product.setName(name); - product.setPrice(price); - return product; - } - - private Product changePriceRequest(final long price) { - return changePriceRequest(BigDecimal.valueOf(price)); - } - - private Product changePriceRequest(final BigDecimal price) { - final Product product = new Product(); - product.setPrice(price); - return product; - } -} diff --git a/src/test/java/kitchenpos/products/application/QueryProductServiceTest.java b/src/test/java/kitchenpos/products/application/QueryProductServiceTest.java new file mode 100644 index 000000000..17d0d501a --- /dev/null +++ b/src/test/java/kitchenpos/products/application/QueryProductServiceTest.java @@ -0,0 +1,37 @@ +package kitchenpos.products.application; + +import kitchenpos.fixture.ProductFixtures; +import kitchenpos.core.products.application.QueryProductService; +import kitchenpos.core.products.tobe.domain.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static kitchenpos.fixture.Fixtures.menu; +import static kitchenpos.fixture.Fixtures.menuProduct; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +class QueryProductServiceTest { + private TobeProductRepository productRepository; + private QueryProductService sut; + + @BeforeEach + void setUp() { + productRepository = new InMemoryProductRepository(); + sut = new QueryProductService(productRepository); + } + + @DisplayName("상품의 목록을 조회할 수 있다.") + @Test + void findAll() { + productRepository.save(ProductFixtures.product("후라이드", 16_000L)); + productRepository.save(ProductFixtures.product("양념치킨", 16_000L)); + final List actual = sut.findProducts(); + assertThat(actual).hasSize(2); + } + +} diff --git a/src/test/java/kitchenpos/products/application/TobeInMemoryProductRepository.java b/src/test/java/kitchenpos/products/application/TobeInMemoryProductRepository.java new file mode 100644 index 000000000..e3c10d7f5 --- /dev/null +++ b/src/test/java/kitchenpos/products/application/TobeInMemoryProductRepository.java @@ -0,0 +1,36 @@ +package kitchenpos.products.application; + + +import kitchenpos.core.products.tobe.domain.Product; +import kitchenpos.core.products.tobe.domain.TobeProductRepository; +import kitchenpos.core.shared.identifier.ProductId; + +import java.util.*; + +public class TobeInMemoryProductRepository implements TobeProductRepository { + private final Map products = new HashMap<>(); + + @Override + public Product save(final Product product) { + products.put(product.getId(), product); + return product; + } + + @Override + public Optional findById(final ProductId id) { + return Optional.ofNullable(products.get(id)); + } + + @Override + public List findAll() { + return new ArrayList<>(products.values()); + } + + @Override + public List findAllByIdIn(final List ids) { + return products.values() + .stream() + .filter(product -> ids.contains(product.getId())) + .toList(); + } +} From 893b1e98bef8f2ab442eb79f520eb8b8b9e9f3c9 Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Mon, 3 Mar 2025 19:11:58 +0900 Subject: [PATCH 11/14] =?UTF-8?q?refactor(product):=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20+=20=EA=B4=80=EB=A0=A8=EB=90=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=20=EC=9D=B4=ED=8E=99=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A0=95?= =?UTF-8?q?=EC=83=81=20=EC=9E=91=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../menus/application/MenuEventListener.java | 24 ++++ .../kitchenpos/core/menus/domain/Menu.java | 14 +-- .../application/CommandProductService.java | 4 +- .../core/products/tobe/domain/Product.java | 19 ++- .../products/tobe/domain/ProductPrice.java | 5 + .../support/UUIDBasedProductIdGenerator.java | 2 +- .../core/shared/data/jpa/MoneyConverter.java | 10 +- .../shared/data/jpa/QuantityConverter.java | 24 ++++ .../event/ProductPriceChangedEvent.java | 7 ++ .../core/shared/identifier/ProductId.java | 13 +- .../core/shared/value/Quantity.java | 6 +- .../kitchenpos/web/ProductRestController.java | 28 +++-- src/test/java/kitchenpos/config/UnitTest.java | 15 +++ .../application/FakeKitchenridersClient.java | 2 +- .../application/InMemoryOrderRepository.java | 2 +- .../InMemoryOrderTableRepository.java | 2 +- .../application/OrderServiceTest.java | 5 +- .../application/OrderTableServiceTest.java | 3 +- .../InMemoryMenuGroupRepository.java | 2 +- .../application/InMemoryMenuRepository.java | 2 +- .../application/MenuGroupServiceTest.java | 3 +- .../menus/application/MenuServiceTest.java | 9 +- .../CommandProductServiceTest.java | 51 +++++--- .../application/FakeProfanityChecker.java | 2 +- .../InMemoryProductRepository.java | 3 +- .../application/QueryProductServiceTest.java | 8 +- .../products/tobe/domain/ProductNameTest.java | 53 +++++++++ .../tobe/domain/ProductPriceTest.java | 75 ++++++++++++ .../products/tobe/domain/ProductTest.java | 85 ++++++++++++++ .../core/shared/value/MoneyTest.java | 111 ++++++++++++++++++ .../core/shared/value/QuantityTest.java | 59 ++++++++++ .../kitchenpos/fixture/ProductFixtures.java | 8 +- .../application/FakeProductNamePolicy.java | 20 ---- .../TobeInMemoryProductRepository.java | 36 ------ 34 files changed, 573 insertions(+), 139 deletions(-) create mode 100644 src/main/java/kitchenpos/core/menus/application/MenuEventListener.java create mode 100644 src/main/java/kitchenpos/core/shared/data/jpa/QuantityConverter.java create mode 100644 src/main/java/kitchenpos/core/shared/event/ProductPriceChangedEvent.java create mode 100644 src/test/java/kitchenpos/config/UnitTest.java rename src/test/java/kitchenpos/{ => core}/eatinorders/application/FakeKitchenridersClient.java (93%) rename src/test/java/kitchenpos/{ => core}/eatinorders/application/InMemoryOrderRepository.java (95%) rename src/test/java/kitchenpos/{ => core}/eatinorders/application/InMemoryOrderTableRepository.java (94%) rename src/test/java/kitchenpos/{ => core}/eatinorders/application/OrderServiceTest.java (99%) rename src/test/java/kitchenpos/{ => core}/eatinorders/application/OrderTableServiceTest.java (98%) rename src/test/java/kitchenpos/{ => core}/menus/application/InMemoryMenuGroupRepository.java (94%) rename src/test/java/kitchenpos/{ => core}/menus/application/InMemoryMenuRepository.java (96%) rename src/test/java/kitchenpos/{ => core}/menus/application/MenuGroupServiceTest.java (95%) rename src/test/java/kitchenpos/{ => core}/menus/application/MenuServiceTest.java (97%) rename src/test/java/kitchenpos/{ => core}/products/application/CommandProductServiceTest.java (61%) rename src/test/java/kitchenpos/{ => core}/products/application/FakeProfanityChecker.java (92%) rename src/test/java/kitchenpos/{ => core}/products/application/InMemoryProductRepository.java (94%) rename src/test/java/kitchenpos/{ => core}/products/application/QueryProductServiceTest.java (83%) create mode 100644 src/test/java/kitchenpos/core/products/tobe/domain/ProductNameTest.java create mode 100644 src/test/java/kitchenpos/core/products/tobe/domain/ProductPriceTest.java create mode 100644 src/test/java/kitchenpos/core/products/tobe/domain/ProductTest.java create mode 100644 src/test/java/kitchenpos/core/shared/value/MoneyTest.java create mode 100644 src/test/java/kitchenpos/core/shared/value/QuantityTest.java delete mode 100644 src/test/java/kitchenpos/products/application/FakeProductNamePolicy.java delete mode 100644 src/test/java/kitchenpos/products/application/TobeInMemoryProductRepository.java diff --git a/src/main/java/kitchenpos/core/menus/application/MenuEventListener.java b/src/main/java/kitchenpos/core/menus/application/MenuEventListener.java new file mode 100644 index 000000000..f60d5cabb --- /dev/null +++ b/src/main/java/kitchenpos/core/menus/application/MenuEventListener.java @@ -0,0 +1,24 @@ +package kitchenpos.core.menus.application; + +import jakarta.transaction.Transactional; +import kitchenpos.core.menus.domain.Menu; +import kitchenpos.core.menus.domain.MenuRepository; +import kitchenpos.core.shared.event.ProductPriceChangedEvent; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@Component +public class MenuEventListener { + private final MenuRepository menuRepository; + + public MenuEventListener(MenuRepository menuRepository) { + this.menuRepository = menuRepository; + } + + @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) + public void onProductPriceChanged(final ProductPriceChangedEvent event) { + menuRepository.findAllByProductId(event.id()) + .forEach(Menu::recalculateDisplayStatus); + } +} diff --git a/src/main/java/kitchenpos/core/menus/domain/Menu.java b/src/main/java/kitchenpos/core/menus/domain/Menu.java index eb6458a13..ce7d47b28 100644 --- a/src/main/java/kitchenpos/core/menus/domain/Menu.java +++ b/src/main/java/kitchenpos/core/menus/domain/Menu.java @@ -1,15 +1,7 @@ package kitchenpos.core.menus.domain; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import jakarta.persistence.Transient; +import jakarta.persistence.*; +import kitchenpos.core.products.tobe.domain.ProductPrice; import kitchenpos.core.shared.value.Money; import java.math.BigDecimal; @@ -26,7 +18,7 @@ public class Menu { @Column(name = "name", nullable = false) private String name; - @Column(name = "price", nullable = false) + @AttributeOverride(name = "amount", column = @Column(name = "price", nullable = false, columnDefinition = "decimal(19,2)")) private Money price; @ManyToOne(optional = false) diff --git a/src/main/java/kitchenpos/core/products/application/CommandProductService.java b/src/main/java/kitchenpos/core/products/application/CommandProductService.java index ec33cce73..2cbfa2751 100644 --- a/src/main/java/kitchenpos/core/products/application/CommandProductService.java +++ b/src/main/java/kitchenpos/core/products/application/CommandProductService.java @@ -39,9 +39,7 @@ public Product changePrice(final ProductId productId, final ProductPrice request Product product = tobeProductRepository.findById(productId) .map(p -> p.changePrice(request)) .orElseThrow(() -> new ProductNotFoundException(productId)); - - menuRepository.findAllByProductId(productId) - .forEach(Menu::recalculateDisplayStatus); + tobeProductRepository.save(product); return product; } diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/Product.java b/src/main/java/kitchenpos/core/products/tobe/domain/Product.java index 7daed924f..cb5353045 100644 --- a/src/main/java/kitchenpos/core/products/tobe/domain/Product.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/Product.java @@ -1,17 +1,19 @@ package kitchenpos.core.products.tobe.domain; import jakarta.persistence.*; -import kitchenpos.core.shared.domain.DomainEntity; +import kitchenpos.core.shared.domain.AggregateRoot; +import kitchenpos.core.shared.event.ProductPriceChangedEvent; import kitchenpos.core.shared.identifier.ProductId; +import java.util.Collection; import java.util.Objects; @Table(name = "product_tobe") @Entity(name = "ProductTobe") -public class Product extends DomainEntity { +public class Product extends AggregateRoot { @EmbeddedId - @AttributeOverride(name = "value", column = @Column(name = "id")) + @AttributeOverride(name = "value", column = @Column(name = "id", nullable = false, columnDefinition = "binary(16)")) private ProductId id; @Embedded @@ -19,7 +21,7 @@ public class Product extends DomainEntity { private ProductName name; @Embedded - @AttributeOverride(name = "price", column = @Column(name = "price", nullable = false)) + @AttributeOverride(name = "price", column = @Column(name = "price", nullable = false, columnDefinition = "decimal(19,2)")) private ProductPrice price; @SuppressWarnings("unused") @@ -48,7 +50,16 @@ public ProductId getId() { public ProductPrice getPrice() { return price; } public Product changePrice(final ProductPrice newPrice) { + Objects.requireNonNull(newPrice, "변경할 금액은 null이 될 수 없습니다."); + ProductPriceChangedEvent productPriceChangedEvent = new ProductPriceChangedEvent(this.id, this.price, newPrice); this.price = newPrice; + + registerEvent(productPriceChangedEvent); return this; } + + @Override + public Collection domainEvents() { + return super.domainEvents(); + } } diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java b/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java index 537cf3dc0..268a95084 100644 --- a/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java @@ -1,5 +1,6 @@ package kitchenpos.core.products.tobe.domain; +import jakarta.persistence.Embeddable; import kitchenpos.core.products.tobe.domain.exception.InvalidProductPriceException; import kitchenpos.core.shared.value.Money; import kitchenpos.core.shared.domain.ValueObject; @@ -7,9 +8,13 @@ import java.math.BigDecimal; +@Embeddable public class ProductPrice extends ValueObject { private Money price; + @SuppressWarnings("unused") + protected ProductPrice() {} + private ProductPrice(Money price) { if (price == null || price.isLessThan(Money.ZERO)) { throw new InvalidProductPriceException("상품 가격은 null 이거나 0보다 작을 수 없습니다."); diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/support/UUIDBasedProductIdGenerator.java b/src/main/java/kitchenpos/core/products/tobe/domain/support/UUIDBasedProductIdGenerator.java index 27fb8808b..8b8e33412 100644 --- a/src/main/java/kitchenpos/core/products/tobe/domain/support/UUIDBasedProductIdGenerator.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/support/UUIDBasedProductIdGenerator.java @@ -10,6 +10,6 @@ public class UUIDBasedProductIdGenerator implements ProductIdGenerator { @Override public ProductId generateId() { - return ProductId.of(UUID.randomUUID().toString()); + return ProductId.of(UUID.randomUUID()); } } diff --git a/src/main/java/kitchenpos/core/shared/data/jpa/MoneyConverter.java b/src/main/java/kitchenpos/core/shared/data/jpa/MoneyConverter.java index 10b43f2dc..7c5b1e0ee 100644 --- a/src/main/java/kitchenpos/core/shared/data/jpa/MoneyConverter.java +++ b/src/main/java/kitchenpos/core/shared/data/jpa/MoneyConverter.java @@ -4,15 +4,17 @@ import jakarta.persistence.Converter; import kitchenpos.core.shared.value.Money; +import java.math.BigDecimal; + @Converter(autoApply = true) -public class MoneyConverter implements AttributeConverter { +public class MoneyConverter implements AttributeConverter { @Override - public Long convertToDatabaseColumn(Money money) { - return money.getAmount().longValue(); + public BigDecimal convertToDatabaseColumn(Money money) { + return money.getAmount(); } @Override - public Money convertToEntityAttribute(Long amount) { + public Money convertToEntityAttribute(BigDecimal amount) { return Money.wons(amount); } } \ No newline at end of file diff --git a/src/main/java/kitchenpos/core/shared/data/jpa/QuantityConverter.java b/src/main/java/kitchenpos/core/shared/data/jpa/QuantityConverter.java new file mode 100644 index 000000000..85a04e685 --- /dev/null +++ b/src/main/java/kitchenpos/core/shared/data/jpa/QuantityConverter.java @@ -0,0 +1,24 @@ +package kitchenpos.core.shared.data.jpa; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import kitchenpos.core.shared.value.Money; +import kitchenpos.core.shared.value.Quantity; + +import java.math.BigDecimal; + +@Converter(autoApply = true) +public class QuantityConverter implements AttributeConverter { + @Override + public Long convertToDatabaseColumn(Quantity quantity) { + return quantity.getValue(); + } + + @Override + public Quantity convertToEntityAttribute(Long quantity) { + if(quantity == null) { + return Quantity.ZERO; + } + return Quantity.of(quantity); + } +} \ No newline at end of file diff --git a/src/main/java/kitchenpos/core/shared/event/ProductPriceChangedEvent.java b/src/main/java/kitchenpos/core/shared/event/ProductPriceChangedEvent.java new file mode 100644 index 000000000..bad9333e4 --- /dev/null +++ b/src/main/java/kitchenpos/core/shared/event/ProductPriceChangedEvent.java @@ -0,0 +1,7 @@ +package kitchenpos.core.shared.event; + +import kitchenpos.core.products.tobe.domain.ProductPrice; +import kitchenpos.core.shared.identifier.ProductId; + +public record ProductPriceChangedEvent(ProductId id, ProductPrice oldPrice, ProductPrice newPrice) { +} diff --git a/src/main/java/kitchenpos/core/shared/identifier/ProductId.java b/src/main/java/kitchenpos/core/shared/identifier/ProductId.java index 7a0213b40..a22c16a92 100644 --- a/src/main/java/kitchenpos/core/shared/identifier/ProductId.java +++ b/src/main/java/kitchenpos/core/shared/identifier/ProductId.java @@ -4,27 +4,28 @@ import kitchenpos.core.products.tobe.domain.exception.InvalidProductIdException; import java.io.Serializable; +import java.util.UUID; @Embeddable public class ProductId implements Serializable { - private String value; + private UUID value; @SuppressWarnings("unused") protected ProductId() {} - private ProductId(String value) { - if (value == null || value.isBlank()) { + private ProductId(UUID value) { + if (value == null) { throw new InvalidProductIdException("Product ID 는 null 이거나 빈 값이 될 수 없습니다."); } - this.value = value.strip(); + this.value = value; } - public static ProductId of(String value) { + public static ProductId of(UUID value) { return new ProductId(value); } - public String getValue() { + public UUID getValue() { return value; } } \ No newline at end of file diff --git a/src/main/java/kitchenpos/core/shared/value/Quantity.java b/src/main/java/kitchenpos/core/shared/value/Quantity.java index f9c14f838..cb8dd2b4b 100644 --- a/src/main/java/kitchenpos/core/shared/value/Quantity.java +++ b/src/main/java/kitchenpos/core/shared/value/Quantity.java @@ -3,9 +3,11 @@ import kitchenpos.core.shared.domain.ValueObject; public class Quantity extends ValueObject { - private final long value; + public static final Quantity ZERO = Quantity.of(0); - public Quantity(long value) { + private final Long value; + + private Quantity(long value) { if (value < 0) { throw new IllegalArgumentException("수량은 음수가 될 수 없습니다."); } diff --git a/src/main/java/kitchenpos/web/ProductRestController.java b/src/main/java/kitchenpos/web/ProductRestController.java index e28c588c6..b6e395f1c 100644 --- a/src/main/java/kitchenpos/web/ProductRestController.java +++ b/src/main/java/kitchenpos/web/ProductRestController.java @@ -1,10 +1,11 @@ package kitchenpos.web; -import kitchenpos.core.products.application.CommandProductService; -import kitchenpos.core.products.application.QueryProductService; +import kitchenpos.core.products.application.*; import kitchenpos.core.products.application.dto.CreateProductRequest; import kitchenpos.core.products.tobe.domain.Product; +import kitchenpos.core.products.tobe.domain.ProductPrice; import kitchenpos.core.shared.identifier.ProductId; +import kitchenpos.core.shared.value.Money; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -16,32 +17,35 @@ import java.net.URI; import java.util.List; +import java.util.UUID; -@RequestMapping("/api/products") +@RequestMapping("/v1/api/products") @RestController public class ProductRestController { - private final CommandProductService commandProductService; - private final QueryProductService queryProductService; + private final AddProduct addProduct; + private final ChangeProductPrice changeProductPrice; + private final FindProducts findProducts; - public ProductRestController(final CommandProductService commandProductService, QueryProductService queryProductService) { - this.commandProductService = commandProductService; - this.queryProductService = queryProductService; + public ProductRestController(AddProduct addProduct, ChangeProductPrice changeProductPrice, FindProducts findProducts) { + this.addProduct = addProduct; + this.changeProductPrice = changeProductPrice; + this.findProducts = findProducts; } @PostMapping public ResponseEntity create(@RequestBody final CreateProductRequest request) { - kitchenpos.core.products.tobe.domain.Product response = commandProductService.addProduct(request); + kitchenpos.core.products.tobe.domain.Product response = addProduct.addProduct(request); return ResponseEntity.created(URI.create("/api/products/" + response.getId())) .body(response); } @PutMapping("/{productId}/price") - public ResponseEntity changePrice(@PathVariable final ProductId productId, @RequestBody final Product request) { - return ResponseEntity.ok(commandProductService.changePrice(productId, request.getPrice())); + public ResponseEntity changePrice(@PathVariable final UUID productId, @RequestBody final Long amount) { + return ResponseEntity.ok(changeProductPrice.changePrice(ProductId.of(productId), ProductPrice.of(Money.wons(amount)))); } @GetMapping public ResponseEntity> findAll() { - return ResponseEntity.ok(queryProductService.findProducts()); + return ResponseEntity.ok(findProducts.findProducts()); } } diff --git a/src/test/java/kitchenpos/config/UnitTest.java b/src/test/java/kitchenpos/config/UnitTest.java new file mode 100644 index 000000000..c915598c5 --- /dev/null +++ b/src/test/java/kitchenpos/config/UnitTest.java @@ -0,0 +1,15 @@ +package kitchenpos.config; + +import org.junit.jupiter.api.Tag; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Tag("unitTest") +@Target({ElementType.TYPE}) +@Retention(RUNTIME) +public @interface UnitTest { +} diff --git a/src/test/java/kitchenpos/eatinorders/application/FakeKitchenridersClient.java b/src/test/java/kitchenpos/core/eatinorders/application/FakeKitchenridersClient.java similarity index 93% rename from src/test/java/kitchenpos/eatinorders/application/FakeKitchenridersClient.java rename to src/test/java/kitchenpos/core/eatinorders/application/FakeKitchenridersClient.java index 5221ba4bd..3441cc5b8 100644 --- a/src/test/java/kitchenpos/eatinorders/application/FakeKitchenridersClient.java +++ b/src/test/java/kitchenpos/core/eatinorders/application/FakeKitchenridersClient.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.application; +package kitchenpos.core.eatinorders.application; import kitchenpos.data.KitchenridersClient; diff --git a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderRepository.java b/src/test/java/kitchenpos/core/eatinorders/application/InMemoryOrderRepository.java similarity index 95% rename from src/test/java/kitchenpos/eatinorders/application/InMemoryOrderRepository.java rename to src/test/java/kitchenpos/core/eatinorders/application/InMemoryOrderRepository.java index fc333e546..6d4413786 100644 --- a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderRepository.java +++ b/src/test/java/kitchenpos/core/eatinorders/application/InMemoryOrderRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.application; +package kitchenpos.core.eatinorders.application; import kitchenpos.core.eatinorders.domain.Order; import kitchenpos.core.eatinorders.domain.OrderRepository; diff --git a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderTableRepository.java b/src/test/java/kitchenpos/core/eatinorders/application/InMemoryOrderTableRepository.java similarity index 94% rename from src/test/java/kitchenpos/eatinorders/application/InMemoryOrderTableRepository.java rename to src/test/java/kitchenpos/core/eatinorders/application/InMemoryOrderTableRepository.java index 73c13c1ee..ad0bec799 100644 --- a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderTableRepository.java +++ b/src/test/java/kitchenpos/core/eatinorders/application/InMemoryOrderTableRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.application; +package kitchenpos.core.eatinorders.application; import kitchenpos.core.eatinorders.domain.OrderTable; import kitchenpos.core.eatinorders.domain.OrderTableRepository; diff --git a/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java b/src/test/java/kitchenpos/core/eatinorders/application/OrderServiceTest.java similarity index 99% rename from src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java rename to src/test/java/kitchenpos/core/eatinorders/application/OrderServiceTest.java index 3449a2a13..4313528b0 100644 --- a/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java +++ b/src/test/java/kitchenpos/core/eatinorders/application/OrderServiceTest.java @@ -1,6 +1,5 @@ -package kitchenpos.eatinorders.application; +package kitchenpos.core.eatinorders.application; -import kitchenpos.core.eatinorders.application.OrderService; import kitchenpos.core.eatinorders.domain.Order; import kitchenpos.core.eatinorders.domain.OrderLineItem; import kitchenpos.core.eatinorders.domain.OrderRepository; @@ -8,7 +7,7 @@ import kitchenpos.core.eatinorders.domain.OrderTable; import kitchenpos.core.eatinorders.domain.OrderTableRepository; import kitchenpos.core.eatinorders.domain.OrderType; -import kitchenpos.menus.application.InMemoryMenuRepository; +import kitchenpos.core.menus.application.InMemoryMenuRepository; import kitchenpos.core.menus.domain.MenuRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java b/src/test/java/kitchenpos/core/eatinorders/application/OrderTableServiceTest.java similarity index 98% rename from src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java rename to src/test/java/kitchenpos/core/eatinorders/application/OrderTableServiceTest.java index 34afd61c3..eb171308d 100644 --- a/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java +++ b/src/test/java/kitchenpos/core/eatinorders/application/OrderTableServiceTest.java @@ -1,6 +1,5 @@ -package kitchenpos.eatinorders.application; +package kitchenpos.core.eatinorders.application; -import kitchenpos.core.eatinorders.application.OrderTableService; import kitchenpos.core.eatinorders.domain.OrderRepository; import kitchenpos.core.eatinorders.domain.OrderStatus; import kitchenpos.core.eatinorders.domain.OrderTable; diff --git a/src/test/java/kitchenpos/menus/application/InMemoryMenuGroupRepository.java b/src/test/java/kitchenpos/core/menus/application/InMemoryMenuGroupRepository.java similarity index 94% rename from src/test/java/kitchenpos/menus/application/InMemoryMenuGroupRepository.java rename to src/test/java/kitchenpos/core/menus/application/InMemoryMenuGroupRepository.java index 9f7cae8d9..0f068c2cd 100644 --- a/src/test/java/kitchenpos/menus/application/InMemoryMenuGroupRepository.java +++ b/src/test/java/kitchenpos/core/menus/application/InMemoryMenuGroupRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.menus.application; +package kitchenpos.core.menus.application; import kitchenpos.core.menus.domain.MenuGroup; import kitchenpos.core.menus.domain.MenuGroupRepository; diff --git a/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java b/src/test/java/kitchenpos/core/menus/application/InMemoryMenuRepository.java similarity index 96% rename from src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java rename to src/test/java/kitchenpos/core/menus/application/InMemoryMenuRepository.java index bf75a91f9..d211631e8 100644 --- a/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java +++ b/src/test/java/kitchenpos/core/menus/application/InMemoryMenuRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.menus.application; +package kitchenpos.core.menus.application; import kitchenpos.core.menus.domain.Menu; import kitchenpos.core.menus.domain.MenuRepository; diff --git a/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java b/src/test/java/kitchenpos/core/menus/application/MenuGroupServiceTest.java similarity index 95% rename from src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java rename to src/test/java/kitchenpos/core/menus/application/MenuGroupServiceTest.java index 908cd3b71..c2730d172 100644 --- a/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java +++ b/src/test/java/kitchenpos/core/menus/application/MenuGroupServiceTest.java @@ -1,6 +1,5 @@ -package kitchenpos.menus.application; +package kitchenpos.core.menus.application; -import kitchenpos.core.menus.application.MenuGroupService; import kitchenpos.core.menus.domain.MenuGroup; import kitchenpos.core.menus.domain.MenuGroupRepository; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java b/src/test/java/kitchenpos/core/menus/application/MenuServiceTest.java similarity index 97% rename from src/test/java/kitchenpos/menus/application/MenuServiceTest.java rename to src/test/java/kitchenpos/core/menus/application/MenuServiceTest.java index 89160d191..f9fccbd05 100644 --- a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java +++ b/src/test/java/kitchenpos/core/menus/application/MenuServiceTest.java @@ -1,6 +1,5 @@ -package kitchenpos.menus.application; +package kitchenpos.core.menus.application; -import kitchenpos.core.menus.application.MenuService; import kitchenpos.core.menus.domain.Menu; import kitchenpos.core.menus.domain.MenuGroupRepository; import kitchenpos.core.menus.domain.MenuProduct; @@ -10,8 +9,8 @@ import kitchenpos.core.shared.identifier.ProductId; import kitchenpos.core.shared.value.Money; import kitchenpos.core.shared.value.Quantity; -import kitchenpos.products.application.FakeProfanityChecker; -import kitchenpos.products.application.InMemoryProductRepository; +import kitchenpos.core.products.application.FakeProfanityChecker; +import kitchenpos.core.products.application.InMemoryProductRepository; import kitchenpos.core.shared.domain.ProfanityChecker; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -89,7 +88,7 @@ private static List menuProducts() { return Arrays.asList( null, Arguments.of(Collections.emptyList()), - Arguments.of(Arrays.asList(createMenuProductRequest(ProductId.of(INVALID_ID.toString()), 2L))) + Arguments.of(Arrays.asList(createMenuProductRequest(ProductId.of(INVALID_ID), 2L))) ); } diff --git a/src/test/java/kitchenpos/products/application/CommandProductServiceTest.java b/src/test/java/kitchenpos/core/products/application/CommandProductServiceTest.java similarity index 61% rename from src/test/java/kitchenpos/products/application/CommandProductServiceTest.java rename to src/test/java/kitchenpos/core/products/application/CommandProductServiceTest.java index e02d834b0..8da20c8e4 100644 --- a/src/test/java/kitchenpos/products/application/CommandProductServiceTest.java +++ b/src/test/java/kitchenpos/core/products/application/CommandProductServiceTest.java @@ -1,22 +1,26 @@ -package kitchenpos.products.application; +package kitchenpos.core.products.application; +import kitchenpos.config.UnitTest; +import kitchenpos.core.shared.event.ProductPriceChangedEvent; import kitchenpos.fixture.ProductFixtures; -import kitchenpos.core.products.application.CommandProductService; import kitchenpos.core.products.application.dto.CreateProductRequest; import kitchenpos.core.products.tobe.domain.*; import kitchenpos.core.products.tobe.domain.exception.InvalidProductNameException; import kitchenpos.core.shared.identifier.ProductId; import kitchenpos.core.shared.value.Money; -import kitchenpos.menus.application.InMemoryMenuRepository; +import kitchenpos.core.menus.application.InMemoryMenuRepository; import kitchenpos.core.menus.domain.Menu; import kitchenpos.core.menus.domain.MenuRepository; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; 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.util.Collection; + import static kitchenpos.fixture.Fixtures.menu; import static kitchenpos.fixture.Fixtures.menuProduct; import static org.assertj.core.api.Assertions.assertThat; @@ -24,24 +28,26 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertThrows; +@UnitTest +@DisplayName("[Product] CommandProductService 테스트") class CommandProductServiceTest { private TobeProductRepository productRepository; private MenuRepository menuRepository; - private CommandProductService productService; + private CommandProductService sut; @BeforeEach void setUp() { productRepository = new InMemoryProductRepository(); menuRepository = new InMemoryMenuRepository(); - productService = new CommandProductService(productRepository, menuRepository); + sut = new CommandProductService(productRepository, menuRepository); } - @DisplayName("상품을 등록할 수 있다.") + @DisplayName("성공: 상품을 등록할 수 있다.") @Test void create() { CreateProductRequest expected = ProductFixtures.createProductRequest("후라이드", 16_000L); - final Product actual = productService.addProduct(expected); + final Product actual = sut.addProduct(expected); assertThat(actual).isNotNull(); assertAll( () -> assertThat(actual.getId()).isNotNull(), @@ -50,19 +56,21 @@ void create() { ); } - @DisplayName("상품의 가격이 올바르지 않으면 등록할 수 없다.") + @Disabled("ProductPrice 생성 시에 이미 검증") + @DisplayName("실패: 상품의 가격이 올바르지 않으면 등록할 수 없다.") @ValueSource(longs = -1000) @ParameterizedTest void create(final Long price) { - assertThrows(IllegalArgumentException.class, () -> productService.addProduct(ProductFixtures.createProductRequest("후라이드", price))); + assertThrows(IllegalArgumentException.class, () -> sut.addProduct(ProductFixtures.createProductRequest("후라이드", price))); } - @DisplayName("상품의 이름이 올바르지 않으면 등록할 수 없다.") + @Disabled("ProductName 생성 시에 이미 검증") + @DisplayName("실패: 상품의 이름이 올바르지 않으면 등록할 수 없다.") @ValueSource(strings = {"비속어", "욕설이 포함된 이름"}) @NullSource @ParameterizedTest void create(final String name) { - assertThatThrownBy(() -> productService.addProduct(ProductFixtures.createProductRequest(name, 16_000L))) + assertThatThrownBy(() -> sut.addProduct(ProductFixtures.createProductRequest(name, 16_000L))) .isInstanceOf(InvalidProductNameException.class); } @@ -71,25 +79,40 @@ void create(final String name) { void changePrice() { final ProductId productId = productRepository.save(ProductFixtures.product("후라이드", 16_000L)).getId(); final ProductPrice expected = ProductPrice.of(Money.wons(15_000L)); - final Product actual = productService.changePrice(productId, expected); + final Product actual = sut.changePrice(productId, expected); assertThat(actual.getPrice()).isEqualTo(expected); } + @Disabled("ProductPrice 생성 시에 이미 검증") @DisplayName("상품의 가격이 올바르지 않으면 변경할 수 없다.") @ValueSource(longs = -1000) @ParameterizedTest void changePrice(final long price) { final ProductId productId = productRepository.save(ProductFixtures.product("후라이드", 16_000L)).getId(); - assertThatThrownBy(() -> productService.changePrice(productId, ProductPrice.of(Money.wons(price)))) + assertThatThrownBy(() -> sut.changePrice(productId, ProductPrice.of(Money.wons(price)))) .isInstanceOf(IllegalArgumentException.class); } + @Disabled("Product Event 발행으로 변경") @DisplayName("상품의 가격이 변경될 때 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 크면 메뉴가 숨겨진다.") @Test void changePriceInMenu() { final Product product = productRepository.save(ProductFixtures.product("후라이드", 16_000L)); final Menu menu = menuRepository.save(menu(19_000L, true, menuProduct(product, 2L))); - productService.changePrice(product.getId(), ProductPrice.of(Money.wons(8_000L))); + sut.changePrice(product.getId(), ProductPrice.of(Money.wons(8_000L))); assertThat(menuRepository.findById(menu.getId()).get().isDisplayed()).isFalse(); } + + @DisplayName("성공: 상품의 가격이 변경될 때 ProductPriceChangedEvent가 발생한다.") + @Test + void changePrice2() { + final Product product = productRepository.save(ProductFixtures.product("후라이드", 16_000L)); + sut.changePrice(product.getId(), ProductPrice.of(Money.wons(8_000L))); + // then: domainEvents를 확인 + Collection events = product.domainEvents(); + + assertThat(events).hasSize(1); + Object event = events.iterator().next(); + assertThat(event).isInstanceOf(ProductPriceChangedEvent.class); + } } diff --git a/src/test/java/kitchenpos/products/application/FakeProfanityChecker.java b/src/test/java/kitchenpos/core/products/application/FakeProfanityChecker.java similarity index 92% rename from src/test/java/kitchenpos/products/application/FakeProfanityChecker.java rename to src/test/java/kitchenpos/core/products/application/FakeProfanityChecker.java index e7c99b5ea..d78b1766c 100644 --- a/src/test/java/kitchenpos/products/application/FakeProfanityChecker.java +++ b/src/test/java/kitchenpos/core/products/application/FakeProfanityChecker.java @@ -1,4 +1,4 @@ -package kitchenpos.products.application; +package kitchenpos.core.products.application; import kitchenpos.core.shared.domain.ProfanityChecker; import org.thymeleaf.util.StringUtils; diff --git a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java b/src/test/java/kitchenpos/core/products/application/InMemoryProductRepository.java similarity index 94% rename from src/test/java/kitchenpos/products/application/InMemoryProductRepository.java rename to src/test/java/kitchenpos/core/products/application/InMemoryProductRepository.java index c7f641566..0a5791208 100644 --- a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java +++ b/src/test/java/kitchenpos/core/products/application/InMemoryProductRepository.java @@ -1,4 +1,4 @@ -package kitchenpos.products.application; +package kitchenpos.core.products.application; import kitchenpos.core.products.tobe.domain.Product; @@ -10,7 +10,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.UUID; public class InMemoryProductRepository implements TobeProductRepository { private final Map products = new HashMap<>(); diff --git a/src/test/java/kitchenpos/products/application/QueryProductServiceTest.java b/src/test/java/kitchenpos/core/products/application/QueryProductServiceTest.java similarity index 83% rename from src/test/java/kitchenpos/products/application/QueryProductServiceTest.java rename to src/test/java/kitchenpos/core/products/application/QueryProductServiceTest.java index 17d0d501a..22d54ffcb 100644 --- a/src/test/java/kitchenpos/products/application/QueryProductServiceTest.java +++ b/src/test/java/kitchenpos/core/products/application/QueryProductServiceTest.java @@ -1,7 +1,7 @@ -package kitchenpos.products.application; +package kitchenpos.core.products.application; +import kitchenpos.config.UnitTest; import kitchenpos.fixture.ProductFixtures; -import kitchenpos.core.products.application.QueryProductService; import kitchenpos.core.products.tobe.domain.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -15,6 +15,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; +@UnitTest +@DisplayName("[Product] QueryProductService 테스트") class QueryProductServiceTest { private TobeProductRepository productRepository; private QueryProductService sut; @@ -25,7 +27,7 @@ void setUp() { sut = new QueryProductService(productRepository); } - @DisplayName("상품의 목록을 조회할 수 있다.") + @DisplayName("성공: 저장된 전체 상품을 조회할 수 있다.") @Test void findAll() { productRepository.save(ProductFixtures.product("후라이드", 16_000L)); diff --git a/src/test/java/kitchenpos/core/products/tobe/domain/ProductNameTest.java b/src/test/java/kitchenpos/core/products/tobe/domain/ProductNameTest.java new file mode 100644 index 000000000..6b4f2d827 --- /dev/null +++ b/src/test/java/kitchenpos/core/products/tobe/domain/ProductNameTest.java @@ -0,0 +1,53 @@ +package kitchenpos.core.products.tobe.domain; + +import kitchenpos.config.UnitTest; +import kitchenpos.core.products.application.FakeProfanityChecker; +import kitchenpos.core.products.tobe.domain.exception.InvalidProductNameException; +import kitchenpos.core.products.tobe.domain.support.DefaultProductNamePolicy; +import kitchenpos.core.shared.domain.ProfanityChecker; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +@UnitTest +@DisplayName("[Product] ProductName 테스트") +class ProductNameTest { + + private final ProfanityChecker profanityChecker = new FakeProfanityChecker(); + private final ProductNamePolicy productNamePolicy = new DefaultProductNamePolicy(profanityChecker); + + @Test + @DisplayName("성공: 유효한 이름으로 ProductName 생성") + void createProductNameSuccess() { + // given + String rawName = " Test Product "; + + // when + ProductName productName = ProductName.create(productNamePolicy, rawName); + + // then + assertThat(productName.getName()).isEqualTo("Test Product"); + } + + @ParameterizedTest + @DisplayName("실패: null이나 빈 문자열로 ProductName 생성시 InvalidProductNameException 발생") + @NullAndEmptySource + void createProductNameFail(String name) { + + // when & then + assertThrows(InvalidProductNameException.class, () -> ProductName.create(productNamePolicy, name)); + } + + @ParameterizedTest + @DisplayName("실패: 비속어가 포함된 ProductName 생성시 InvalidProductNameException 발생") + @ValueSource(strings = {"비속어", "욕설이 포함된 이름"}) + void createProductNameFailWhenContainsProfanity(String name) { + // when & then + assertThrows(InvalidProductNameException.class, () -> ProductName.create(productNamePolicy, name)); + } +} \ No newline at end of file diff --git a/src/test/java/kitchenpos/core/products/tobe/domain/ProductPriceTest.java b/src/test/java/kitchenpos/core/products/tobe/domain/ProductPriceTest.java new file mode 100644 index 000000000..939ac2601 --- /dev/null +++ b/src/test/java/kitchenpos/core/products/tobe/domain/ProductPriceTest.java @@ -0,0 +1,75 @@ +package kitchenpos.core.products.tobe.domain; + +import static org.junit.jupiter.api.Assertions.*; + +import kitchenpos.config.UnitTest; +import kitchenpos.core.products.tobe.domain.exception.InvalidProductPriceException; +import kitchenpos.core.shared.value.Money; +import kitchenpos.core.shared.value.Quantity; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.math.BigDecimal; + +import static org.junit.jupiter.api.Assertions.*; + +@UnitTest +@DisplayName("[Product] ProductPrice 테스트") +class ProductPriceTest { + + @Test + @DisplayName("성공: 유효한 금액으로 ProductPrice 생성") + void createProductPriceSuccess() { + // given + Money money = Money.wons(new BigDecimal("10000")); + + // when + ProductPrice productPrice = ProductPrice.of(money); + + // then + assertNotNull(productPrice); + assertEquals(money, productPrice.getPrice()); + } + + @Disabled("Money 객체의 생성자에서 null 체크를 하므로 해당 테스트는 필요 없음") + @Test + @DisplayName("실패: null 금액으로 ProductPrice 생성 시 예외 발생") + void createProductPriceFailWhenNull() { + // given & when & then + assertThrows(InvalidProductPriceException.class, () -> { + ProductPrice.of(null); + }); + } + + @Disabled("Money 객체의 생성자에서 0보다 작은 금액 체크를 하므로 해당 테스트는 필요 없음") + @ParameterizedTest + @DisplayName("실패: 0보다 작은 금액으로 ProductPrice 생성 시 예외 발생") + @ValueSource(strings = {"-1", "-10000"}) + void createProductPriceFailWhenNegative(BigDecimal price) { + // given + Money negativeMoney = Money.wons(price); + + // when & then + assertThrows(InvalidProductPriceException.class, () -> { + ProductPrice.of(negativeMoney); + }); + } + @Test + @DisplayName("성공: 곱하기 연산 테스트") + void multiplyTest() { + // given + Money money = Money.wons(new BigDecimal("5000")); + ProductPrice productPrice = ProductPrice.of(money); + Quantity quantity = Quantity.of(3); // Quantity의 내부 값은 3으로 가정 + + // when + Money result = productPrice.multiply(quantity); + + // then + Money expected = money.multiply(3); + assertEquals(expected, result); + } +} \ No newline at end of file diff --git a/src/test/java/kitchenpos/core/products/tobe/domain/ProductTest.java b/src/test/java/kitchenpos/core/products/tobe/domain/ProductTest.java new file mode 100644 index 000000000..602fdabc7 --- /dev/null +++ b/src/test/java/kitchenpos/core/products/tobe/domain/ProductTest.java @@ -0,0 +1,85 @@ +package kitchenpos.core.products.tobe.domain; + +import kitchenpos.config.UnitTest; +import kitchenpos.core.products.application.dto.CreateProductRequest; +import kitchenpos.core.shared.value.Money; +import kitchenpos.fixture.ProductFixtures; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +@UnitTest +@DisplayName("[Product] Product 테스트") +class ProductTest { + @Test + @DisplayName("성공: 유효한 값으로 Product 생성") + void testCreateProductSuccess() { + // given + CreateProductRequest request = ProductFixtures.createProductRequest("후라이드", 16_000L); + + // when + Product product = Product.create(request.id(), request.name(), request.price()); + + // then + assertNotNull(product); + assertEquals(request.id(), product.getId()); + assertEquals(request.name(), product.getName()); + assertEquals(request.price(), product.getPrice()); + } + + @Test + @DisplayName("실패: null id로 Product 생성 시 예외 발생") + void testCreateProductFailWhenIdIsNull() { + // given + CreateProductRequest request = ProductFixtures.createProductRequest("후라이드", 16_000L); + + // when & then + Exception exception = assertThrows(NullPointerException.class, () -> + Product.create(null, request.name(), request.price()) + ); + assertTrue(exception.getMessage().contains("id는 null이 될 수 없습니다.")); + } + + @Test + @DisplayName("실패: null name으로 Product 생성 시 예외 발생") + void testCreateProductFailWhenNameIsNull() { + // given + CreateProductRequest request = ProductFixtures.createProductRequest("후라이드", 16_000L); + + // when & then + Exception exception = assertThrows(NullPointerException.class, () -> + Product.create(request.id(), null, request.price()) + ); + assertTrue(exception.getMessage().contains("name은 null이 될 수 없습니다.")); + } + + @Test + @DisplayName("실패: null price로 Product 생성 시 예외 발생") + void testCreateProductFailWhenPriceIsNull() { + // given + CreateProductRequest request = ProductFixtures.createProductRequest("후라이드", 16_000L); + + // when & then + Exception exception = assertThrows(NullPointerException.class, () -> + Product.create(request.id(), request.name(), null) + ); + assertTrue(exception.getMessage().contains("price은 null이 될 수 없습니다.")); + } + + @Test + @DisplayName("성공: changePrice를 통해 Product 가격 변경") + void testChangePrice() { + // given + CreateProductRequest request = ProductFixtures.createProductRequest("후라이드", 16_000L); + Product product = Product.create(request.id(), request.name(), request.price()); + + ProductPrice newPrice = ProductPrice.of(Money.wons(15_000)); + + // when + product.changePrice(newPrice); + + // then + assertEquals(newPrice, product.getPrice()); + } +} \ No newline at end of file diff --git a/src/test/java/kitchenpos/core/shared/value/MoneyTest.java b/src/test/java/kitchenpos/core/shared/value/MoneyTest.java new file mode 100644 index 000000000..82ec93ac9 --- /dev/null +++ b/src/test/java/kitchenpos/core/shared/value/MoneyTest.java @@ -0,0 +1,111 @@ +package kitchenpos.core.shared.value; + +import kitchenpos.config.UnitTest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; + +import static org.junit.jupiter.api.Assertions.*; + + +@UnitTest +@DisplayName("[Shared] Money 테스트") +class MoneyTest { + + @Test + @DisplayName("성공: wons(long)으로 Money 생성") + void createMoneyFromLong() { + Money money = Money.wons(1000); + assertNotNull(money); + assertEquals(BigDecimal.valueOf(1000), money.getAmount()); + } + + @Test + @DisplayName("성공: wons(BigDecimal)으로 Money 생성") + void createMoneyFromBigDecimal() { + Money money = Money.wons(BigDecimal.valueOf(5000)); + assertNotNull(money); + assertEquals(BigDecimal.valueOf(5000), money.getAmount()); + } + + @Test + @DisplayName("실패: null 금액으로 Money 생성 시 IllegalArgumentException 예외 발생") + void createMoneyFailWhenNull() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> + Money.wons((BigDecimal) null) + ); + assertTrue(exception.getMessage().contains("금액은 null이 될 수 없습니다.")); + } + + @Test + @DisplayName("실패: 음수 금액으로 Money 생성 시 IllegalArgumentException 예외 발생") + void createMoneyFailWhenNegative() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> + Money.wons(-100L) + ); + assertTrue(exception.getMessage().contains("금액은 0보다 작을 수 없습니다.")); + } + + @Test + @DisplayName("성공: Money.ZERO는 0원이어야 함") + void moneyZeroTest() { + Money zero = Money.ZERO; + assertNotNull(zero); + assertEquals(BigDecimal.ZERO, zero.getAmount()); + } + + @Test + @DisplayName("성공: add 연산 테스트") + void testAdd() { + Money money1 = Money.wons(1000); + Money money2 = Money.wons(2000); + Money result = money1.add(money2); + Money expected = Money.wons(3000); + assertEquals(expected.getAmount(), result.getAmount()); + } + + @Test + @DisplayName("성공: multiply 연산 테스트") + void testMultiply() { + Money money = Money.wons(500); + Money result = money.multiply(4); + Money expected = Money.wons(2000); + assertEquals(expected.getAmount(), result.getAmount()); + } + + @Test + @DisplayName("성공: isLessThan 연산 테스트") + void testIsLessThan() { + Money money1 = Money.wons(1000); + Money money2 = Money.wons(2000); + assertTrue(money1.isLessThan(money2)); + assertFalse(money2.isLessThan(money1)); + } + + @Test + @DisplayName("성공: isBiggerThan 연산 테스트") + void testIsBiggerThan() { + Money money1 = Money.wons(3000); + Money money2 = Money.wons(2000); + assertTrue(money1.isBiggerThan(money2)); + assertFalse(money2.isBiggerThan(money1)); + } + + @Test + @DisplayName("성공: isEqual 연산 테스트") + void testIsEqual() { + Money money1 = Money.wons(1500); + Money money2 = Money.wons(1500); + Money money3 = Money.wons(2000); + assertTrue(money1.isEqual(money2)); + assertFalse(money1.isEqual(money3)); + } + + @Test + @DisplayName("성공: toString 테스트") + void testToString() { + Money money = Money.wons(2500); + assertEquals("2500원", money.toString()); + } +} \ No newline at end of file diff --git a/src/test/java/kitchenpos/core/shared/value/QuantityTest.java b/src/test/java/kitchenpos/core/shared/value/QuantityTest.java new file mode 100644 index 000000000..bda51b927 --- /dev/null +++ b/src/test/java/kitchenpos/core/shared/value/QuantityTest.java @@ -0,0 +1,59 @@ +package kitchenpos.core.shared.value; + +import kitchenpos.config.UnitTest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +@UnitTest +@DisplayName("[Shared] Quantity 테스트") +class QuantityTest { + + @Test + @DisplayName("성공: 유효한 수량으로 Quantity 생성") + void createQuantitySuccess() { + // given + long validValue = 5; + // when + Quantity quantity = Quantity.of(validValue); + // then + assertNotNull(quantity); + assertEquals(validValue, quantity.getValue()); + } + + @Test + @DisplayName("실패: 음수 수량으로 Quantity 생성 시 IllegalArgumentException 예외 발생") + void createQuantityFailWhenNegative() { + // given + long negativeValue = -1; + // when & then + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> + Quantity.of(negativeValue) + ); + assertTrue(exception.getMessage().contains("수량은 음수가 될 수 없습니다.")); + } + + @Test + @DisplayName("성공: toString 테스트") + void testToString() { + // given + Quantity quantity = Quantity.of(10); + // when + String result = quantity.toString(); + // then + assertEquals("10 개", result); + } + + @Test + @DisplayName("성공: isEqual 테스트") + void testEquality() { + // given + Quantity q1 = Quantity.of(5); + Quantity q2 = Quantity.of(5); + Quantity q3 = Quantity.of(6); + // then + assertEquals(q1, q2); + assertNotEquals(q1, q3); + } +} \ No newline at end of file diff --git a/src/test/java/kitchenpos/fixture/ProductFixtures.java b/src/test/java/kitchenpos/fixture/ProductFixtures.java index 420f319a2..ddc2437bb 100644 --- a/src/test/java/kitchenpos/fixture/ProductFixtures.java +++ b/src/test/java/kitchenpos/fixture/ProductFixtures.java @@ -3,14 +3,16 @@ import kitchenpos.core.products.application.dto.CreateProductRequest; import kitchenpos.core.products.tobe.domain.*; import kitchenpos.core.products.tobe.domain.support.DefaultProductNamePolicy; -import kitchenpos.core.products.tobe.domain.support.UUIDBasedProductIdGenerator; import kitchenpos.core.shared.domain.ProfanityChecker; +import kitchenpos.core.shared.identifier.ProductId; import kitchenpos.core.shared.value.Money; -import kitchenpos.products.application.FakeProfanityChecker; +import kitchenpos.core.products.application.FakeProfanityChecker; + +import java.util.UUID; public class ProductFixtures { - private static ProductIdGenerator productIdGenerator = new UUIDBasedProductIdGenerator(); + private static ProductIdGenerator productIdGenerator = () -> ProductId.of(UUID.randomUUID()); private static ProfanityChecker profanityChecker = new FakeProfanityChecker(); private static ProductNamePolicy productNamePolicy = new DefaultProductNamePolicy(profanityChecker); diff --git a/src/test/java/kitchenpos/products/application/FakeProductNamePolicy.java b/src/test/java/kitchenpos/products/application/FakeProductNamePolicy.java deleted file mode 100644 index 2cf7a7f2d..000000000 --- a/src/test/java/kitchenpos/products/application/FakeProductNamePolicy.java +++ /dev/null @@ -1,20 +0,0 @@ -package kitchenpos.products.application; - -import kitchenpos.core.products.tobe.domain.ProductNamePolicy; -import kitchenpos.core.products.tobe.domain.support.ProductNameValidationResult; -import kitchenpos.core.shared.domain.ProfanityChecker; - -import java.util.List; - -public class FakeProductNamePolicy implements ProductNamePolicy { - - private final ProfanityChecker profanityChecker = new FakeProfanityChecker(); - - @Override - public ProductNameValidationResult validate(String name) { - if (profanityChecker.containsProfanity(name)) { - return new ProductNameValidationResult(false, List.of("비속어가 포함되어 있습니다.")); - } - return new ProductNameValidationResult(true, List.of()); - } -} diff --git a/src/test/java/kitchenpos/products/application/TobeInMemoryProductRepository.java b/src/test/java/kitchenpos/products/application/TobeInMemoryProductRepository.java deleted file mode 100644 index e3c10d7f5..000000000 --- a/src/test/java/kitchenpos/products/application/TobeInMemoryProductRepository.java +++ /dev/null @@ -1,36 +0,0 @@ -package kitchenpos.products.application; - - -import kitchenpos.core.products.tobe.domain.Product; -import kitchenpos.core.products.tobe.domain.TobeProductRepository; -import kitchenpos.core.shared.identifier.ProductId; - -import java.util.*; - -public class TobeInMemoryProductRepository implements TobeProductRepository { - private final Map products = new HashMap<>(); - - @Override - public Product save(final Product product) { - products.put(product.getId(), product); - return product; - } - - @Override - public Optional findById(final ProductId id) { - return Optional.ofNullable(products.get(id)); - } - - @Override - public List findAll() { - return new ArrayList<>(products.values()); - } - - @Override - public List findAllByIdIn(final List ids) { - return products.values() - .stream() - .filter(product -> ids.contains(product.getId())) - .toList(); - } -} From a8f25d6e331430c48955b11ff7d2eee84cb6a0b9 Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Mon, 3 Mar 2025 19:12:13 +0900 Subject: [PATCH 12/14] =?UTF-8?q?chore(product):=20flyway=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kitchenpos/data/flyway/FlywayConfig.java | 16 ++++++++++++++++ .../db/migration/V3__temp_product_tobe.sql | 6 ++++-- .../db/migration/V4__Insert_product_tobe.sql | 12 ++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 src/main/java/kitchenpos/data/flyway/FlywayConfig.java create mode 100644 src/main/resources/db/migration/V4__Insert_product_tobe.sql diff --git a/src/main/java/kitchenpos/data/flyway/FlywayConfig.java b/src/main/java/kitchenpos/data/flyway/FlywayConfig.java new file mode 100644 index 000000000..ffe727623 --- /dev/null +++ b/src/main/java/kitchenpos/data/flyway/FlywayConfig.java @@ -0,0 +1,16 @@ +package kitchenpos.data.flyway; + +import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FlywayConfig { + @Bean + public FlywayMigrationStrategy cleanMigrateStrategy() { + return flyway -> { + flyway.repair(); + flyway.migrate(); + }; + } +} diff --git a/src/main/resources/db/migration/V3__temp_product_tobe.sql b/src/main/resources/db/migration/V3__temp_product_tobe.sql index 5c2956f09..0c20cc74b 100644 --- a/src/main/resources/db/migration/V3__temp_product_tobe.sql +++ b/src/main/resources/db/migration/V3__temp_product_tobe.sql @@ -1,7 +1,9 @@ create table product_tobe ( - id varchar(255) not null + id binary(16) not null primary key, name varchar(255) not null, - price bigint not null + price decimal(19, 2) not null ); + + diff --git a/src/main/resources/db/migration/V4__Insert_product_tobe.sql b/src/main/resources/db/migration/V4__Insert_product_tobe.sql new file mode 100644 index 000000000..0725d14ef --- /dev/null +++ b/src/main/resources/db/migration/V4__Insert_product_tobe.sql @@ -0,0 +1,12 @@ +insert into product_tobe (id, name, price) +values (x'3b52824434f7406bbb7e690912f66b10', '후라이드', 16000); +insert into product_tobe (id, name, price) +values (x'c5ee925c3dbb4941b825021446f24446', '양념치킨', 16000); +insert into product_tobe (id, name, price) +values (x'625c6fc4145d408f8dd533c16ba26064', '반반치킨', 16000); +insert into product_tobe (id, name, price) +values (x'4721ee722ff3417fade3acd0a804605b', '통구이', 16000); +insert into product_tobe (id, name, price) +values (x'0ac16db71b024a87b9c1e7d8f226c48d', '간장치킨', 17000); +insert into product_tobe (id, name, price) +values (x'7de4b8affa0f4391aaa9c61ea9b40f83', '순살치킨', 17000); From 5d6326449a248100defba2116aedce6aa88b7911 Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Mon, 3 Mar 2025 19:12:35 +0900 Subject: [PATCH 13/14] =?UTF-8?q?feat(product):=20=EA=B3=B5=ED=86=B5=20Agg?= =?UTF-8?q?regateRoot=20=ED=83=80=EC=9E=85=20=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/shared/domain/AggregateRoot.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/main/java/kitchenpos/core/shared/domain/AggregateRoot.java diff --git a/src/main/java/kitchenpos/core/shared/domain/AggregateRoot.java b/src/main/java/kitchenpos/core/shared/domain/AggregateRoot.java new file mode 100644 index 000000000..56a697de7 --- /dev/null +++ b/src/main/java/kitchenpos/core/shared/domain/AggregateRoot.java @@ -0,0 +1,34 @@ +package kitchenpos.core.shared.domain; + +import jakarta.persistence.Transient; +import org.springframework.data.domain.AfterDomainEventPublication; +import org.springframework.data.domain.DomainEvents; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public abstract class AggregateRoot, TID> extends DomainEntity { + @Transient + private final transient List domainEvents = new ArrayList(); + + public AggregateRoot() { + } + + protected void registerEvent(T event) { + Assert.notNull(event, "Domain event must not be null"); + this.domainEvents.add(event); + } + + @AfterDomainEventPublication + protected void clearDomainEvents() { + this.domainEvents.clear(); + } + + @DomainEvents + protected Collection domainEvents() { + return Collections.unmodifiableList(this.domainEvents); + } +} From cae28ff512f7bb6ccc8244e48b42aaade9e0bb0d Mon Sep 17 00:00:00 2001 From: nomoreFt Date: Wed, 5 Mar 2025 00:11:47 +0900 Subject: [PATCH 14/14] =?UTF-8?q?refactor(product):=20ValueObject=20?= =?UTF-8?q?=EC=9E=90=EC=9E=98=ED=95=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/products/tobe/domain/ProductName.java | 3 ++- .../core/products/tobe/domain/ProductPrice.java | 2 ++ .../kitchenpos/core/shared/identifier/ProductId.java | 10 +++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/ProductName.java b/src/main/java/kitchenpos/core/products/tobe/domain/ProductName.java index 96bfab9c9..0eb28bbfb 100644 --- a/src/main/java/kitchenpos/core/products/tobe/domain/ProductName.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/ProductName.java @@ -6,6 +6,7 @@ import kitchenpos.core.products.tobe.domain.support.ProductNameValidationResult; import kitchenpos.core.shared.domain.ProfanityChecker; import kitchenpos.core.shared.domain.ValueObject; +import org.jetbrains.annotations.NotNull; @Embeddable public class ProductName extends ValueObject { @@ -19,7 +20,7 @@ private ProductName(String name) { this.name = name; } - public static ProductName create(ProductNamePolicy policy, String name) { + public static ProductName create(@NotNull ProductNamePolicy policy, String name) { ProductNameValidationResult result = policy.validate(name); if (!result.valid()) { throw new InvalidProductNameException((String.join("; ", result.errorMessages()))); diff --git a/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java b/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java index 268a95084..034802a8c 100644 --- a/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java +++ b/src/main/java/kitchenpos/core/products/tobe/domain/ProductPrice.java @@ -1,6 +1,7 @@ package kitchenpos.core.products.tobe.domain; import jakarta.persistence.Embeddable; +import jakarta.persistence.Transient; import kitchenpos.core.products.tobe.domain.exception.InvalidProductPriceException; import kitchenpos.core.shared.value.Money; import kitchenpos.core.shared.domain.ValueObject; @@ -35,6 +36,7 @@ public Money getPrice() { @Override + @Transient protected Object[] getEqualityFields() { return new Object[] { price }; } diff --git a/src/main/java/kitchenpos/core/shared/identifier/ProductId.java b/src/main/java/kitchenpos/core/shared/identifier/ProductId.java index a22c16a92..d36a082a9 100644 --- a/src/main/java/kitchenpos/core/shared/identifier/ProductId.java +++ b/src/main/java/kitchenpos/core/shared/identifier/ProductId.java @@ -1,13 +1,15 @@ package kitchenpos.core.shared.identifier; import jakarta.persistence.Embeddable; +import jakarta.persistence.Transient; import kitchenpos.core.products.tobe.domain.exception.InvalidProductIdException; +import kitchenpos.core.shared.domain.ValueObject; import java.io.Serializable; import java.util.UUID; @Embeddable -public class ProductId implements Serializable { +public class ProductId extends ValueObject { private UUID value; @@ -28,4 +30,10 @@ public static ProductId of(UUID value) { public UUID getValue() { return value; } + + @Override + @Transient + protected Object[] getEqualityFields() { + return new Object[] { value }; + } } \ No newline at end of file