์ฃผ๋ฌธ ์์ฑ ํธ๋์ญ์ ์ ๊น๋ํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ด ์ฝ๋๋ก ๋ง๋ค๊ธฐ ์ํด, ๊ธฐ๋ฅ๊ณผ ์ฑ ์์ ์ชผ๊ฐ๊ณ ํฉ์น๋ ์ฌ์ ์ ๊ธฐ๋ก์ ๋๋ค. ์ด ๊ณผ์ ์์ ์์ฉ ์๋น์ค(Facade)์ ๋๋ฉ์ธ ์๋น์ค์ ๊ฒฝ๊ณ๋ฅผ ๋ช ํํ ํ์ต๋๋ค.
๊ตฌํํด์ผํ๋ ๊ธฐ๋ฅ์ ๋ฌธ์ฅ์ผ๋ก,
"์ฃผ๋ฌธ์๊ฐ / ์ฌ๊ณ ๊ฐ ์๋ ์ํ์ / ์ถฉ๋ถํ ํฌ์ธํธ๋ก ๊ฒฐ์ฌํ์ฌ / ์ฃผ๋ฌธ์ ์์ฑํ๋ค."
๋ฌธ์ฅ์์ ํ์ํ ๋๋ฉ์ธ(๋์)์?
1) ์ฌ์ฉ์
2) ์ํ(์ฃผ๋ฌธ์ ํ์ํ ์์)
3) ํฌ์ธํธ(์ฃผ๋ฌธ์ ํ์ํ ์์)
4) ์ฃผ๋ฌธ
์ด๋ค ์์๋ก ์งํ๋์ด์ผ ํ๋๊ฐ?
1) ์ฃผ๋ฌธ์ ํ์ํ ๋๋ฉ์ธ์ด ์ง์ง ์๋๊ฐ?
2) ์ฃผ๋ฌธ์ ํ์ํ ์์์ ํ๋ณดํ์๋๊ฐ?
3) ์์์ ์ฌ์ฉํ๋ค.
4) ์ฃผ๋ฌธ์ ์์ฑํ๋ค.
| ์์ | ๋ช ์นญ | ํต์ฌ ํ์ | ๊ด๋ จ ๋๋ฉ์ธ ์ํฐํฐ |
|---|---|---|---|
| 1 | ํ์ธ (Active) | ์ฌ์ฉ์, ์ํ, ํฌ์ธํธ๊ฐ ์กด์ฌํ๋๊ฐ? | User, Product, Point |
| 2 | ๊ฒ์ฆ (Validate) | ์ฌ๊ณ /์์ก ์กฐ๊ฑด์ด ์ถฉ๋ถํ๊ฐ? | Product, Point |
| 3 | ์คํ (Execute) | ์ฌ๊ณ ์ฐจ๊ฐ, ํฌ์ธํธ ์ฐจ๊ฐ (์ํ ๋ณ๊ฒฝ) | Product, Point |
| 4 | ์์ฑ (Create) | ์ต์ข ์ฃผ๋ฌธ ๊ฐ์ฒด ์์ฑ ๋ฐ ์ ์ฅ | Order |
์ฃผ๋ฌธ ์์ฑ์ด ๊ฐ๋ฅํ ํ์ฑ ์ํ์ธ์ง ํ์ธํฉ๋๋ค.
๊ฐ์ฅ ๋ณต์กํ ์ฌ๊ณ /ํฌ์ธํธ์ ๊ฒ์ฆ ๋ฐ ์ฐจ๊ฐ์ ๋ค์ ๋ฐ์ดํฐ๋ฅผ ํ์๋ก ํฉ๋๋ค.
| ์์ | ํ์ฌ ์ํ (๋๋ฉ์ธ) | ํ์ ์๋/๊ธ์ก (์์ฒญ) |
|---|---|---|
| ์ฌ๊ณ | List<Product> (ํ์ฌ ์ฌ๊ณ ) | Map<Long, Long> (์ํ ID โ ์๋) |
| ํฌ์ธํธ | Point ๋๋ User | SUM(์ํ ๊ฐ๊ฒฉ x ์๋) |
์ ๋จ๊ณ์ ์ด๋ฆ๊ณผ ํน์ง์ ์ ์ด๋ณด์
1) ํ์ธ
userService.getActiveUser(Long `์ฌ์ฉ์ID`)
productService.getExistProducts(List <Long\> `์ํIDs`)
pointService.getAvailablePoints(Long `์ฌ์ฉ์ID`)
2) ์ฌ๊ณ ์ฐจ๊ฐ
verifyProductStock(List <Product\> `ํ์ฌ์ฌ๊ณ `, List<Long ์ํID ,Long ์๋\> `ํ์ํ ์ฌ๊ณ `)
deductProductStock(List<Product\> `ํ์ฌ์ฌ๊ณ `, List<Long ์ํID,Long ์๋\> `ํ์ํ ์ฌ๊ณ `)
3) ํฌ์ธํธ์ฐจ๊ฐ
verifyPointBalance(Point `ํ์ฌ ํฌ์ธํธ`, `? ํ์ํ ํฌ์ธํธ`)
useUserPoint(Point `ํ์ฌ ํฌ์ธํธ`, `? ํ์ํ ํฌ์ธํธ`)
4) ์ฃผ๋ฌธ์ ์์ฑ(ํธ๋์ญ์ )
createOrder(Order)
verify(๊ฒ์ฆ) ํ deduct(์ฐจ๊ฐ) ์ ๋ถ๋ฆฌํ๋ ๋์ , ํ๋์ ๋๋ฉ์ธ ์๋น์ค ๋ด์์ ๊ฒ์ฆ๊ณผ ์ํ ๋ณ๊ฒฝ(์ฐจ๊ฐ)์ ํ๋์ ํ์๋ก ํตํฉํ์ต๋๋ค. ์ ๋ ฅ๊ฐ์ด ๋์ผํ๋ฉฐ, ๋ฆฌ์คํธ ํ์ธ ๋ฐ ๋ฐ์ดํฐ ์์ ์ผ๋ก ์ค๋ณต ์กฐํ๊ฐ ์ผ์ด๋๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด์ ์ ๋๋ค.
public class OrderCreateV1Dto {
public record OrderItemRequest(long productId, long quantity) {
}
public record OrderRequest(List<OrderItemRequest> items) {
}
}
์ฌ๊ณ ๋ฐ์ดํฐ List<Product> ์ ์์ฒญ๋ฐ์ List<OrderItemRequest> ์ฃผ๋ฌธ๋ด์ญ์ ๋น๊ตํ๊ธฐ ์ํด์, List<OrderItemRequest> ๋ฅผ ์ค๋ณต์ ์ ๊ฑฐํ Map<Long,Long> ํ์์ผ๋ก ๋ณ๊ฒฝํ์ฌ productId๋ฅผ ํค๊ฐ์ผ๋ก ์๋๊ฒ์์ด ์ฉ์ดํ๋๋ก ํ์ต๋๋ค.
public record CreateOrderCommand(Long userId, Map<Long, Long> orderItemInfo) {
public record ItemCommand(Long productId, Map<Long, Long> quantity) {
}
}
์ง๊ธ๊น์ง์ ๊ฒฐ์ ์ ๋ฐํ ํ์ ๊น์ง ํฌํจํด์ ์ ์ด๋ณด๋ฉด,
UserService.getActiveUser(Long userId)->User
ProductService.getExistProducts(List<Long>)->List<Product>
PointService.getAvailablePoints(Long userId)->Point
verifyProductStock(List<Product> ํ์ฌ์ฌ๊ณ , Map<Long ์ํID ,Long ์๋> ํ์ํ์ฌ๊ณ )-> `์ฌ๊ณ ๋ฅผ ์ฐจ๊ฐํ List<Product>`
deductProductStock(List<Product> ํ์ฌ์ฌ๊ณ , Map<Long ์ํID,Long ์๋> ํ์ํ ์ฌ๊ณ )
verifyPointBalance(Point ํ์ฌ ํฌ์ธํธ, List<Product> ๊ฐ๊ฒฉ,Map<Long,Long> ์๋) -> `์ฌ์ฉ ํฌ์ธํธ๋ฅผ ์ฐจ๊ฐํ Point`
saveUserPoint(Point ํ์ฌ ํฌ์ธํธ, `ํ์ํ ํฌ์ธํธ`)
createOrder(Order)-> Order
์ด ๊ฐ๊ฒฉ๋ฅผ ๋ฐํํ์.verifyPointBalance์ผ๋ก( Point, List<Product>, Map<Long,Long>)์ ๋ณด๋ด๊ณ , Point ์์ ์ด๊ฐ๊ฒฉ์ ๋บ ๋จ์ Point ๋ฅผ ๋ฆฌํดํ๊ณ ๊ทธ๋๋ก Point ๋ฅผ ์ ์ฅํ์.
Order: ์ ์ ID, ์ด ๊ฐ๊ฒฉ
OrderItem: ์ํID, ์๋, ๋จ๊ฐ
์ด๊ฐ๊ฒฉ์ ๋น๊ตํ๋ verifyPointBalance์์ ์ฐจ๊ฐ๋ ์์ก ํฌ์ธํธ๊ฐ ๋ฆฌํด๋๋ค. ๋ฐ๋ผ์ ์ด๋์๋ ์ด๊ฐ๊ฒฉ์ ์ ์ฅํ๊ณ ์์ง ์์, ๊ฒ์ฆ ์ดํ ์ด ๊ฐ๊ฒฉ์ ๋ค์ ๊ตฌํด์ผ ํ๋ค. verifyPointBalance ์์ ์ฐจ๊ฐ๋ ํฌ์ธํธ๊ฐ ์๋ ์ด๊ฐ๊ฒฉ์ ๋ฐํํ์.
Order Facade์์ ์ ๋ฆฌํ๋ฉด,
OrderFacade {
public OrderInfo createOrder(CreateOrderCommand command) {
User user = UserService.getActiveUser(Long userId);
List<Product> products = ProductService.getExistProducts(List<Long> productIds);
Point point = PointService.getAvailablePoints(Long userId);
List<Product> deductedProducts = OrderPreparer.verifyProductStock(List<Product> products, List<Long ์ํID ,Long ์๋> command.getOrderItemCommand);
deductProductStock(deductedProducts)
BigDecimal requestedPoints = OrderPreparer.verifyPointBalance(Point ํ์ฌ ํฌ์ธํธ, List<Product> ๊ฐ๊ฒฉ,Map<Long,Long> ์๋);
saveUserPoint(Point ํ์ฌ ํฌ์ธํธ - requestedPoints)
Order order = createOrder(User,List<Product>,Point)
return OrderInfo.from(savedOrder);
}
์ด๊ฐ๊ฒฉ ๋์ Map<Long ์ํID ,Long ์๋> ์ฃผ๋ฌธ ์์ธ ๋ฐ์ดํฐ ์์ฑ์ ์ํด ์ํ์ ๋จ๊ฐ,์๋์ ์๋ ค๋ฉด Map<Long ์ํID ,Long ์๋> ๋ฅผ ๋ฃ์ด์ผ ํ๊ณ , List ๋ง๋ค๋ฉด์ ์ด๊ฐ๊ฒฉ์ ๊ณ์ฐํ ์ ์๊ธฐ ๋๋ฌธ์, ์ด๊ฐ๊ฒฉ ๋์ Map<Long ์ํID ,Long ์๋> ์ ๋ฃ๊ธฐ๋ก ํ์ต๋๋ค.
์ฐจ๊ฐํ ํฌ์ธํธ๋ฅผ ์ ์ฅํ๋ ค๋ค๊ฐ, "ํฌ์ธํธ๋ฅผ ์ ์ฅํ๋ค" ๋์ "ํฌ์ธํธ๋ฅผ ์ฐจ๊ฐํ๋ค" ์ ์๋ฏธ๊ฐ ํ๋์ ์๋ฏธ๋ฅผ ์ ๋ด๊ณ ์๋๊ฒ ๊ฐ์์, verifyPointBalance ๋์ , ์ด๊ฐ๊ฒฉ์ ์กฐํํ๋ ํจ์๋ก ๋ฐ๊พธ๊ธฐ๋ก ํ์ต๋๋ค.
@Transactional
public OrderInfo createOrder(CreateOrderCommand command) {
Map<Long, Long> quantityMap = command.orderItemInfo();
User user = userService.getActiveUser(command.userId());
List<Product> productList = productService.getExistingProducts(quantityMap.keySet());
Point point = pointService.getAvailablePoints(user.getId());
List<Product> deductedProducts = OrderPreparer.verifyProductStock(productList, quantityMap);
productService.save(deductedProducts);
BigDecimal totalAmt = OrderPreparer.getTotalAmt(point, productList, quantityMap);
pointService.use(user, totalAmt);
Order savedOrder = createOrderService.save(user, productList, quantityMap);
return OrderInfo.from(savedOrder);
}
๊ฐ์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฐ๊ณ ์๋ ์ฌ๊ณ ๊ฒ์ฆ ๋ฐ ์ฐจ๊ฐ ๊ณผ ํฌ์ธํธ๊ฒ์ฆ ๋ฐ ์ฐจ๊ฐ์ productStockService,userPointService๋ก ํฉ์ณค์ต๋๋ค.
@Transactional
public OrderInfo createOrder(CreateOrderCommand command) {
Map<Long, Long> quantityMap = command.orderItemInfo();
User user = userService.getActiveUser(command.userId());
List<Product> productList = productService.getExistingProducts(quantityMap.keySet());
productStockService.deduct(productList, quantityMap);
userPointService.use(user, productList, quantityMap);
Order savedOrder = createOrderService.save(user, productList, quantityMap);
return OrderInfo.from(savedOrder);
}
์ฒ์ ๋ณต์กํ๊ฒ ๋๊ปด์ก๋ ์ฃผ๋ฌธ ์์ฑ ๋ก์ง์ด, ์ฑ ์(์ฌ๊ณ , ํฌ์ธํธ)์ ๊ธฐ์ค์ผ๋ก ๋๋ฉ์ธ ์๋น์ค(Manager)๋ฅผ ๋ถ์ํ๊ณ , ์ต์ข ์ ์ผ๋ก ๊ฐ๊ฒฐํ๊ณ ์ดํดํ๊ธฐ ์ฌ์ด ํํ์ OrderFacade๋ฅผ ๋ง๋ค์ด๋ด์์ต๋๋ค. ์ถ๊ฐ๋ก,์ด๊ธฐ ๋ชจ๋ธ์ ์กด์ฌ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ๋ถ๋ถ๋ ํ๋ฒ ํฉ์ณ๋ณด๋ฉด ๋ ๋ณด๊ธฐ์ข์ ์ฝ๋๊ฐ ๋ ๊ฒ ๊ฐ์๋ฐ ์ด๋ ๊ฒ ๊ณ์ ํฉ์ณ๋ ๋๋์ง, ์๋ฌธ์ด ๋ค๊ธฐ๋ ํ์ต๋๋ค.