-
Notifications
You must be signed in to change notification settings - Fork 154
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
3단계 - 리팩터링(매장 식사 주문) #289
base: pawoo0211
Are you sure you want to change the base?
3단계 - 리팩터링(매장 식사 주문) #289
Conversation
루트 엔티티를 제외한 하위 엔티티에서는 래핑한 Vo를 반환하도록 변경
루트 엔티티를 제외한 하위 엔티티에서는 래핑한 Vo를 반환하도록 변경
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요.
추가로 몇 가지 코멘트 남겼습니다.
확인 부탁드려요! 🙂
@Transient | ||
private BigDecimal price; | ||
|
||
@Transient | ||
private UUID menuId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 경우 메뉴 MC 에서 menu name이 변경되는 경우 어떻게 처리 될까요?
얼마 전에 저희 DM 으로 Menu BC에서의 menu와 Order BC에서 Menu는 의미하는 바가 다름을 도출했는데요. 이를 코드로 녹여보는 것은 어떨까요? Order에 Menu 모델이 만들어 질 수도 있지 않을까요?
public OrderLineItem(Long seq, long quantity, BigDecimal price) { | ||
this(seq, quantity, price, null); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여러 생성자를 둬 클라이언트에게 편의를 주는 것은 좋은데요. menu는 없어도 괜찮나요?
|
||
Optional<OrderTable> findBy(UUID id); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional<OrderTable> findBy(UUID id); | |
} | |
Optional<OrderTable> findById(UUID id); | |
} |
@Transient | ||
private BigDecimal price; | ||
|
||
@Transient | ||
private UUID menuId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
price와 menuId는 Transient 로 선언되어도 괜찮나요?
@Column(name = "occupied", nullable = false) | ||
private boolean occupied; | ||
|
||
@Transient | ||
private OrderTableStatus status; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
occupied 와 status 모두 필요할까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
논리적으로 동일하네요 하하..
@EventListener | ||
public void setEmptyTable(CompletedOrderEvent event) { | ||
System.out.println("주문 테이블 빈 테이블 설정"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
필요하다면 로그를 남기는 것은 어떨까요?
package kitchenpos.menus.tobe.domain.common; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
패키지가 조금 어색한 것 같은데요. 모든 BC에서 사용한다면 kitchenpos.common
으로 둬도 괜찮을 것 같긴해요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제가 좀 설명이 부족했던 것 같은데요. price 클래스가 여러개로 보이네요. BC 별로 정의 한다면 명확하게 정의하는 것이 좋을 것 같고 모든 BC에서 사용하면 kitchenpos.common
에 위치할 수 있을 것 같아요.
|
||
@SpringBootTest | ||
@DisplayName("매장 주문 완료 도메인 서비스 테스트") | ||
public class EatInOrderCompletePolicyTest { | ||
@Autowired | ||
private EatInOrderCompletePolicy completePolicy; | ||
@MockBean | ||
private EatInOrderRepository orderRepository; | ||
@MockBean | ||
private OrderTableRepository tableRepository; | ||
|
||
private MenuRepository menuRepository; | ||
private ToBeFixtures toBeFixtures; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
꼼꼼한 테스트 👍
EatInOrderCompletePolicy에 ApplicationEventPublisher를 주입 받고 있어 SpringBootTest를 사용하신 것 같네요. 여러방법이 있지만 ApplicationEventPublisher는 인터페이스입니다. 이를 활용해 fake 객체를 만들어 보는 것도 좋을 것 같아요.
혹은 이벤트를 도메인 모델에서 발급해도 괜찮을 것 같아요. AbstractAggregateRoot
별도로 @RecordApplicationEvents
, ApplicationEvents
도 찾아보면 좋을 것 같아요.
이 부분은 참고만 부탁드립니다. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵, 말씀 감사합니다!
제가 이벤트 기반이 익숙치 않아서 일단 가장 기본적인 형태로 구현했습니다. 말씀해주신 부분들도 학습해보겠습니다~
|
||
public class FakeEatInOrderRepository implements EatInOrderRepository { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
가짜 객체 👍
|
||
@DisplayName("주문 항목 도메인 테스트") | ||
public class OrderLineItemTest { | ||
|
||
@Test | ||
@DisplayName("주문 항목을 생성한다.") | ||
void create() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
위에서도 남겼지만 주문 항목에 menu는 필수가 아닌가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요.
가벼운 코멘트 몇 가지 남겼습니다!
끝이 보이는데요! 마지막까지 힘내시면 좋겠습니다! 🙂
언제든 DM 남겨주세요!
|
||
@Embeddable | ||
public class MenuInEatInOrders { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eatinorders 내부에 Menu 정의 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
기본 생성자는 필요하지 않을까요?
@Transient | ||
private boolean isDisplayedMenu; | ||
|
||
@Transient | ||
private Price price; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Transient 인 이유가 따로 있나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
주문 테이블에서 위 정보를 굳이 저장할 필요성을 못느껴서 Transient를 적용했습니다.
DM에서도 현수님께서 이미 주문된 메뉴는 변경 가능성이 없다고 하셨는데 저 또한 해당 필드를 영속성으로 유지하거나 DB에 저장할 필요성을 못느껴서 Transient를 적용했습니다.
|
||
@Component | ||
public class UpdateMenuInOrders { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 도메인 서비스는 어떤 경우에 사용되나요?
메뉴의 변경에 따라서 이미 주문된 order 내부의 menu를 모두 update 하는 것 같은데요. 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
메뉴의 정보가 바뀌었을 때를 가정하여 만든 도메인 서비스입니다. 실제 요구사항은 없지만 해당 상황을 가정하고 만드는 걸로 이해하여 만들었습니다.
|
||
@Component | ||
public class ChangedMenuSender { | ||
private final ApplicationEventPublisher publisher; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
강의에서 다뤘을 것 같은데요. AbstractAggregateRoot
통해서 이벤트 발급도 가능합니다. :)
@TransactionalEventListener
, @EventListener
키워드도 함께 찾아보면 좋을 것 같아요.
참고만 해주세요~
|
||
public class ToBeFixtures { | ||
public Menu 메뉴_치킨 = new kitchenpos.menus.tobe.domain.entity.Menu( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixtures 도 패키지가 나눠지면 좋을 것 같아요. :)
|
||
public class EatInOrderAccept { | ||
private OrderTableRepository tableRepository; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 클래스는 EatInOrder를 생성하는 시점에 사용될 것 같아요.
MenuInEatInOrders
의 생성 시점은 언제인가요? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bean으로 등록되지 않아도 괜찮나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MenuInEatInOrders 생성 시점도 EatInOrders와 동일한 생성 시점으로 생각하고 있습니다.
MenuInEatInOrders에 대한 유효성을 검증하기 위해서는 EatInOrder 객체가 생성되어 하기에 EatInOrderTest 클래스에서 유효성 검증 테스트 코드 작성했습니다.
package kitchenpos.menus.tobe.domain.common; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제가 좀 설명이 부족했던 것 같은데요. price 클래스가 여러개로 보이네요. BC 별로 정의 한다면 명확하게 정의하는 것이 좋을 것 같고 모든 BC에서 사용하면 kitchenpos.common
에 위치할 수 있을 것 같아요.
@DisplayName("주문 도메인 테스트") | ||
public class OrderTest { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트 클래스 이름이 EatInOrderTest 가 아니네요. 🤔
아래에 대한 요구 사항은 어떤 클래스의 책임일까요? 테스트로 작성되어야 하지 않을까요?
주문한 메뉴의 가격은 실제 메뉴 가격과 일치해야 한다.
@Column(name = "quantity", nullable = false) | ||
private long quantity; | ||
|
||
@Embedded | ||
private Price price; | ||
|
||
@Embedded | ||
private MenuInEatInOrders menuInOrders; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
궁금해서 그러는데 price는 MenuInEatInOrders.price와 동일한가요? 그렇다면 중복인 것 같은데요.
quantity * MenuInEatInOrders.price 라면 직접 계산할 수 도 있을 것 같아서요.
현수님 안녕하세요~ 3단계 진행하여 PR 생성합니다.
먼저 코드가 지저분해져서 보기 어려우실 것 같아 죄송하다는 말씀 먼저 드립니다.. 코드가 지저분해진 이유는 도메인 서비스(ACL)를 강제로 의존하게 되면서 Repository에 주문 테이블이나 메뉴를 반드시 save 해야 하는 강제성 때문에 지저분해졌네요..
부패 방지 레이어(EatInOrderServiceAdapter)를 사용해서 도메인 코드를 보호하려고 했는데 뭔가 보호되지 않은 느낌이라 방향성이 좀 잘못되고 있다고 생각해 현재까지 진행한 부분에 대해서 PR 생성드린 후에 피드백 받아 개선하도록 하겠습니다.
감사합니다.