[DDD Start 실전] 주문-결제 시스템 설계 및 테스트

🚀 DDD Start 실전: 주문-결제 시스템 예제 #

이번 글에서는 DDD Start를 기반으로 실제 주문(Order) - 결제(Payment) 시스템을 설계하고,
테스트 케이스까지 포함한 실전 예제를 다룹니다.
DDD 입문자뿐만 아니라 실무 개발자에게도 바로 적용 가능한 가이드입니다.


1️⃣ 단계별 DDD Start 요약 #

단계설명예제 적용
🟢 도메인 언어 정리개발자 + 도메인 전문가가 같은 용어 사용Order, Customer, Product, Payment
🟡 도메인 분리핵심(Core) / 지원(Supporting) / 범용(Generic)Core: Order, Supporting: Payment, Generic: Logging
🔵 Bounded Context 정의각 컨텍스트별 책임 정의주문 Context, 결제 Context, 배송 Context
🟣 모델링엔티티, 값 객체, 도메인 서비스 설계Order, OrderItem, PaymentService 등
🟠 구현도메인 중심 구조 기반 코드 작성src/domain/… 구조로 구현, 테스트 포함

2️⃣ 도메인 모델링 #

2.1 Order 엔티티 #

 1public class Order {
 2    private final OrderId id;
 3    private final Customer customer;
 4    private final List<OrderItem> items = new ArrayList<>();
 5    private OrderStatus status = OrderStatus.PENDING;
 6
 7    public Order(OrderId id, Customer customer) {
 8        this.id = id;
 9        this.customer = customer;
10    }
11
12    public void addItem(Product product, int quantity) {
13        items.add(new OrderItem(product, quantity));
14    }
15
16    public void confirm() {
17        if (items.isEmpty()) throw new IllegalStateException("주문 아이템이 없습니다.");
18        status = OrderStatus.CONFIRMED;
19    }
20
21    public OrderStatus getStatus() {
22        return status;
23    }
24}

2.2 값 객체(Value Object) #

1public class OrderItem {
2    private final Product product;
3    private final int quantity;
4
5    public OrderItem(Product product, int quantity) {
6        this.product = product;
7        this.quantity = quantity;
8    }
9}

2.3 도메인 서비스 #

1public class PaymentService {
2    public PaymentResult pay(Order order, PaymentMethod method) {
3        if (order.getStatus() != OrderStatus.CONFIRMED) {
4            throw new IllegalStateException("결제 전 주문만 결제 가능합니다.");
5        }
6        return new PaymentResult(order.getId(), true);
7    }
8}

3️⃣ 코드 구조 예시 #

src/
 ├── domain/
 │    ├── order/
 │    │    ├── Order.java
 │    │    ├── OrderItem.java
 │    │    └── OrderStatus.java
 │    ├── payment/
 │    │    ├── PaymentService.java
 │    │    └── PaymentResult.java
 │    └── customer/
 │         └── Customer.java
 ├── application/
 │    └── OrderApplicationService.java
 └── infrastructure/
      ├── repository/
      └── external/

📌 핵심: 도메인 중심 구조 유지, 테스트와 애플리케이션 로직은 분리


4️⃣ 테스트 케이스 예제 #

 1import org.junit.jupiter.api.Test;
 2import static org.junit.jupiter.api.Assertions.*;
 3
 4class OrderTest {
 5
 6    @Test
 7    void 주문_생성_및_확인() {
 8        Customer customer = new Customer("홍길동");
 9        Order order = new Order(new OrderId("1"), customer);
10        order.addItem(new Product("상품A"), 2);
11
12        order.confirm();
13        assertEquals(OrderStatus.CONFIRMED, order.getStatus());
14    }
15
16    @Test
17    void 결제_실패_주문_미확인() {
18        Customer customer = new Customer("홍길동");
19        Order order = new Order(new OrderId("2"), customer);
20        PaymentService paymentService = new PaymentService();
21
22        Exception exception = assertThrows(IllegalStateException.class, () -> {
23            paymentService.pay(order, PaymentMethod.CARD);
24        });
25
26        assertEquals("결제 전 주문만 결제 가능합니다.", exception.getMessage());
27    }
28}

5️⃣ 실무 팁 #

  • 📝 작게 시작: 핵심 도메인부터 Aggregate 정의
  • 📝 Bounded Context 유지: Context 간 책임 명확히
  • 📝 Ubiquitous Language: 팀 공통 용어로 설계
  • 📝 테스트 기반 개발: 도메인 로직부터 검증
  • 📝 점진적 확장: 결제, 배송, 할인 등 기능은 후속 확장

6️⃣ 요약 #

  • DDD Start = 도메인 주도 설계 시작 단계 프로세스
  • 실전 적용: 작은 핵심 도메인 모델링 → Bounded Context → 서비스 구현 → 테스트
  • 목표: 도메인 중심 설계로 확장 가능하고 유지보수 쉬운 코드 구조 확보

💡 결론: DDD Start는 단순 설계 철학이 아니라, 실무에서 바로 적용 가능한 도메인 중심 설계 가이드입니다.

Advertisement