Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Step2 리펙터링(메뉴) 입니다. #103

Open
wants to merge 7 commits into
base: baekseungyeol
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kitchenpos.products.tobe.menu.domain;

import kitchenpos.products.tobe.support.Value;

import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class DisplayedName extends Value<DisplayedName> {

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

protected DisplayedName() {
}

public DisplayedName(String name, Profanities profanities) {
if (profanities.contains(name)) {
throw new IllegalArgumentException("이름에는 비속어가 포함될 수 없습니다.");
}
this.name = name;
}

}

71 changes: 71 additions & 0 deletions src/main/java/kitchenpos/products/tobe/menu/domain/Menu.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package kitchenpos.products.tobe.menu.domain;

import kitchenpos.menus.domain.MenuGroup;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;


@Table(name = "menu")
@Entity
public class Menu {
@Column(name = "id", columnDefinition = "varbinary(16)")
@Id
private UUID id;

@Embedded
private DisplayedName name;

@Embedded
private MenuPrice price;

@Embedded
private MenuProducts menuProducts;


@ManyToOne(optional = false)
@JoinColumn(
name = "menu_group_id",
columnDefinition = "varbinary(16)",
foreignKey = @ForeignKey(name = "fk_menu_to_menu_group")
)
private MenuGroup menuGroup;

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



@Transient
private UUID menuGroupId;

protected Menu() {
}

private Menu(final DisplayedName name, final BigDecimal price, final List<MenuProduct> menuProducts) {
this(null, name, new MenuPrice(price), menuProducts);
}

public Menu(final UUID id, final DisplayedName name, final BigDecimal price, final List<MenuProduct> menuProducts) {
this(id, name, new MenuPrice(price), menuProducts);
}

public Menu(final UUID id, final DisplayedName name, final MenuPrice price, final List<MenuProduct> menuProducts) {
validate(price, menuProducts);
this.id = id;
this.name = name;
this.price = price;
this.menuProducts = new MenuProducts(menuProducts);
}

private void validate(final MenuPrice price, final List<MenuProduct> menuProducts) {
final int sum = menuProducts.stream().mapToInt(MenuProduct::amount).sum();
if (price.isGreaterThan(sum)) {
throw new IllegalArgumentException("메뉴의 가격은 메뉴 상품 금액의 합보다 클 수 없습니다.");
}
}


}
34 changes: 34 additions & 0 deletions src/main/java/kitchenpos/products/tobe/menu/domain/MenuPrice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package kitchenpos.products.tobe.menu.domain;

import kitchenpos.products.tobe.support.Value;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.math.BigDecimal;
import java.util.Objects;

@Embeddable
public class MenuPrice extends Value<MenuPrice> {

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

protected MenuPrice() {
}

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

private void validate(BigDecimal value) {
if (Objects.isNull(value) || value.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("가격은 비어있거나 음수가 될 수 없습니다.");
}
}


public boolean isGreaterThan(final int price) {
return this.price.intValue() > price;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package kitchenpos.products.tobe.menu.domain;

import kitchenpos.products.domain.Product;

import javax.persistence.*;
import java.util.UUID;


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

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

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


protected MenuProduct() {
}

public MenuProduct(UUID productId, int price, int quantity) {
this.productId = productId;
this.price = price;
this.quantity = quantity;
}

public int amount() {
return price * quantity;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package kitchenpos.products.tobe.menu.domain;

import kitchenpos.products.tobe.support.Value;

import javax.persistence.*;
import java.util.List;

@Embeddable
public class MenuProducts extends Value<MenuProducts> {

Choose a reason for hiding this comment

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

menu 도메인이 별도의 서버로 분리가 된다면 Value 에 대한 의존성은 어떨까요?

중복을 제거하기 위한 상속은 좋으나 BoundedContext 단위로 도메인을 생각해보면 좋을 것 같아요



@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(
name = "menu_id",
nullable = false,
columnDefinition = "varbinary(16)",
foreignKey = @ForeignKey(name = "fk_menu_product_to_menu")
)
private List<MenuProduct> menuProducts;


protected MenuProducts() {
}

public MenuProducts(List<MenuProduct> menuProducts) {
this.menuProducts = menuProducts;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package kitchenpos.products.tobe.menu.domain;

interface Profanities {
boolean contains(String text);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package kitchenpos.products.tobe.domain;
package kitchenpos.products.tobe.product.domain;


import kitchenpos.products.infra.PurgomalumClient;

import javax.persistence.*;
import javax.validation.constraints.Email;
import java.math.BigDecimal;
import java.util.UUID;

Expand Down Expand Up @@ -39,4 +38,6 @@ public BigDecimal getPrice() {
public void changePrice(BigDecimal price) {
this.price = new ProductPrice(price);
}


}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package kitchenpos.products.tobe.domain;
package kitchenpos.products.tobe.product.domain;

import kitchenpos.products.infra.PurgomalumClient;
import kitchenpos.products.tobe.support.Value;
import org.apache.logging.log4j.util.Strings;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.util.Objects;

@Embeddable
public class ProductName {
public class ProductName extends Value<ProductName> {

private static final String PRODUCT_NAME_EMPTY_MESSAGE = "상품의 이름은 비어있을 수 없습니다.";
private static final String PRODUCT_NAME_PROFANITY_MESSAGE = "상품의 이름은 비속어가 포함될 수 없습니다.";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package kitchenpos.products.tobe.domain;
package kitchenpos.products.tobe.product.domain;

import kitchenpos.products.tobe.support.Value;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.math.BigDecimal;
import java.util.Objects;

@Embeddable
public class ProductPrice {
public class ProductPrice extends Value<ProductPrice> {

private static final String PRODUCT_PRICE_EMPTY_MESSAGE = "상품의 가격은 비어있을 수 없습니다.";
private static final String PRODUCT_PRICE_NEGATIVE_MESSAGE = "상품의 가격은 음수일 수 없습니다.";
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/kitchenpos/products/tobe/support/Value.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package kitchenpos.products.tobe.support;

import java.util.Arrays;
import java.util.Objects;

public abstract class Value<T extends Value<T>> {

Choose a reason for hiding this comment

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

추상화를 잘 표현해주셨어요 👍
하지만 util 및 support 와 같이 여러 객체에서 사용을 한다면 어떤 단점이 있을까요?

OOP 와 DDD 의 차이를 한번 생각해 보아요 :)


public boolean equals(final Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;

return Arrays.stream(getClass().getDeclaredFields())
.allMatch(field -> {
field.setAccessible(true);
try {
return Objects.equals(field.get(this),field.get(o));
} catch (IllegalAccessException e) {
e.printStackTrace();
return false;
}
});
}

public int hashCode() {
return Objects.hash(
Arrays.stream(getClass().getDeclaredFields())
.map(field -> {
field.setAccessible(true);
try {
return field.get(this);
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}).toArray()
);

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

import kitchenpos.products.tobe.menu.domain.DisplayedName;
import kitchenpos.products.tobe.domain.Menu.Profanities;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class DisplayedNameTest {
private final Profanities profanities = new FakeProfanities();

@ValueSource(strings = {"욕설", "비속어"})
@ParameterizedTest
void 이름에_욕설이_포함될_수_없다(final String value) {
assertThatThrownBy(() -> new DisplayedName(value, profanities))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 동등성() {
final DisplayedName name1 = new DisplayedName("치킨", profanities);
final DisplayedName name2 = new DisplayedName("치킨", profanities);
assertThat(name1).isEqualTo(name2);
}
}

18 changes: 18 additions & 0 deletions src/test/java/kitchenpos/products/tobe/domain/FakeProfanities.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package kitchenpos.products.tobe.domain;

import kitchenpos.products.tobe.domain.Menu.Profanities;

import java.util.Arrays;
import java.util.List;

class FakeProfanities implements Profanities {
private final List<String> values = Arrays.asList("욕설", "비속어");

@Override
public boolean contains(final String text) {
return values.stream()
.anyMatch(text::contains);
}
}


25 changes: 25 additions & 0 deletions src/test/java/kitchenpos/products/tobe/domain/MenuPriceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kitchenpos.products.tobe.domain;

import kitchenpos.products.tobe.menu.domain.MenuPrice;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class MenuPriceTest {

@Test
void 가격이_음수면_예외가_발생한다() {
assertThatThrownBy(() -> new MenuPrice(BigDecimal.valueOf(-16_000)))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 동등성() {
final MenuPrice price1 = new MenuPrice(BigDecimal.valueOf(16_000));
final MenuPrice price2 = new MenuPrice(BigDecimal.valueOf(16_000));
assertThat(price1).isEqualTo(price2);
}
}
Loading