본문 바로가기
혼자 공부하는 것들

[디자인패턴] Domain Model Pattern vs Transaction Script Pattern

by applepick 2022. 3. 13.
반응형

Domain Model Pattern [도메인 모델 패턴]이란?

대부분의 비즈니스 로직이 엔티티 안에 구성되어있습니다. 서비스 계층은 엔티티에 필요한 역할을 위임하는 역할을 합니다. 엔티티 안에 비즈니스 로직을 가지고 객체지향을 활용하는 기법입니다. (DDD를 접목시킬 경우 이 방법을 사용합니다.)

간단 예시

Transaction Script Pattern [트랜잭션 스크립트 패턴]이란?

엔티티에 비지니스로직이 거의 없고, 서비스 계층에서 비즈니스 로직을 처리하는 방법을 가리킵니다. 엔티티는 단순하게 데이터를 전달하는 역할이 되면서 서비스 로직이 커지게 됩니다. 

간단예시


간단하게 주문 엔티티가 있다고 가정해봅니다. 

도메인 모델 패턴으로 구현하면 

@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<>();

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "delivery_id") //1대1 관계에서 order에서 조회가 더많이 일어나니 Order 테이블에서 delivery_id를 여기서 연관관계 주인이된다.
    private Delivery delivery; //배송정보

    private LocalDateTime orderDate; //주문시간 @Enumerated(EnumType.STRING) Date 말고 LocalDateTime 쓰자

    @Enumerated(EnumType.STRING) //Enum순서의 보장 ORDINAL 사용 x
    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 void setDelivery(Delivery delivery) {
        this.delivery = delivery;
        delivery.setOrder(this);
    }

    //==생성 메서드==//
    public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems){
        Order order = new Order();
        order.setMember(member);
        order.setDelivery(delivery);
        for(OrderItem orderItem: orderItems){
            order.addOrderItem(orderItem);
        }
        order.setStatus(OrderStatus.ORDER);
        order.setOrderDate(LocalDateTime.now());
        return order; //로직이 변경될 경우 여기만 고치면 된다.
    }

    //==비즈니스 로직==//
    /**
     * 주문 취소
     */
    public void cancel(){
        //배송완료이면 취소 x
        if(delivery.getStatus() == DeliveryStatus.COMP){
            throw new IllegalStateException("이미 배송완료된 상품은 취소가 불가능합니다.");
        }
        this.setStatus(OrderStatus.CANCEL);
        for(OrderItem orderItem: orderItems){
            orderItem.cencel();
        }
    }

    //==조회 로직==//
    /**
     * 전체 주문 가격 조회
     */
    public int getTotalPrice(){
        return orderItems.stream()
                .mapToInt(OrderItem::getTotalPrice)
                .sum();
    }
}

엔티티 안에 비지니스 로직을 처리는 것을 볼 수 있습니다.

 

트랜잭션 스크립트 모델로 구현한다면 엔티티는 DTO의 역할 만하고 위에 해당 비즈니스 로직서비스 계층으로 옮겨지게 됩니다.  

 

결론

두 패턴 다 장단점이 뚜렷하기에 어떤 방법이 좋다는 것은 없는 것 같습니다. 각각의 상황에 맞게 적절히 선택하는 것이 중요합니다. 

이에 자세한 내용은 클린코드의 6장 객체와 자료구조를 읽어보면 좋을 것 같습니다. 간단하게 책의 내용을 정리하자면, 시스템을 구현할 때 새로운 자료 타입을 추가하는 유연성이 필요하다면 객체를, 다른 경우로 새로운 동작을 추가하는 유연성이 필요하다면 자료 전달 객체(ex. DTO)와 절차적인 코드가 더 적합 할 수도 있습니다. 해당 문제에 최적인 해결책을 선택하는 것이 중요합니다.

 

해당 주제로 좋은 글이 있어서 첨부합니다.

https://docs.microsoft.com/ko-kr/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/microservice-domain-model#the-domain-entity-pattern

 

마이크로 서비스 도메인 모델 디자인

컨테이너화된 .NET 애플리케이션용 .NET 마이크로 서비스 아키텍처 | DDD 지향 도메인 모델을 디자인할 때 주요 개념을 이해합니다.

docs.microsoft.com

 

반응형

댓글