헥사고날 아키텍처에서 의존 역전 원칙(DIP) - f-lab-edu/msa-commerce-lab GitHub Wiki
한 줄 요약: 비즈니스 로직이 데이터베이스나 프레임워크에 직접 의존하지 않고, 인터페이스(추상화)를 통해 의존 방향을 뒤집는 것
OrderService → OrderJpaRepository (JPA에 직접 의존)
비즈니스 로직이 JPA에 종속되어, JPA를 MyBatis로 바꾸면 OrderService까지 수정해야 함
OrderService → OrderRepository (인터페이스) ← OrderJpaRepositoryAdapter (구현체)
비즈니스 로직은 인터페이스만 알고, 구현체는 바꿔 끼울 수 있음
Domain (비즈니스 로직)
↓ 의존
Port (인터페이스) ← 여기서 DIP 발생!
↑ 구현
Adapter (JPA, REST 등)
- Port: 도메인이 정의한 인터페이스
- Adapter: Port를 구현한 구체적인 기술 (JPA, REST API 등)
// 1. Port 정의 (domain/port/out/OrderRepository.java)
public interface OrderRepository {
Order save(Order order);
Optional<Order> findById(UUID id);
}
// 2. 도메인 서비스는 Port에만 의존
public class OrderService {
private final OrderRepository orderRepository; // 인터페이스
public Order createOrder(CreateOrderCommand command) {
Order order = Order.create(command);
return orderRepository.save(order);
}
}
// 3. Adapter가 Port 구현 (infrastructure/adapter/out/persistence/OrderRepositoryImpl.java)
@Repository
public class OrderRepositoryImpl implements OrderRepository {
private final SpringDataOrderRepository jpaRepository;
@Override
public Order save(Order order) {
// JPA Entity로 변환 후 저장
OrderEntity entity = mapper.toEntity(order);
return mapper.toDomain(jpaRepository.save(entity));
}
}- 기술 교체 용이: JPA → MyBatis로 바꿀 때 Adapter만 교체
- 테스트 쉬움: Mock 객체로 쉽게 테스트 가능
- 비즈니스 로직 집중: OrderService는 JPA를 전혀 몰라도 됨
-
현재 구조가 DIP를 제대로 따르고 있는지?
- Port가 도메인 계층에 있는지
- 도메인 모델(Order)을 사용하는지, Entity를 사용하는지
-
어디까지 인터페이스를 만들어야 하는지?
- 모든 Service에 UseCase 인터페이스가 필요한지
- Repository만 인터페이스면 충분한지
-
성능과 설계의 균형
- 추상화 계층이 성능에 영향을 주지 않는지