Inbound Port와 Outbound Port의 정의 및 접근 방향
1. Inbound Port
정의:
애플리케이션의 내부(Core Domain 또는 비즈니스 로직)로 들어오는 요청을 처리하기 위한 인터페이스.
외부 계층(예: Controller, API Gateway 등)이 비즈니스 로직을 호출하기 위해 사용하는 진입점.
접근 방향:
외부에서 내부로의 접근이 허용됩니다.
Controller → Inbound Port → Application Logic의 흐름.
내부(Core)는 Inbound Port에 의존하지 않으며, 이 Port를 구현하는 어댑터가 외부 계층에서 이를 호출합니다.
예시:
// Inbound Port 인터페이스
public interface OrderService {
OrderResponse processOrder(OrderRequest request);
}
// 구현체 (Application Layer)
@Service
public class OrderServiceImpl implements OrderService {
@Override
public OrderResponse processOrder(OrderRequest request) {
// 비즈니스 로직 처리
return new OrderResponse(...);
}
}
// Controller에서 Inbound Port 호출
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderRequest request) {
return ResponseEntity.ok(orderService.processOrder(request));
}
}
접근 방향:
외부(
Controller
) → 내부(OrderService
).
2. Outbound Port
정의:
애플리케이션 내부(Core Domain 또는 비즈니스 로직)에서 외부 시스템(예: 데이터베이스, API, 메시지 브로커 등)과 상호작용하기 위한 인터페이스.
외부 의존성(예: 구현체)을 캡슐화하여, 내부(Core)가 구체적인 외부 시스템에 의존하지 않도록 설계.
접근 방향:
내부에서 외부로의 접근이 허용됩니다.
Application Logic → Outbound Port → Adapter → External System의 흐름.
내부(Core)는 Outbound Port에 의존하며, 구현체(Adapter)는 외부 시스템과 상호작용을 처리합니다.
예시:
// Outbound Port 인터페이스
public interface NotificationConfigRepository {
NotificationConfig findById(Long id);
NotificationConfig save(NotificationConfig config);
}
// 구현체 (Adapter)
@Repository
public class JpaNotificationConfigRepository implements NotificationConfigRepository {
private final JpaRepository<NotificationConfig, Long> jpaRepository;
public JpaNotificationConfigRepository(JpaRepository<NotificationConfig, Long> jpaRepository) {
this.jpaRepository = jpaRepository;
}
@Override
public NotificationConfig findById(Long id) {
return jpaRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Config not found"));
}
@Override
public NotificationConfig save(NotificationConfig config) {
return jpaRepository.save(config);
}
}
// 비즈니스 로직에서 Outbound Port 호출
@Service
public class NotificationService {
private final NotificationConfigRepository configRepository;
public NotificationService(NotificationConfigRepository configRepository) {
this.configRepository = configRepository;
}
public void updateNotification(Long id, boolean isActive) {
NotificationConfig config = configRepository.findById(id);
config.setActive(isActive);
configRepository.save(config);
}
}
접근 방향:
내부(
NotificationService
) → 외부(NotificationConfigRepository
).
Inbound Port와 Outbound Port의 접근 규칙
1. 접근 방향의 규칙
Inbound Port는 외부에서 내부로 접근하는 데 사용됩니다.
예:
Controller
→Service
호출.
Outbound Port는 내부에서 외부로 접근하는 데 사용됩니다.
예:
Service
→Repository
호출.
2. 의존성 역전 원칙(DIP)의 적용
Inbound Port와 Outbound Port 모두 구현체(Adapter)에 의존하지 않도록 설계되어야 합니다.
Inbound Port: 외부 계층은 Port 인터페이스에 의존.
Outbound Port: 내부(Core)는 Port 인터페이스에 의존.
Inbound Port와 Outbound Port의 외부 Feature 접근 허용 여부
1. Inbound Port 접근 허용 여부
허용: 외부 계층(Controller 등)이 해당 Feature의 비즈니스 로직을 호출하기 위해 사용.
Feature 간 공유는 비추천:
Inbound Port는 Feature의 비즈니스 로직에 특화된 인터페이스이므로, 다른 Feature에서 직접 호출하지 않는 것이 원칙.
다른 Feature에서 호출하려면, 해당 Feature에 새로운 Inbound Port를 정의하는 것이 바람직합니다.
2. Outbound Port 접근 허용 여부
허용: Outbound Port는 외부 시스템과의 상호작용을 추상화하므로, 다른 Feature에서도 활용 가능.
공유 Port가 필요한 경우:
Outbound Port를 공통 모듈(
common
또는shared
)로 분리하여 여러 Feature에서 사용할 수 있도록 설계합니다.
결론
Inbound Port: 외부에서 내부로의 접근만 허용하며, 다른 Feature에서의 직접적인 접근은 지양.
Outbound Port: 내부에서 외부로의 접근을 위해 사용되며, 여러 Feature에서 재사용하려면 공통 모듈로 분리.
헥사고날 아키텍처의 철학에 따라, 의존성의 방향을 유지하면서 접근 방향을 결정해야 합니다. 필요하면 더 구체적으로 설계를 도와드리겠습니다! 😊