본문 바로가기
Spring 7기 프로젝트/플러스 팀 프로젝트

도메인 간 의존성 낮추기 : API 호출 및 스프링 이벤트 구독을 위한 Common 패키지 설계

by JuNo_12 2025. 7. 10.

문제 상황: 도메인 간 강한 결합

프로젝트가 커지면서 도메인 간 협력이 복잡해졌습니다.

1. API 호출 중복

// EventService.java - 상품 정보 조회
private List<SearchProductResponse> productSearch(List<Long> productIds) {
    // 13줄의 RestTemplate 코드...
}

// OrderService.java - 또 상품 정보 조회  
private List<SearchProductResponse> productSearch(List<Long> productIds) {
    // 거의 동일한 13줄 코드 반복...
}

// SubscribeService.java - 또또 상품 정보 조회
private SearchProductResponse getProductInfo(Long productId) {
    // 또 다른 형태의 RestTemplate 코드...
}

2. 도메인 패키지 직접 탐색

다른 도메인의 기능을 사용하려면 해당 패키지를 뒤져야 했습니다.

3. 이벤트 발행/구독 코드 분산

// EventService.java - 이벤트 발행
eventPublisher.publishEvent(new WSEventProduct(
    event.getEventId(),
    productId,
    event.getEventType(),
    productName,
    originalPrice,
    discountPrice,
    discount
));

// NotificationService.java - 이벤트 구독
@EventListener
public void handleProductEvent(WSEventProduct event) {
    // 구독 로직...
}

 

문제는 다른 도메인에서 이벤트를 사용하려면:

  1. WSEventProduct 클래스가 어디에 있는지 찾아야 함
  2. 이벤트 발행 방법을 EventService 코드를 뒤져서 파악해야 함
  3. 도메인 패키지 간 직접적인 의존성 발생

해결책: Common 패키지로 공통화

패키지 구조

 

구현: API 호출 공통화

예시 : ProductApiClient - 모든 Product API 호출 담당

 

예시 : 이벤트 발행/구독 공통화

1. 공통 이벤트 DTO

 

2. 공통 이벤트 발행 클라이언트


개선 효과

1. 코드 중복 제거

  • Before: 각 도메인마다 13줄의 RestTemplate 코드
  • After: 1줄의 메서드 호출

2. 의존성 방향 개선

❌ Before: 
event → product.dto (직접 의존)
order → product.controller (URL 탐색)
notification → event.dto (이벤트 클래스 탐색)

✅ After:
event → common (공통 모듈 의존)
order → common (공통 모듈 의존)  
notification → common (공통 모듈 의존)

3. 도메인 지식 캡슐화

// Before: URL, HTTP 메서드, DTO 구조 모두 알아야 함
URI uri = UriComponentsBuilder
        .fromUriString("http://localhost:8080")
        .path("/api/products/search-product")  // 어떻게 알지?
        .encode()
        .build()
        .toUri();


// After: 메서드 이름만 알면 됨
List<ProductDto> products = productApiClient.getProducts(productIds);

핵심 설계 원칙

1. 단일 책임 원칙

  • ProductApiClient: API 호출만 담당
  • ProductEventPublisher: 이벤트 발행만 담당
  • 각 도메인 Service: 비즈니스 로직만 담당

2. 의존성 역전 원칙

// ✅ 상위 모듈(도메인)이 하위 모듈(common)에 의존
@Service
public class EventService {
    private final ProductApiClient productApiClient;  // 인터페이스에 의존
}

3. 캡슐화

  • URL, HTTP 메서드, 요청 형식 등 구현 상세사항을 Common에서 숨김
  • 도메인은 비즈니스 로직에만 집중 가능

4. 재사용성

  • 한 번 구현한 클라이언트를 모든 도메인에서 재사용
  • 일관된 API로 학습 비용 최소화

결론

Common 패키지 도입으로 얻은 것들:

  1. 개발 생산성 향상
    • 다른 도메인 패키지 뒤질 필요 없음
    • 일관된 API로 학습 비용 감소
  2. 유지보수성 개선
    • 중복 코드 제거
    • 한 곳에서 관리하는 API 호출 로직
  3.  확장성 확보
    • 새로운 도메인 추가 시 Common 활용
    • 일관된 아키텍처 패턴
  4. 도메인 순수성
    • 각 도메인이 비즈니스 로직에만 집중
    • 기술적 관심사와 비즈니스 관심사 분리

 

"도메인 간 협력은 Common을 통해, 각 도메인은 자신의 책임에만 집중하자!"