음식 주문은 사용자와 음식이 필요한 도메인이다.
public void order(long userId, FoodOrderRequests foodOrderRequests) {
// DB에서 가져오기
Users user = usersRepository.findById(userId);
List<Foods> foods = foodsRepository.findAll(foodOrderRequests.foodIds());
List<FoodOrders> foodOrders = FoodOrders.from(foodOrderRequests, foods);
// 객체 내부 값 변경하기
// user 잔액 감소 + 음식 재고 감소 + 음식 주문 정보 반환
Orders order = orderService.order(user, foodOrders);
// 변경된 값 DB에 반영 + Order 정보 저장
orderRepository.save(order);
usersRepository.updateBalance(user.getId(), user.getBalance());
for (Foods food : foods)
foodsRepository.updateStock(food.getId(), food.getStock());
}
주문은 구현을 대강 해놨다.
중요한 것은 트랜잭션이다.
order 저장 + 사용자 잔액 반영 + 음식 재고 감소 반영 로직이 한 번에 성공하거나 한 번에 실패해야 한다.지금까지 Repository의 코드는 변경된다고 생각하고, 내부 구현 기술(JDBC)을 외부로 빼지 않도록 노력했다.
근데 이렇게 여러 도메인의 Repository가 합쳐져 있는 곳에서 트랜잭션을 유지하려면 JDBC의 Connection을 밖으로 꺼내야 한다.
일단 트랜잭션을 유지하는 것은 필수니 Connection을 밖으로 빼서 트랜잭션을 유지해본다.
public class FoodOrderService {
private final DBConfig dbConfig; // 추가됨
...
public void order(long userId, FoodOrderRequests foodOrderRequests) {
// DB에서 가져오기
Users user = usersRepository.findById(userId);
List<Foods> foods = foodsRepository.findAll(foodOrderRequests.foodIds());
List<FoodOrders> foodOrders = FoodOrders.from(foodOrderRequests, foods);
try(
Connection conn = DriverManager.getConnection(dbConfig.getUrl(), dbConfig.getUsername(), dbConfig.getPassword())
) {
try {
conn.setAutoCommit(false);
// 객체 내부 값 변경하기
// user 잔액 감소 + 음식 재고 감소 + 음식 주문 정보 반환
Orders order = orderService.order(user, foodOrders);
// 변경된 값 DB에 반영 + Order 정보 저장
orderRepository.save(conn, order);
usersRepository.updateBalance(conn, user.getId(), user.getBalance());
for (Foods food : foods)
foodsRepository.updateStock(conn, food.getId(), food.getStock());
conn.commit();
} catch (SQLException | RuntimeException e) {
conn.rollback();
throw e;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
내부 구현이 밖으로 나와버렸다.
트랜잭션으로 비즈니스적인 요구사항은 만족했지만,
변경될 수 있는 내부 구현 기술이 여러 계층에 퍼지고 있다.
JDBC를 사용하기 때문에 DB를 바꾸는 것은 크게 문제가 없을 것이다.
하지만 JDBC를 JPA로 바꾼다고 가정하면, Connection 관련 외부 코드도 싹다 갈아엎어야 한다.
코드적으론 문제가 많다.