아래에서는 어그리게이트 방식과 각각 도메인 분리 방식의 구조를 그래프와 코드로 설명하겠습니다. 두 가지 접근 방식 모두 각각의 장단점이 있으므로, 시스템의 요구 사항에 따라 적절히 선택할 수 있습니다.
+---------------------------+
| OrderAggregate | <--- 어그리게이트 루트
| +---------------------+ |
| | Order | | <--- 도메인 객체 (Order)
| | ID: 1 | |
| | Status: Created | |
| | Total: 100.00 | |
| | | |
| | +----------------+ | |
| | | OrderItem | | | <--- 하위 도메인 객체 (OrderItem)
| | | ID: 101 | | |
| | | Product: A | | |
| | | Price: 50.00 | | |
| | +----------------+ | |
| | +----------------+ | |
| | | OrderItem | | |
| | | ID: 102 | | |
| | | Product: B | | |
| | | Price: 50.00 | | |
| | +----------------+ | |
| +---------------------+ |
+---------------------------+
// 도메인 객체: OrderItem
public class OrderItem {
private Long id;
private String product;
private BigDecimal price;
public OrderItem(Long id, String product, BigDecimal price) {
this.id = id;
this.product = product;
this.price = price;
}
// Getter 및 비즈니스 로직 메서드
}
// 어그리게이트 루트: Order
public class Order {
private Long id;
private String status;
private BigDecimal totalAmount;
private List<OrderItem> items = new ArrayList<>();
public Order(Long id, String status) {
this.id = id;
this.status = status;
}
public void addItem(OrderItem item) {
items.add(item);
totalAmount = totalAmount.add(item.getPrice());
}
public void changeStatus(String newStatus) {
this.status = newStatus;
}
// Getter 및 비즈니스 로직 메서드
}
@Entity
public class OrderEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String status;
private BigDecimal totalAmount;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "order_id")
private List<OrderItemEntity> items;
// 생성자, Getter 및 Setter
}
@Entity
public class OrderItemEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String product;
private BigDecimal price;
// 생성자, Getter 및 Setter
}
// OrderPersistencePort 인터페이스
public interface OrderPersistencePort {
void saveOrder(Order order);
Order findOrderById(Long id);
}
// OrderAdapter 클래스
public class OrderAdapter implements OrderPersistencePort {
private final OrderRepository orderRepository;
public OrderAdapter(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@Override
public void saveOrder(Order order) {
List<OrderItemEntity> itemEntities = order.getItems().stream()
.map(item -> new OrderItemEntity(item.getId(), item.getProduct(), item.getPrice()))
.collect(Collectors.toList());
OrderEntity orderEntity = new OrderEntity(order.getId(), order.getStatus(), order.getTotalAmount());
orderEntity.setItems(itemEntities);
orderRepository.save(orderEntity);
}
@Override
public Order findOrderById(Long id) {
return orderRepository.findById(id)
.map(entity -> {
Order order = new Order(entity.getId(), entity.getStatus());
entity.getItems().forEach(itemEntity ->
order.addItem(new OrderItem(itemEntity.getId(), itemEntity.getProduct(), itemEntity.getPrice()))
);
return order;
}).orElse(null);
}
}
+---------------------------+
| Order | <--- 도메인 객체
| ID: 1 |
| Status: Created |
| Total: 100.00 |
+---------------------------+
+---------------------------+
| OrderItem | <--- 독립적인 도메인 객체
| ID: 101 |
| Product: A |
| Price: 50.00 |
+---------------------------+
+---------------------------+
| OrderItem | <--- 독립적인 도메인 객체
| ID: 102 |
| Product: B |
| Price: 50.00 |
+---------------------------+