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)와 절차적인 코드가 더 적합 할 수도 있습니다. 해당 문제에 최적인 해결책을 선택하는 것이 중요합니다.
해당 주제로 좋은 글이 있어서 첨부합니다.
'혼자 공부하는 것들' 카테고리의 다른 글
docker: 'compose' is not a docker command. (1) | 2023.11.26 |
---|---|
EC2를 사용하다가 용량이 꽉찼을 때 해결방법 (0) | 2023.05.21 |
node.js & express.js 단단하게 설계하기 (0) | 2021.09.09 |
[Git] rebase와 merge의 차이점 (0) | 2021.08.28 |
Git-flow를 사용해보자! (2) | 2021.05.20 |
댓글