[JAVA] 도메인 모델 패턴 VS. 트랜잭션 스크립트 패턴
도메인 모델 패턴
대부분의 비즈니스 로직이 엔티티 안에 구성되고, 서비스 계층은 엔티티에게 필요한 역할을 위임하는 패턴입니다. 객체 지향의 특성을 적극 활용할 수 있습니다.
예를 들어 주문을 생성하고 취소하고, 총 가격을 조회하는 로직을 만들어보겠습니다.
Order
Order 는 간단하게 id
, member
, orderItems
, status
필드를 가집니다.
@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {
@Id
@GeneratedValue @Column(name = "order_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
@Enumerated(EnumType.STRING)
private OrderStatus status; //주문상태 [ORDER, CANCEL]
//==연관관계 메서드==//
public void setMember(Member member) {
this.member = member;
member.getOrders().add(this);
}
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this);
}
//==생성 메서드==//
public static Order createOrder(Member member, OrderItem... orderItems){
Order order = new Order();
order.setMember(member);
for(OrderItem orderItem: orderItems){
order.addOrderItem(orderItem);
}
order.setStatus(OrderStatus.ORDER);
return order; //로직이 변경될 경우 여기만 고치면 됩니다.
}
//==비즈니스 로직==//
/**
* 주문 취소
*/
public void cancel(){
this.setStatus(OrderStatus.CANCEL);
for(OrderItem orderItem: orderItems){
orderItem.cancel();
}
}
//==조회 로직==//
/**
* 전체 주문 가격 조회
*/
public int getTotalPrice(){
return orderItems.stream()
.mapToInt(OrderItem::getTotalPrice)
.sum();
}
}
내부에서 createOrder()
, cancel()
, getTotalPrice()
의 메서드로 기능을 나타내고 있습니다.
OrderService
OrderService
는 도메인의 기능을 호출하는 역할을 합니다.
@Transactional
@RequiredArgsContructor
@Service
public class OrderService {
private final OrderRepository orderRepository;
public Order createOrder(ServiceDto dto){
Order order = Order.createOrder(getMember(), OrderItem.createOrderItem(dto.getItemId));
return orderRepository.save(order);
}
public void cancelOrder(Long id){
Order order = orderRepository.findById(id).getOrThrow(()-> new RuntimeException());
order.cancel();
}
public int getTotalPrice(Long id){
Order order = orderRepository.findById(id).getOrThrow(()-> new RuntimeException());
return order.getTotalPrice();
}
}
내부 메서드를 보면 모두 Order
의 기능을 단순히 사용합니다. 물론 복잡한 로직이 추가될 수 있겠지만요.
장단점
도메인 모델의 장점은 객체 지향에 기반한 사용성, 확장성, 그리고 유지 보수의 편리함에 있습니다. 일단 도메인 모델을 구축하고 나면 언제든지 재사용할 수 있습니다. 또한 상속, 인터페이스 등으로 무한한 확장성도 갖습니다.
단점으로 초기 설계와 구현이 복잡할 수 있습니다. 도메인 모델 패턴은 보통 복잡한 비즈니스 로직과 관련된 애플리케이션에 사용되므로 객체 모델 설계에 시간과 노력이 필요합니다. 또한 단순한 비즈니스 로직에 도메인 모델 패턴을 사용하면 오버헤드가 발생할 수 있습니다.
“오버헤드”는 컴퓨터 공학 용어로, 어떤 처리를 위해 들어가는 비용 중에서 실제로 필요한 작업에 직접적으로 기여하지 않는 비용을 말합니다.
예를 들어, 프로그램 실행에 필요한 메모리 할당, 네트워크 통신, 디스크 I/O, 시스템 호출, 함수 호출로 인한 시간 소모, 스레드 생성 비용 등이 오버헤드에 포함됩니다.
트랜잭션 스크립트 패턴
트랜잭션 스크립트(Transaction Script) 패턴은 하나의 트랜잭션으로 구성된 로직을 단일 함수 또는 단일 스크립트에서 처리하는 구조를 갖습니다. 엔티티는 단순히 데이터를 전달하는 역할을 하게 되고, 서비스 계층에 모든 비즈니스 로직이 들어가게 됩니다.
장단점
장점으로, 트랜잭션 스크립트 패턴은 간단하고 직관적입니다. 각 트랜잭션에 대해 하나의 스크립트를 만드는 것이 전부입니다. 또한 비즈니스 로직이 간단하거나 애플리케이션이 작을 때는 트랜잭션 스크립트 패턴을 사용하면 개발 속도를 높일 수 있습니다.
단점은 재사용성과 확장성이 떨어지고, 유지보수가 어렵다는 접입니다. 비즈니스 로직이 복잡해질수록 코드의 중복이 증가하고 관리가 어려워집니다.
마무리
도메인 모델 패턴과 트랜잭션 스크립트 패턴은 서로 다른 상황에서 잘 작동하는 패턴이므로, 각 패턴의 장단점과 해당 애플리케이션의 요구 사항을 고려하여 패턴을 선택하는 것이 중요할 듯 합니다.
Ref.
[디자인패턴] Domain Model Pattern vs Transaction Script Pattern
도메인 모델 패턴, 트랜잭션 스크립트 패턴 (Domain Model Pattern, Transaction Script Pattern)
댓글남기기