@@ -0,0 +1,98 @@
+package christmas.service;
+
+import static christmas.constants.event.EventRule.MAX_MENU_AMOUNT;
+import static christmas.constants.menu.MenuType.DRINKS;
+import static christmas.exception.ErrorCode.INVALID_MENU_ORDER;
+
+import christmas.constants.menu.Menu;
+import christmas.constants.menu.MenuType;
+import christmas.dto.SingleMenu;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+public class MenuService {
+    private final Map<Menu, Integer> menuScript;
+
+    public MenuService() {
+        this.menuScript = new EnumMap<>(Menu.class);
+    }
+
+    public void order(List<SingleMenu> singleMenus) {
+        validate(singleMenus);
+        singleMenus.forEach(singleOrder -> {
+            Menu menu = Menu.from(singleOrder.menu());
+            menuScript.put(menu, singleOrder.amount());
+        });
+    }
+
+    public int getOrderPrice() {
+        return menuScript.keySet()
+                .stream()
+                .mapToInt(menu -> menu.getPrice() * menuScript.get(menu))
+                .sum();
+    }
+
+    private void validate(List<SingleMenu> singleMenus) {
+        validateDuplicate(singleMenus);
+        validatePerMenuAmount(singleMenus);
+        validateOnlyDrink(singleMenus);
+        validateTotalMenuAmount(singleMenus);
+    }
+
+    private void validateDuplicate(List<SingleMenu> singleMenus) {
+        int count = (int) getMenuStream(singleMenus)
+                .distinct()
+                .count();
+
+        if (count != singleMenus.size()) {
+            throw new IllegalArgumentException(INVALID_MENU_ORDER.getMessage());
+        }
+    }
+
+    private void validateOnlyDrink(List<SingleMenu> singleMenus) {
+        int drinks = (int) getMenuStream(singleMenus)
+                .filter(menu -> menu.getMenuType() == DRINKS)
+                .count();
+
+        if (drinks > 0 && drinks == singleMenus.size()) {
+            throw new IllegalArgumentException(INVALID_MENU_ORDER.getMessage());
+        }
+    }
+
+    private Stream<Menu> getMenuStream(List<SingleMenu> singleMenus) {
+        return singleMenus.stream()
+                .map(singleOrder -> Menu.from(singleOrder.menu()));
+    }
+
+    private void validatePerMenuAmount(List<SingleMenu> singleMenus) {
+        boolean present = singleMenus.stream()
+                .anyMatch(singleOrder -> singleOrder.amount() < 1);
+        if (present) {
+            throw new IllegalArgumentException(INVALID_MENU_ORDER.getMessage());
+        }
+    }
+
+    private void validateTotalMenuAmount(List<SingleMenu> singleMenus) {
+        int totalAmount = singleMenus.stream()
+                .mapToInt(SingleMenu::amount)
+                .sum();
+        if (totalAmount >= MAX_MENU_AMOUNT.getValue()) {
+            throw new IllegalArgumentException(INVALID_MENU_ORDER.getMessage());
+        }
+    }
+
+    public int getAmountByMenu(MenuType menuType) {
+        return menuScript.keySet()
+                .stream()
+                .filter(target -> target.getMenuType() == menuType)
+                .mapToInt(menuScript::get)
+                .sum();
+    }
+
+    public Map<Menu, Integer> getMenuScript() {
+        return Collections.unmodifiableMap(menuScript);
+    }
+}

리뷰: `MenuService` 에서 주문 정보들을 담고있는게 인상깊습니다!
이것도 좋은 접근 방식인 것 같지만, `Map<Menu, Integer>`가 `주문 정보` 라는 사실을 추적하기 어려울 수도 있을 것 같아요!
`Order` Domain 클래스를 생성하고, validate 검증 역할도 위임해주면 MenuService 클래스가 한층 얇아질 수 있을 것 같습니다.

여기서 Service Layer의 클래스가 얇아진다는 것은 **Domain 객체에게 요청하여 결과 값을 반환하는 역할**, **비즈니스 로직 흐름을 담당하는 역할**로 축소 된다는 의미라고 생각해요. 이때의 장점은 **MenuService 비즈니스 로직이 어떤 흐름으로 진행되는구나** 추적하기 편해지는 점 같습니다 :)