study/소프트웨어 공학

헥사고날 아키텍처(Hexagonal Architecture)

836586697769 2024. 7. 13. 11:23

핵심 개념

  • 핵심 도메인 (Core Domain): 애플리케이션의 비즈니스 로직이 위치하는 부분으로 외부 세계와 독립적으로 설계되며 핵심 기능들이 위치함
  • 포트 (Ports): 애플리케이션이 외부 세계와 상호 작용하는 방법을 정의하는 인터페이스로 외부에서 핵심 도메인에 접근하거나 데이터를 주고받는 방법을 제공함
  • 어댑터 (Adapters): 포트를 통해 외부 시스템과 통신하는 구체적인 구현체로 입력과 출력을 처리함

 

구성 요소

  1. 도메인 모델
    • 비즈니스 엔티티와 규칙을 정의
    • e.g. Order, Customer, Product 등 클래스와 이들 간의 비즈니스 로직
  2. 애플리케이션 서비스
    • 도메인 모델을 사용하여 특정 기능을 수행하는 서비스
    • 비즈니스 로직을 실행하고 포트를 통해 어댑터와 상호 작용
    • e.g. OrderServiceImpl, CustomerService
  3. 어댑터
    • 인터페이스를 구현하여 외부와의 상호작용을 담당하며, 입력 어댑터와 출력 어댑터로 나뉨
    • 입력 어댑터: 사용자의 입력을 받아들이는 부분
      • e.g. OrderController(REST API), OrderCli(CLI 인터페이스)
    • 출력 어댑터: 외부 시스템과 통신하는 부분
      • e.g. OrderRepository(DB 연동), EmailService(외부 이메일 API 연동)

 

예시

도메인 모델

// Order.ts
export class Order {
  constructor(public id: number, public product: string, public quantity: number) {}
}

포트 (인터페이스)

// OrderService.ts
export interface OrderService {
  placeOrder(order: Order): void;
}

애플리케이션 서비스 (포트의 구현)

// OrderServiceImpl.ts
export class OrderServiceImpl implements OrderService {
  private orders: Order[] = [];

  placeOrder(order: Order): void {
    this.orders.push(order);
  }
}

입력 어댑터

// OrderController.ts
export class OrderController {
  constructor(private orderService: OrderService) {}

  createOrder(id: number, product: string, quantity: number): void {
    const order = new Order(id, product, quantity);
    this.orderService.placeOrder(order);
  }
}

출력 어댑터

// InMemoryOrderRepository.ts
export class InMemoryOrderRepository {
  private orders: Order[] = [];

  save(order: Order): void {
    this.orders.push(order);
  }
}

통합 (애플리케이션 설정)

// main.ts
const orderService = new OrderServiceImpl();
const orderController = new OrderController(orderService);

// 주문 생성
orderController.createOrder(1, 'Apple', 10);

 

장점

  • 유지보수성: 도메인 로직이 외부 인터페이스와 분리되어 있어 변경이 용이하고 특정 어댑터를 변경해도 도메인 로직에 영향을 미치지 않음
  • 테스트 용이성: 핵심 비즈니스 로직을 독립적으로 테스트할 수 있고 모의 객체를 사용하여 외부 의존성을 대체할 수 있음
  • 확장성: 새로운 입력 및 출력 어댑터를 쉽게 추가할 수 있음