본문 바로가기
카테고리 없음

헥사고날 아키텍쳐

by 순원이 2024. 10. 3.

1. 질문사항 유저랑 댓글작성자를 구분해서 엔티티 만드는 것이 나을까?

2. ENUM과 값 객체 차이

  ENUM VO
ID x x
목적   고정된 상수 집합을 나타냅니다 속성과 동작으로 개념 캡슐화
사례 카테고리 돈, 주소 

3. VO와 엔티티 차이

4. 서비스마다 데이터베이스를 따로 만들어야 하나?

 

------

개념정리

애그릿게잇 루트

하위의 모든 객체를 처리하는 책임 

https://velog.io/@heoseungyeon/MSA-%EC%99%80-DDD-2-DDD%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-MSA

 

포트와 어댑터란?

 

  • Order: 도메인 모델을 나타내는 클래스입니다.
  • OrderService (입력 포트):
    • 주문 처리 로직을 정의하는 인터페이스입니다.
    • 애플리케이션의 비즈니스 로직 진입점 역할을 합니다.
  • OrderEventPublisher (출력 포트):
    • 주문 처리 결과를 외부 시스템에 알리는 인터페이스입니다.
    • 도메인 이벤트를 발행하는 추상화된 방법을 제공합니다.
  • OrderServiceImpl:
    • OrderService 인터페이스의 구현체입니다.
    • 실제 주문 처리 로직과 이벤트 발행을 담당합니다.
  • RabbitMQOrderEventPublisher (출력 어댑터):
    • OrderEventPublisher 인터페이스의 구현체입니다.
    • RabbitMQ에 실제로 메시지를 발행하는 로직을 포함합니다.
  • RabbitMQOrderConsumer (입력 어댑터):
    • RabbitMQ에서 메시지를 받아 처리하는 클래스입니다.
    • 받은 메시지를 도메인 객체로 변환하고 OrderService를 호출합니다.
// 도메인 모델
public class Order {
    private String id;
    private String customerName;
    private double amount;

    public Order(String id, String customerName, double amount) {
        this.id = id;
        this.customerName = customerName;
        this.amount = amount;
    }

    // Getters and setters omitted for brevity

    @Override
    public String toString() {
        return "Order{" +
                "id='" + id + '\'' +
                ", customerName='" + customerName + '\'' +
                ", amount=" + amount +
                '}';
    }
}

// 입력 포트 (애플리케이션 서비스 인터페이스)
public interface OrderService {
    void processOrder(Order order);
}

// 출력 포트 (메시지 발행 인터페이스)
public interface OrderEventPublisher {
    void publishOrderProcessed(Order order);
}

// 애플리케이션 서비스 구현
public class OrderServiceImpl implements OrderService {
    private final OrderEventPublisher orderEventPublisher;

    public OrderServiceImpl(OrderEventPublisher orderEventPublisher) {
        this.orderEventPublisher = orderEventPublisher;
    }

    @Override
    public void processOrder(Order order) {
        // 주문 처리 로직
        System.out.println("Processing order: " + order);
        // 이벤트 발행
        orderEventPublisher.publishOrderProcessed(order);
    }
}

// 메시지 큐 어댑터 (출력 어댑터)
public class RabbitMQOrderEventPublisher implements OrderEventPublisher {
    @Override
    public void publishOrderProcessed(Order order) {
        // RabbitMQ에 메시지 발행 로직
        System.out.println("Publishing order processed event to RabbitMQ: " + order);
    }
}

// 메시지 큐 어댑터 (입력 어댑터)
public class RabbitMQOrderConsumer {
    private final OrderService orderService;

    public RabbitMQOrderConsumer(OrderService orderService) {
        this.orderService = orderService;
    }

    public void consumeOrderMessage(String message) {
        // 메시지를 Order 객체로 변환
        Order order = parseOrder(message);
        // 애플리케이션 서비스 호출
        orderService.processOrder(order);
    }

    private Order parseOrder(String message) {
        // 메시지를 파싱하여 Order 객체 생성 로직
        // 여기서는 간단히 더미 데이터를 반환
        return new Order("1", "John Doe", 100.0);
    }
}

// 메인 애플리케이션
public class Main {
    public static void main(String[] args) {
        OrderEventPublisher orderEventPublisher = new RabbitMQOrderEventPublisher();
        OrderService orderService = new OrderServiceImpl(orderEventPublisher);
        RabbitMQOrderConsumer orderConsumer = new RabbitMQOrderConsumer(orderService);

        // 메시지 큐에서 메시지를 받았다고 가정
        orderConsumer.consumeOrderMessage("Some message from RabbitMQ");
    }
}

 

 

 

주의점)

1. 도메인을 Loose Coupling과 High Cohesion 관점에서 잘 나누는 것이 DDD와 MSA에서 가장 중요하다 할 수 있습니다. 

 

2.

<TIP>
다수의 애그리거트는 한개의 엔티티 객체만 갖는 경우가 많다. 두 개 이상의 엔티티로 구성되는 애그리거트는 드물게 존재한다.
- 최범균, SK C&C 도메인 설계

출처: https://devlos.tistory.com/51 [Devlos Archive:티스토리]

 

 

 현재 마이크로서비스 사이에 트랜잭션을 처리하는 표준이 없다. 따라서 트랜잭션 관리가 필요하다면 직접 만들어야 한다. 이때 메시징을 이용하는데 메시징에선 데이터를 업데이트할 때 지연 시간이 발생ㅎ나다. 따라서 업데이트한 데이터가 즉시 나타나지 않을 수 있어 일관성 유지에도 노력을 기울여야 한다.

MSA는 처음부터 올바르게 설계하기 어렵기에 잘게 나뉜 서비스보다 굵게 나뉜 서비스에서 시작하는 것이 더 좋다. 너무 잘계 나뉘면 분리된 두 서비스 사이에 지나친 호출이 발생하기 때문에 데이터를 합치는 집합 (aggregation) 서비스를 별도로 만들거나 서비스 영역의 명확한 경계선이 없는 서비스에서 물리적 제약이 발생할 수 있다.

 

 

인프라

ECS + Fargate

Spring multimodule

메시지큐

메시지 큐는 헥사고날 아키텍처에서 중요한 역할을 합니다. 주로 애플리케이션의 외부 통신과 내부 도메인 로직 사이의 인터페이스 역할을 하며, 다음과 같은 방식으로 활용됩니다:

  1. 포트와 어댑터 패턴 구현:
    • 메시지 큐는 '포트'로 간주될 수 있으며, 큐와 상호작용하는 코드는 '어댑터'로 구현됩니다.
    • 입력 포트: 외부 시스템에서 들어오는 메시지를 받아 도메인 로직으로 전달합니다.
    • 출력 포트: 도메인 로직의 결과를 외부 시스템으로 전달합니다.
  2. 비동기 통신:
    • 도메인 로직과 외부 시스템 간의 느슨한 결합을 제공합니다.
    • 장기 실행 프로세스나 배치 작업을 비동기적으로 처리할 수 있게 합니다.
  3. 이벤트 기반 아키텍처 지원:
    • 도메인 이벤트를 발행하고 구독하는 메커니즘으로 활용됩니다.
    • 서로 다른 바운디드 컨텍스트 간의 통신을 가능하게 합니다.
  4. 시스템 확장성 향상:
    • 부하 분산과 피크 시간 처리를 위한 버퍼 역할을 합니다.
    • 마이크로서비스 간 통신을 원활하게 합니다.
  5. 인프라스트럭처 레이어와의 분리:
    • 메시지 큐 관련 로직을 인프라스트럭처 레이어에 둠으로써 도메인 로직을 순수하게 유지할 수 있습니다.