Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions src/main/java/kitchenpos/menus/tobe/domain/MenuProduct.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package kitchenpos.menus.tobe.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import kitchenpos.products.tobe.domain.Product;

import java.util.UUID;

@Table(name = "menu_product")
@Entity
public class MenuProduct {
@Column(name = "seq")
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long seq;

@ManyToOne(optional = false)
@JoinColumn(
name = "product_id",
columnDefinition = "binary(16)",
foreignKey = @ForeignKey(name = "fk_menu_product_to_product")
)
private Product product;

@Column(name = "quantity", nullable = false)
private long quantity;

@Transient
private UUID productId;

public MenuProduct() {
}

public Long getSeq() {
return seq;
}

public void setSeq(final Long seq) {
this.seq = seq;
}

public Product getProduct() {
return product;
}

public void setProduct(final Product product) {
this.product = product;
}

public long getQuantity() {
return quantity;
}

public void setQuantity(final long quantity) {
this.quantity = quantity;
}

public UUID getProductId() {
return productId;
}

public void setProductId(final UUID productId) {
this.productId = productId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package kitchenpos.products.tobe.application;

import kitchenpos.menus.domain.Menu;
import kitchenpos.menus.domain.MenuProduct;
import kitchenpos.menus.domain.MenuRepository;
import kitchenpos.products.tobe.domain.Product;
import kitchenpos.products.tobe.domain.ProductName;
import kitchenpos.products.tobe.domain.ProductRepository;
import kitchenpos.products.infra.PurgomalumClient;
import kitchenpos.products.tobe.domain.Price;
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.UUID;

@Service
public class ProductService {
private final ProductRepository productRepository;
private final MenuRepository menuRepository;
private final PurgomalumClient purgomalumClient;

public ProductService(
final ProductRepository productRepository,
final MenuRepository menuRepository,
final PurgomalumClient purgomalumClient
) {
this.productRepository = productRepository;
this.menuRepository = menuRepository;
this.purgomalumClient = purgomalumClient;
}

@Transactional
public Product create(final Product request) {
final String name = request.getName().getName();
final Price price = new Price(request.getPrice().getPrice());
final ProductName productName = new ProductName(name, purgomalumClient);
final Product product = new Product();
product.setId(UUID.randomUUID());
product.setName(productName);
product.setPrice(price);
return productRepository.save(product);
}

@Transactional
public Product changePrice(final UUID productId, final Product request) {
final Price price = new Price(request.getPrice().getPrice());
final Product product = productRepository.findById(productId)
.orElseThrow(NoSuchElementException::new);
product.setPrice(price);
final List<Menu> 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);
}
}
return product;
}

@Transactional(readOnly = true)
public List<Product> findAll() {
return productRepository.findAll();
}
}
62 changes: 62 additions & 0 deletions src/main/java/kitchenpos/products/tobe/domain/Price.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package kitchenpos.products.tobe.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;

import java.math.BigDecimal;
import java.util.Objects;

@Embeddable
public class Price {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


@Column(name = "price", nullable = false)
private BigDecimal price;

protected Price() {
}

public Price(Long price) {
if (Objects.isNull(price)) {
throw new IllegalArgumentException();
}
var tempPrice = BigDecimal.valueOf(price);
validate(tempPrice);
this.price = tempPrice;
}

public Price(BigDecimal price) {
validate(price);
this.price = price;
}

public void validate(BigDecimal price) {
if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException();
}
}

public BigDecimal multiply(BigDecimal bigDecimal) {
return price.multiply(bigDecimal);
}

public BigDecimal getPrice() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VO는 immutable 해야하는데, VO을 변경하는 setPrice가 필요할지 고민해보면 좋을 것 같아요~!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네넵! 불변이라 새로 생성하는 방법으로 로직 및 테스트가 되어있습니다! 제거하겠습니다


@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Price price1 = (Price) o;
return Objects.equals(price, price1.price);
}

@Override
public int hashCode() {
return Objects.hashCode(price);
}
}
46 changes: 46 additions & 0 deletions src/main/java/kitchenpos/products/tobe/domain/Product.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package kitchenpos.products.tobe.domain;

import jakarta.persistence.*;

import java.util.UUID;

@Table(name = "product")
@Entity
public class Product {
@Column(name = "id", columnDefinition = "binary(16)")
@Id
private UUID id;

@Embedded
private ProductName name;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VO 사용 👍


@Embedded
private Price price;

public Product() {
}

public UUID getId() {
return id;
}

public void setId(final UUID id) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setter를 의미있게 변경해보면 어떨까요?
단순 Setter가 필요없다면 다른 방식으로 풀어보는 것도 좋을 것 같네요~! 😄

Copy link
Author

@wotjd4305 wotjd4305 Mar 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Product의 ID만을 세팅하는 경우는 없고, 생성하기 위해서 이름, 가격을 주입 받고 있습니다!
제거하고 생성자로 생성하는게 나을 것 같아요

this.id = id;
}

public ProductName getName() {
return name;
}

public void setName(final ProductName name) {
this.name = name;
}

public Price getPrice() {
return price;
}

public void setPrice(Price price) {
this.price = price;
}
}
55 changes: 55 additions & 0 deletions src/main/java/kitchenpos/products/tobe/domain/ProductName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package kitchenpos.products.tobe.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import kitchenpos.products.infra.PurgomalumClient;

import java.math.BigDecimal;
import java.util.Objects;

@Embeddable
public class ProductName {

@Column(name = "name", nullable = false)
private String name;

protected ProductName() {
}

public ProductName(String name, PurgomalumClient purgomalumClient) {
validate(name, purgomalumClient);
this.name = name;
}

public void validate(String name, PurgomalumClient purgomalumClient) {
if (Objects.isNull(name) || name.trim().isEmpty()) {
throw new IllegalArgumentException();
}

if (purgomalumClient.containsProfanity(name)) {
throw new IllegalArgumentException();
}
this.name = name;
}
Comment on lines +19 to +33
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ProductName의 역할이 무엇인지 알 수 있겠네요~! 👍


public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setName이 필요한지 고민해보면 좋을 것 같아요~!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용하는 부분이 없네요!..
제거하겠습니다


@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProductName that = (ProductName) o;
return Objects.equals(name, that.name);
}

@Override
public int hashCode() {
return Objects.hashCode(name);
}
Comment on lines +39 to +50
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

꼭 필요한 선언이죠~! 👍

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package kitchenpos.products.tobe.domain;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

public interface ProductRepository {
Product save(Product product);

Optional<Product> findById(UUID id);

List<Product> findAll();

List<Product> findAllByIdIn(List<UUID> ids);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package kitchenpos.products.tobe.application;

import kitchenpos.products.tobe.domain.Product;
import kitchenpos.products.tobe.domain.ProductRepository;

import java.util.*;

public class InMemoryProductRepository implements ProductRepository {
private final Map<UUID, Product> products = new HashMap<>();

@Override
public Product save(final Product product) {
products.put(product.getId(), product);
return product;
}

@Override
public Optional<Product> findById(final UUID id) {
return Optional.ofNullable(products.get(id));
}

@Override
public List<Product> findAll() {
return new ArrayList<>(products.values());
}

@Override
public List<Product> findAllByIdIn(final List<UUID> ids) {
return products.values()
.stream()
.filter(product -> ids.contains(product.getId()))
.toList();
}
}
Loading