Pink Spider/Feign Timecheck

Created Thu, 17 Apr 2025 18:54:11 +0900 Modified Mon, 08 Dec 2025 08:41:47 +0900
1345 Words 6 min

DDD(Domain-Driven Design, 도메인 주도 설계)는 복잡한 소프트웨어 시스템을 설계할 때 비즈니스 도메인(업무 영역) 을 중심에 두고 개발하는 방법론이에요. 에릭 에반스(Eric Evans)의 책 *“Domain-Driven Design: Tackling Complexity in the Heart of Software”*에서 처음 제안되었고, 특히 도메인 복잡성이 높은 시스템에서 강력한 효과를 발휘합니다.


✅ DDD란?

간단히 말해 DDD는:

  • 비즈니스의 핵심 로직(도메인) 을 가장 중요한 것으로 보고,
  • 도메인 전문가와 개발자가 협력하여,
  • 소프트웨어 모델을 업무 용어와 일치시키고,
  • 유지보수 가능하고 유연한 설계를 만드는 것을 목표로 해요.

✅ DDD의 핵심 개념

개념 설명
Entity 식별자가 있어 고유하게 구분되는 객체 (ex. Order, User)
Value Object 식별자 없이 값 자체로 의미가 있는 객체 (ex. Address, Money)
Aggregate 도메인 객체들의 군집. 하나의 루트 엔티티(Aggregate Root)가 존재
Repository 도메인 객체를 저장하고 조회하는 인터페이스 (findById, save)
Service (도메인 서비스) 특정 엔티티에 속하지 않는 비즈니스 로직 담당
Factory 복잡한 객체 생성 로직 담당
Bounded Context 특정 도메인 모델이 적용되는 명확한 경계

✅ Spring Boot에서 DDD 코드상 특징

Spring Boot에서 DDD를 적용하면 다음과 같은 패키지 구조 및 역할 분리가 나타납니다:

📁 예시 디렉토리 구조

com.example.order
 ┣ domain
 ┃ ┣ model
 ┃ ┃ ┣ Order.java (Entity)
 ┃ ┃ ┣ OrderLine.java (Value Object)
 ┃ ┣ repository
 ┃ ┃ ┗ OrderRepository.java
 ┃ ┣ service
 ┃ ┃ ┗ OrderService.java (도메인 서비스)
 ┣ application
 ┃ ┗ OrderApplicationService.java (유스케이스 담당)
 ┣ infrastructure
 ┃ ┗ JpaOrderRepository.java (Repository 구현)
 ┣ api
 ┃ ┗ OrderController.java (Web layer)

📌 코드 예시

Entity 예시

@Entity
public class Order {
    @Id
    private Long id;

    private String customerName;

    @ElementCollection
    private List<OrderLine> orderLines;

    // 도메인 로직
    public void addItem(OrderLine item) {
        orderLines.add(item);
    }
}

Value Object 예시

@Embeddable
public class OrderLine {
    private String productName;
    private int quantity;
    private int price;

    public int totalPrice() {
        return quantity * price;
    }
}

Repository

public interface OrderRepository {
    Optional<Order> findById(Long id);
    void save(Order order);
}

Domain Service

public class OrderService {
    public void cancelOrder(Order order) {
        if (order.isCancelable()) {
            order.cancel();
        }
    }
}

Application Service

@Service
public class OrderApplicationService {

    private final OrderRepository orderRepository;

    public OrderApplicationService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public void placeOrder(OrderDto dto) {
        Order order = new Order(...); // 팩토리 패턴도 가능
        orderRepository.save(order);
    }
}

✅ 요약

  • DDD는 비즈니스 도메인 중심의 설계 방식
  • Spring Boot에서는 레이어를 분리하고, 도메인 모델에 집중
  • Entity / Value Object / Repository / Service 등 개념 명확히 분리
  • 복잡한 비즈니스 로직을 다룰 때 유지보수성과 확장성이 뛰어남

좋은 OrderRepository.javaJpaOrderRepository.java 두 개의 파일이 있지만, 실제로는 Repository는 하나인 게 맞아요. 그 이유를 간단하게 말하면:

OrderRepository인터페이스(역할),
JpaOrderRepository는 그 인터페이스의 구현체(구현) 입니다.


✅ 왜 이렇게 나누는가?

1. 도메인 계층과 기술 계층의 분리 (DDD 철학)

  • OrderRepository도메인 레이어에 위치:

    “이 주문을 저장하고 찾을 수 있다"는 비즈니스 관점의 역할만 선언함.

  • JpaOrderRepository인프라 레이어에 위치:

    실제로 JPA를 써서 “어떻게” 저장할지 구현함.

➕ 이 구조의 장점

장점 설명
느슨한 결합 도메인 로직은 JPA 같은 기술에 의존하지 않음
유연성 나중에 JPA → MongoDB로 바꿔도 도메인 코드는 그대로
테스트 용이 도메인 테스트 시, 구현체(JPA 등) 없이 가짜(Mock)로 대체 가능

✅ 예시로 다시 정리

OrderRepository.java (도메인에 있음)

public interface OrderRepository {
    Optional<Order> findById(Long id);
    void save(Order order);
}

JpaOrderRepository.java (인프라에 있음)

@Repository
public class JpaOrderRepository implements OrderRepository {

    private final SpringDataJpaOrderRepository jpaRepo;

    public JpaOrderRepository(SpringDataJpaOrderRepository jpaRepo) {
        this.jpaRepo = jpaRepo;
    }

    @Override
    public Optional<Order> findById(Long id) {
        return jpaRepo.findById(id);
    }

    @Override
    public void save(Order order) {
        jpaRepo.save(order);
    }
}

SpringDataJpaOrderRepository.java (Spring Data JPA 인터페이스)

public interface SpringDataJpaOrderRepository extends JpaRepository<Order, Long> {
}

✅ 요약

  • OrderRepository: “역할” 정의 (비즈니스 중심, 도메인 영역)
  • JpaOrderRepository: “구현” 정의 (기술 중심, 인프라 영역)
  • 이렇게 분리함으로써 기술에 종속되지 않는 유연한 아키텍처를 구성할 수 있음