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 PR #681

Merged
merged 68 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
3a4a92c
refactor(order): 테스트 검증 로직 위치 변경
Dec 11, 2022
f11c9e2
refactor: 서비스 테스트 코드 접근제한자 제거
Dec 11, 2022
d843a0a
refactor: 중복 fixture 리팩토링
Dec 11, 2022
b3a831a
docs(readme): step2 서비스 리팩터링 요구사항 작성
Dec 11, 2022
d06e9e7
refactor(product): 상품 도메인 리팩토링 및 테스트
Dec 11, 2022
236e116
refactor(menuGroup): 메뉴그룹 도메인 리팩토링 및 테스트
Dec 11, 2022
3cec725
refactor(menu): 메뉴 도메인 리팩토링 및 테스트 추가
Dec 11, 2022
ce41530
refactor(orderTable): 주문 테이블 도메인 리팩토링 및 테스트 추가
Dec 11, 2022
8c6aaa2
refactor(orderTable): 단체지정 도메인 리팩토링 및 테스트 추가
Dec 11, 2022
f363ee9
refactor(order): 주문 도메인 리팩토링 및 테스트 추가
Dec 11, 2022
c8ec268
refactor(product): ProductRepository 생성 및 JPA 엔티티 적용
Dec 11, 2022
50db07d
refactor(menuGroup): MenuGroupRepository 생성 및 JPA 엔티티 적용
Dec 11, 2022
f2eee5e
refactor(menu): MenuRepository 생성 및 JPA 엔티티 적용
Dec 11, 2022
f67dfb6
refactor(menu): 테스트 오류 관련 리팩토링
Dec 11, 2022
28cb570
refactor(order): 주문 관련 Repository 생성 및 JPA 엔티티 적용
Dec 12, 2022
d97e55e
refactor(orderTable): 주문테이블 관련 테스트 정상동작 되도록 리팩토링
Dec 12, 2022
bd1727f
refactor(tableGroup): 단체테이블 관련 테스트 정상동작 되도록 리팩토링
Dec 12, 2022
40d085b
refactor(order): 주문 관련 테스트 정상동작 되도록 리팩토링
Dec 12, 2022
07e8f32
refactor(orderTable): 주문 테이블 관련 테스트 정상동작 되도록 리팩토링
Dec 12, 2022
5657830
refactor(dao): dao 파일 제거
Dec 12, 2022
5592eb5
refactor(entity): setter 함수 제거
Dec 12, 2022
a386a70
refactor(dto): setter 함수 제거
Dec 12, 2022
6579b95
refactor: 사용하지 않는 import 제거
Dec 12, 2022
015091e
refactor(tableGroup): 테이블 그룹 도메인 리팩토링
Dec 12, 2022
c396799
refactor(ordertable): 주문테이블 도메인 리팩토링
Dec 12, 2022
9856654
refactor(menu): 메뉴테이블 도메인 리팩토링
Dec 12, 2022
5a5ac0b
refactor(tableGroup): 테이블그룹 fixture 리팩토링
Dec 12, 2022
aa96263
refactor: 불필요한 fixture 함수 삭제
Dec 12, 2022
1458e86
refactor: 사용하지 않는 fixture setup 삭제
Dec 12, 2022
42f14cf
refactor(orderStatus): 주문상태 도메인 테스트코드 작성
Dec 13, 2022
b63221d
refactor(orderLine): 주문목록 도메인 테스트 코드 작성
Dec 13, 2022
247ee60
test(menuProduct): 메뉴상품 도메인 테스트코드 작성
Dec 13, 2022
78cfa66
refactor: dto 구조 개선
Dec 13, 2022
81c7d3a
refactor: 예외 메시지 추가
Dec 13, 2022
6116d98
refactor(orderLine): dto 구조 개선
Dec 13, 2022
9bd78a4
refactor: dto 필드 네이밍 변경
Dec 13, 2022
5f098cc
refactor: 불필요한 ID 매개변수 제거
Dec 13, 2022
81a2fc4
refactor(fixture): 함수 네이밍 통일되도록 수정
Dec 13, 2022
733d2d2
refactor(exception): 예외처리 메시지 추가
Dec 13, 2022
9fe6ee7
refactor(menuProducts): 메뉴상품목록 도메인 테스트 코드 추가
Dec 13, 2022
bffc760
test(orderTable): 주문테이블 도메인 테스트 코드 추가
Dec 13, 2022
13ee7b1
test(order): 주문 도메인 테스트 코드 추가
Dec 13, 2022
14c26c8
test(price): 가격 도메인 테스트 코드 추가
Dec 13, 2022
33d0aab
test(menuProduct): 메뉴상품 도메인 테스트 코드 추가
Dec 13, 2022
5f770d6
test(menu): 메뉴 도메인 테스트 코드 추가
Dec 13, 2022
9fa6e82
test(orderTable): 주문테이블 도메인 테스트 코드 추가
Dec 13, 2022
12d4ffa
test(price): 가격 도메인 테스트 코드 추가
Dec 13, 2022
8de614e
test(tableGroup): 테이블그룹 도메인 테스트 코드 추가
Dec 13, 2022
9925336
test(menuGroupService): 가독성을 위해 중간에 변수로 분리
Dec 14, 2022
15a575f
refactor(orderService): orderLineItem size 체크로직 추가
Dec 14, 2022
691d303
refactor(orderService): menuId 추출을 위한 메소드명 변경
Dec 14, 2022
210412e
refactor(tableGroupService): 주문상태 검증 로직을 위한 함수 생성
Dec 14, 2022
0ae4c29
refactor(orderTable): group, ungroup 메소드 생성
Dec 14, 2022
358ce74
refactor(orderTable): 테이블그룹 생성자 매개변수 제거
Dec 15, 2022
ee3e2ac
refactor(orderTable): addOrder 메소드 네이밍 수정
Dec 15, 2022
54fb454
refactor(orderTable): order에 batch size 설정
Dec 15, 2022
af7c111
refactor(tableGroup): orderTables에 batch size 설정
Dec 15, 2022
f259189
refactor(menuProductResponse): 사용하지 않는 코드 제거
Dec 15, 2022
0929e35
refactor: request 객체에서 toEntity 함수 사용
Dec 15, 2022
efb7771
refactor(menuResponse): toMenuResponse 함수 네이명 변경
Dec 15, 2022
73e2eff
refactor(controller): @RequestMapping 사용
Dec 15, 2022
a6f2836
test(menu): 누락된 displayname 작성
Dec 15, 2022
929031b
refactor(orderStatus): @Enumurate 사용
Dec 15, 2022
c05ceee
refactor(orderTables): 주문테이블 목록 일급컬렉션 사용
Dec 15, 2022
c68807f
refactor(orderStatus): enum description 추가
Dec 15, 2022
3296da2
refactor(errorMessage): 예외 메시지 관리를 위한 클래스 추가
Dec 15, 2022
d3d39b9
refactor(productRepository): 트랜잭션 횟수 줄이기 위해 findAllByIdIn 활용
Dec 16, 2022
f341f49
refactor(menuService): 샹품ID validation 추가
Dec 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,26 @@
### 요구 사항 체크리스트
- [X] 키친포스 요구사항 작성
- [X] 키친포스 테스트 코드 작성

## 2단계 - 서비스 리팩터링
### 요구 사항 체크리스트
- [X] 단위 테스트 가능한 코드에 대해 단위 테스트를 구현한다.
- [X] Menu
- [X] MenuGroup
- [X] MenuProduct
- [X] Order
- [X] OrderLineItem
- [X] OrderStatus
- [X] OrderTable
- [X] Product
- [X] TableGroup
- [X] JPA로 Migration
- [X] Menu
- [X] MenuGroup
- [X] MenuProduct
- [X] Order
- [X] OrderLineItem
- [X] OrderStatus
- [X] OrderTable
- [X] Product
- [X] TableGroup
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.flywaydb:flyway-core'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand Down
1 change: 1 addition & 0 deletions src/main/java/kitchenpos/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
public class Application {
Expand Down
23 changes: 15 additions & 8 deletions src/main/java/kitchenpos/application/MenuGroupService.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
package kitchenpos.application;

import kitchenpos.dao.MenuGroupDao;
import kitchenpos.domain.MenuGroup;
import kitchenpos.dto.MenuGroupRequest;
import kitchenpos.dto.MenuGroupResponse;
import kitchenpos.repository.MenuGroupRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@Transactional(readOnly = true)
public class MenuGroupService {
private final MenuGroupDao menuGroupDao;
private final MenuGroupRepository menuGroupRepository;

public MenuGroupService(final MenuGroupDao menuGroupDao) {
this.menuGroupDao = menuGroupDao;
public MenuGroupService(final MenuGroupRepository menuGroupRepository) {
this.menuGroupRepository = menuGroupRepository;
}

@Transactional
public MenuGroup create(final MenuGroup menuGroup) {
return menuGroupDao.save(menuGroup);
public MenuGroupResponse create(final MenuGroupRequest request) {
MenuGroup menuGroup = menuGroupRepository.save(MenuGroup.of(request.getName()));
return MenuGroupResponse.from(menuGroup);
}

public List<MenuGroup> list() {
return menuGroupDao.findAll();
public List<MenuGroupResponse> list() {
return menuGroupRepository.findAll().stream()
.map(MenuGroupResponse::from)
.collect(Collectors.toList());
}
}
102 changes: 39 additions & 63 deletions src/main/java/kitchenpos/application/MenuService.java
Original file line number Diff line number Diff line change
@@ -1,84 +1,60 @@
package kitchenpos.application;

import kitchenpos.dao.MenuDao;
import kitchenpos.dao.MenuGroupDao;
import kitchenpos.dao.MenuProductDao;
import kitchenpos.dao.ProductDao;
import kitchenpos.domain.Menu;
import kitchenpos.domain.MenuProduct;
import kitchenpos.domain.Product;
import kitchenpos.domain.*;
import kitchenpos.dto.MenuProductRequest;
import kitchenpos.dto.MenuRequest;
import kitchenpos.dto.MenuResponse;
import kitchenpos.repository.MenuGroupRepository;
import kitchenpos.repository.MenuRepository;
import kitchenpos.repository.ProductRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@Transactional(readOnly = true)
public class MenuService {
private final MenuDao menuDao;
private final MenuGroupDao menuGroupDao;
private final MenuProductDao menuProductDao;
private final ProductDao productDao;
private final MenuRepository menuRepository;
private final MenuGroupRepository menuGroupRepository;
private final ProductRepository productRepository;

public MenuService(
final MenuDao menuDao,
final MenuGroupDao menuGroupDao,
final MenuProductDao menuProductDao,
final ProductDao productDao
final MenuRepository menuRepository,
final MenuGroupRepository menuGroupRepository,
final ProductRepository productRepository
) {
this.menuDao = menuDao;
this.menuGroupDao = menuGroupDao;
this.menuProductDao = menuProductDao;
this.productDao = productDao;
this.menuGroupRepository = menuGroupRepository;
this.menuRepository = menuRepository;
this.productRepository = productRepository;
}

@Transactional
public Menu create(final Menu menu) {
final BigDecimal price = menu.getPrice();

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

if (!menuGroupDao.existsById(menu.getMenuGroupId())) {
throw new IllegalArgumentException();
}

final List<MenuProduct> menuProducts = menu.getMenuProducts();

BigDecimal sum = BigDecimal.ZERO;
for (final MenuProduct menuProduct : menuProducts) {
final Product product = productDao.findById(menuProduct.getProductId())
.orElseThrow(IllegalArgumentException::new);
sum = sum.add(product.getPrice().multiply(BigDecimal.valueOf(menuProduct.getQuantity())));
}

if (price.compareTo(sum) > 0) {
throw new IllegalArgumentException();
}

final Menu savedMenu = menuDao.save(menu);

final Long menuId = savedMenu.getId();
final List<MenuProduct> savedMenuProducts = new ArrayList<>();
for (final MenuProduct menuProduct : menuProducts) {
menuProduct.setMenuId(menuId);
savedMenuProducts.add(menuProductDao.save(menuProduct));
}
savedMenu.setMenuProducts(savedMenuProducts);

return savedMenu;
public MenuResponse create(final MenuRequest request) {
Long menuGroupId = request.getMenuGroupId();
MenuGroup menuGroup = menuGroupRepository.findById(menuGroupId)
.orElseThrow(() -> new IllegalArgumentException(String.format("%d에 해당하는 메뉴그룹을 찾을 수 없습니다.", menuGroupId)));
MenuProducts menuProducts = MenuProducts.from(findAllMenuProductsByProductId(request.getMenuProductsRequest()));
Menu menu = Menu.of(request.getName(), request.getPrice(), menuGroup, menuProducts);

return MenuResponse.from(menuRepository.save(menu));
}

public List<Menu> list() {
final List<Menu> menus = menuDao.findAll();
public List<MenuResponse> list() {
return menuRepository.findAll().stream()
.map(MenuResponse::from)
.collect(Collectors.toList());
}

for (final Menu menu : menus) {
menu.setMenuProducts(menuProductDao.findAllByMenuId(menu.getId()));
}
private List<MenuProduct> findAllMenuProductsByProductId(List<MenuProductRequest> menuProductRequests) {
return menuProductRequests.stream()
.map(menuProductRequest -> menuProductRequest.toMenuProduct(findProductById(menuProductRequest.getProductId())))
.collect(Collectors.toList());
}

return menus;
private Product findProductById(final Long id) {
return productRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException(id + "에 해당하는 상품을 찾을 수 없습니다."));
}
Copy link
Member

Choose a reason for hiding this comment

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

productId만 추출해 inquery로 개선해볼 수 있을것같습니다 ㅎㅎ

Copy link
Author

@Gyeom Gyeom Dec 14, 2022

Choose a reason for hiding this comment

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

음 그렇게 되면 toMenuProduct함수를 통해 사용되는 quantity를 사용할 수 있을지... 고민입니다 ㅠㅠ

Copy link
Member

Choose a reason for hiding this comment

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

toMenuProduct는 그대로 quantity를 사용하고
in query로 조회한 데이터의 사이즈와 요청받은 사이즈를 비교해 예외를 처리해보면 어떨까 싶습니다 ㅎㅎ
현재 id를 예외메시지에 같이 나타내고 있어 중간에 몇가지 과정이 더 필요할 수 있겠군요 ㅠ

}
124 changes: 54 additions & 70 deletions src/main/java/kitchenpos/application/OrderService.java
Original file line number Diff line number Diff line change
@@ -1,108 +1,92 @@
package kitchenpos.application;

import kitchenpos.dao.MenuDao;
import kitchenpos.dao.OrderDao;
import kitchenpos.dao.OrderLineItemDao;
import kitchenpos.dao.OrderTableDao;
import kitchenpos.domain.Order;
import kitchenpos.domain.OrderLineItem;
import kitchenpos.domain.OrderStatus;
import kitchenpos.domain.OrderTable;
import kitchenpos.dto.OrderLineItemRequest;
import kitchenpos.dto.OrderRequest;
import kitchenpos.dto.OrderResponse;
import kitchenpos.repository.MenuRepository;
import kitchenpos.repository.OrderRepository;
import kitchenpos.repository.OrderTableRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@Transactional(readOnly = true)
public class OrderService {
private final MenuDao menuDao;
private final OrderDao orderDao;
private final OrderLineItemDao orderLineItemDao;
private final OrderTableDao orderTableDao;
private final OrderRepository orderRepository;
private final MenuRepository menuRepository;
private final OrderTableRepository orderTableRepository;

public OrderService(
final MenuDao menuDao,
final OrderDao orderDao,
final OrderLineItemDao orderLineItemDao,
final OrderTableDao orderTableDao
final OrderRepository orderRepository,
final OrderTableRepository orderTableRepository,
final MenuRepository menuRepository
) {
this.menuDao = menuDao;
this.orderDao = orderDao;
this.orderLineItemDao = orderLineItemDao;
this.orderTableDao = orderTableDao;
this.orderRepository = orderRepository;
this.orderTableRepository = orderTableRepository;
this.menuRepository = menuRepository;
}

@Transactional
public Order create(final Order order) {
final List<OrderLineItem> orderLineItems = order.getOrderLineItems();

if (CollectionUtils.isEmpty(orderLineItems)) {
throw new IllegalArgumentException();
}
public OrderResponse create(final OrderRequest request) {
Long orderTableId = request.getOrderTableId();
OrderTable orderTable = orderTableRepository.findById(orderTableId)
.orElseThrow(() -> new IllegalArgumentException(orderTableId + "에 해당하는 주문테이블을 찾을 수가 없습니다."));
Copy link
Member

Choose a reason for hiding this comment

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

id도 같이 예외메시지에 포함하신거 좋네요 ㅎㅎ
다만 직접스트링으로 선언하는것 보다는 별도의 예외메시지를 관리하는 클래스나 enum을 고려해보시는건 어떨까요??

Copy link
Author

Choose a reason for hiding this comment

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

넵 enum으로 예외메시지 관리할 수 있도록 수정하겠습니다 ~!

List<OrderLineItemRequest> requestOrderLineItems = request.getOrderLineItemsRequest();
validateOrderLineItems(requestOrderLineItems);
List<OrderLineItem> orderLineItems = mapToOrderLineItems(requestOrderLineItems);
Order savedOrder = orderRepository.save(Order.of(orderTable, orderLineItems));

return OrderResponse.from(savedOrder);
}

final List<Long> menuIds = orderLineItems.stream()
.map(OrderLineItem::getMenuId)
private List<OrderLineItem> mapToOrderLineItems(final List<OrderLineItemRequest> requestOrderLineItems) {
return requestOrderLineItems.stream()
.map(orderLineItem -> OrderLineItem.of(orderLineItem.getMenuId(), orderLineItem.getQuantity()))
.collect(Collectors.toList());
}

if (orderLineItems.size() != menuDao.countByIdIn(menuIds)) {
throw new IllegalArgumentException();
}

final OrderTable orderTable = orderTableDao.findById(order.getOrderTableId())
.orElseThrow(IllegalArgumentException::new);

if (orderTable.isEmpty()) {
throw new IllegalArgumentException();
private void validateOrderLineItems(final List<OrderLineItemRequest> orderLineItems) {
if (Objects.isNull(orderLineItems) || orderLineItems.isEmpty()) {
throw new IllegalArgumentException("요청정보에 주문정보가 존재하지 않습니다.");
}

order.setOrderTableId(orderTable.getId());
order.setOrderStatus(OrderStatus.COOKING.name());
order.setOrderedTime(LocalDateTime.now());
final List<Long> menuIds = fetchMenuIdsFrom(orderLineItems);

final Order savedOrder = orderDao.save(order);

final Long orderId = savedOrder.getId();
final List<OrderLineItem> savedOrderLineItems = new ArrayList<>();
for (final OrderLineItem orderLineItem : orderLineItems) {
orderLineItem.setOrderId(orderId);
savedOrderLineItems.add(orderLineItemDao.save(orderLineItem));
if (orderLineItems.size() != countByIdIn(menuIds)) {
throw new IllegalArgumentException("주문정보 중 존재하지 않는 메뉴 정보가 있습니다.");
}
savedOrder.setOrderLineItems(savedOrderLineItems);

return savedOrder;
}

public List<Order> list() {
final List<Order> orders = orderDao.findAll();
private Long countByIdIn(final List<Long> menuIds) {
return menuRepository.countByIdIn(menuIds);
}

for (final Order order : orders) {
order.setOrderLineItems(orderLineItemDao.findAllByOrderId(order.getId()));
}
private List<Long> fetchMenuIdsFrom(final List<OrderLineItemRequest> orderLineItems) {
return orderLineItems.stream()
.map(OrderLineItemRequest::getMenuId)
.collect(Collectors.toList());
}

return orders;
public List<OrderResponse> list() {
return orderRepository.findAll().stream()
.map(OrderResponse::from)
.collect(Collectors.toList());
}

@Transactional
public Order changeOrderStatus(final Long orderId, final Order order) {
final Order savedOrder = orderDao.findById(orderId)
.orElseThrow(IllegalArgumentException::new);

if (Objects.equals(OrderStatus.COMPLETION.name(), savedOrder.getOrderStatus())) {
throw new IllegalArgumentException();
}

final OrderStatus orderStatus = OrderStatus.valueOf(order.getOrderStatus());
savedOrder.setOrderStatus(orderStatus.name());

orderDao.save(savedOrder);
public OrderResponse changeOrderStatus(final Long orderId, final Order order) {
final Order savedOrder = orderRepository.findById(orderId)
.orElseThrow(() -> new IllegalArgumentException(orderId + "에 대한 주문을 찾을 수 없습니다."));

savedOrder.setOrderLineItems(orderLineItemDao.findAllByOrderId(orderId));
savedOrder.changeOrderStatus(order.getOrderStatus());

return savedOrder;
return OrderResponse.from(savedOrder);
}
}