package jpashop.jpaorder.domain;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "ORDERS")
public class Order {
@Id @GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
@Column(name = "MEMBER_ID")
private Long memberId;
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus;
}
❓ 이런 방식의 설계는 객체지향스럽지 않다 RDB에 맞춘 설계이다 객체지향스럽게 설계를 맞추려면?
📝 객체지향적 설계
1. 문제점
- MEMBER_ID 필드가 외래 키로 사용되지만 연관된 Member 객체를 표현하지 않음: 현재 MEMBER_ID는 단순히 외래 키로 존재하며, Member 객체에 대한 직접적인 참조가 없습니다.
- 주문 상태를 표현하는 OrderStatus는 적절하지만, 주문 항목(OrderItem)을 포함하지 않음: Order는 개별 주문 항목(상품, 수량 등)을 포함해야 객체지향적으로 의미 있는 상태를 가질 수 있습니다.
- 행위 중심의 설계 부족: 현재 엔티티는 단순히 데이터를 보관하는 역할만 수행하며, 비즈니스 로직(예: 주문 생성, 상태 변경)이 없습니다.
2. 객체지향적으로 설계하기
2.1 연관 관계 매핑
외래 키(MEMBER_ID
)를 제거하고, 대신 Member
엔티티와의 연관 관계를 매핑합니다. 주문 항목(OrderItem
)을 Order
에 포함하여 Order
와 OrderItem
간의 관계를 명확히 합니다.
2.2 비즈니스 로직 추가
Order
클래스에 책임(행위)을 추가하여, 객체가 스스로의 상태를 관리하도록 합니다.
객체지향적 설계 코드
Order 엔티티
package jpashop.jpaorder.domain;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "ORDERS")
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, orphanRemoval = true)
private List<OrderItem> orderItems = new ArrayList<>();
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus;
//== 연관 관계 편의 메서드 ==//
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 changeStatus(OrderStatus status) {
this.orderStatus = status;
}
public void calculateTotalPrice() {
int totalPrice = orderItems.stream()
.mapToInt(OrderItem::getTotalPrice)
.sum();
System.out.println("Total Price: " + totalPrice);
}
}
Member 엔티티
package jpashop.jpaorder.domain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "MEMBER")
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
//== 연관 관계 편의 메서드 ==//
public void addOrder(Order order) {
orders.add(order);
order.setMember(this);
}
}
OrderItem 엔티티
package jpashop.jpaorder.domain;
import javax.persistence.*;
@Entity
@Table(name = "ORDER_ITEM")
public class OrderItem {
@Id
@GeneratedValue
@Column(name = "ORDER_ITEM_ID")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ORDER_ID")
private Order order;
private String itemName;
private int price;
private int quantity;
//== 비즈니스 로직 ==//
public int getTotalPrice() {
return price * quantity;
}
}
OrderStatus Enum
package jpashop.jpaorder.domain;
public enum OrderStatus {
NEW, PROCESSING, COMPLETED, CANCELLED
}
객체지향 설계의 장점
- 직관적인 관계 표현: Order와 Member, OrderItem 간의 관계를 객체적으로 표현하여 더 자연스럽습니다.
- 행위 중심 설계: Order와 OrderItem이 각자의 비즈니스 로직을 포함하여 객체 스스로 상태를 관리합니다.
- 유지보수 용이성: 객체 간 관계와 비즈니스 로직이 명확히 분리되므로, 변경이 용이합니다.
- 확장 가능성: Order에 결제 정보, 배송 정보 등을 추가할 때 객체 간 관계를 쉽게 확장할 수 있습니다.
결론
객체지향적으로 설계하려면:
- RDB 설계에 맞춘 외래 키 중심의 설계를 제거하고, 객체 간 연관 관계를 표현합니다.
- 객체가 상태와 행위를 가지도록 비즈니스 로직을 추가합니다.
이렇게 하면 코드가 더 명확해지고, 유지보수성과 확장성이 높은 설계를 만들 수 있습니다. 😊
'JPA' 카테고리의 다른 글
JPA에서 단방향 매핑을 우선으로 하는 이유 (단방향 → 필요시 양방향) (0) | 2024.12.09 |
---|---|
JPA 엔티티 양방향 관계에서 연관관계 주인이 필요한 이유 (1) | 2024.12.09 |
JPA PK 생성전략 @GeneratedValue(strategy = GenerationType.❓) (0) | 2024.12.09 |
JPA EntityManagerFactory, EntityManager는 사용 후 꼭 닫아야 한다 (3) | 2024.12.05 |
JPA 구동방식 (0) | 2024.12.05 |
댓글