Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

헥사고날 아키텍처와 마이크로서비스 아키텍처(MSA)를 결합하면 시스템의 복잡성을 효과적으로 관리할 수 있습니다. 이 글에서는 Feature 중심 설계와 Port와 Adapter를 활용한 Feature 간 상호작용 방법을 설명합니다.

...

헥사고날 아키텍처와 MSA의 결합

헥사고날 아키텍처는 애플리케이션을 Core Domain외부 시스템으로 명확히 구분합니다. 모든 외부와의 상호작용은 Port와 Adapter를 통해 이루어지며, Core Domain은 외부 의존성 없이 비즈니스 로직을 중심으로 설계됩니다.

...

  1. Port 중심 상호작용:

    • Feature 간 데이터 요청은 Inbound Outbound Port를 통해 이루어집니다.

    • Outbound Adapter는 다른 Feature의 Inbound Port를 호출하여 데이터를 가져옵니다.

  2. Feature 간 의존성 최소화:

    • 한 Feature는 다른 Feature의 내부 구현을 알지 못하도록 설계해야 합니다.

    • 상호작용은 Interface(Port)를 통해 상호작용을 추상화합니다.

  3. 기술 독립성 보장:

    • REST, gRPC, 메시징 등 다양한 통신 기술은 Adapter에서 처리하고, Core Domain은 이를 알 필요가 없습니다.

...

아래 예제는 Notification Feature와 User Feature 간의 상호작용을 Port와 Adapter를 통해 처리하는 방식을 보여줍니다.

Notification Feature의 Outbound Port

Notification Feature는 User Feature와의 상호작용을 위해 Outbound Port를 정의합니다. 이를 통해 Notification Feature는 User Feature의 내부 구현을 알 필요가 없습니다.

Code Block
public interface UserClientPort {
    UserResponse getUser(String userId);
}

User Feature의 Adapter

User Feature는 Notification Feature의 Outbound Port를 구현하여 데이터를 제공합니다. 이 Adapter는 User Feature 내에 위치하며, Notification Feature에서 호출됩니다.

Code Block
@Component
public class UserClientAdapter implements UserClientPort {
    private final UserServicePort userServicePort; // User Feature의 Inbound Port

    public UserClientAdapter(UserServicePort userServicePort) {
        this.userServicePort = userServicePort;
    }

    @Override
    public UserResponse getUser(String userId) {
        return userServicePort.getUserById(userId);
    }
}

User Feature의 Inbound Port

User Feature는 내부 데이터를 외부에 제공하기 위해 Inbound Port를 정의합니다. 이 Port는 User Feature의 Core Domain과 외부 시스템 간의 추상화된 인터페이스 역할을 합니다.

Code Block
public interface UserServicePort {
    UserResponse getUserById(String userId);
}

...

User Feature의 Inbound Port 구현체

UserServicePort의 구현체는 User Feature의 데이터 접근 로직을 처리하며, JPA Repository를 사용해 데이터를 관리합니다.

Code Block
@Service
public class UserServiceAdapterUserServiceImpl implements UserServicePort {
    private final UserRepository userRepository;

    public UserServiceAdapterUserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserResponse getUserById(String userId) {
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new RuntimeException("User not found"));
        return new UserResponse(user.getId(), user.getName(), user.getEmail());
    }
}

...

User Feature의 Repository

JPA를 활용하여 User 데이터를 관리하는 Repository는 다음과 같이 정의됩니다:

Code Block
public interface UserClientPortUserRepository {extends JpaRepository<User,    UserResponse getUser(String userId);String> {
}

Notification Feature의

...

서비스

Notification Feature는 Outbound Port를 통해 User Feature와 상호작용하며, 비즈니스 로직을 처리합니다.

Code Block
@Component@Service
public class UserClientAdapter implements UserClientPort NotificationService {
    private final UserClientPort userClientPort;

    public NotificationService(UserClientPort userClientPort) {
    private final RestTemplate restTemplate    this.userClientPort = userClientPort;
    }
@Override
    public UserResponseNotificationResponse getUsersendNotification(String userId, String message) {
        UserResponse user return= restTemplateuserClientPort.getForObject("http://user-service/users/getUser(userId);
        // 비즈니스 로직 수행
        return new NotificationResponse("Sent to " + userId, UserResponse.classuser.getEmail());
    }
}

Notification Feature의 서비스

...

클래스 간 관계 도식화

Code Block
[Notification Feature]                                   [User Feature]
+---------------------+                                  +------------------------+
| NotificationService |                                  |   UserServiceImpl      |
|   (Core Logic)      |                      this.userClientPort = userClientPort;     }     | public NotificationResponse sendNotification(String userId, String message) {(Business Logic)     |
+---------------------+                                  +------------------------+
          |                                                         |
          v                                                         |
+---------------------+             Request              +------------------------+
|   UserClientPort    | -------------------------------> |   UserServicePort      |
|  (Outbound Port)    |                                  UserResponse| user = userClientPort.getUser(userId);(Inbound Port)       |
+---------------------+                                  +------------------------+
          ^                                                         ^
          |                                                         |
+---------------------+                 // 비즈니스 로직 수행         return new NotificationResponse("Sent to " + user.getEmail()); +------------------------+
| UserClientAdapter   | <------------------------------- |     UserRepository     |
| (Outbound Adapter)  | Response                         | (JPA Repository)       |
+---------------------+                                 }
}
 +------------------------+

설명

  1. NotificationService:

    • NotificationService는 비즈니스 로직에서 UserClientPort를 통해 User 정보를 요청합니다.

  2. UserClientPort:

    • Notification Feature의 Outbound Port로서, UserClientAdapter의 호출 인터페이스 역할을 합니다.

  3. UserClientAdapter:

    • UserClientPort를 구현하며, UserServicePort를 호출하여 User 정보를 가져옵니다.

  4. UserServicePort:

    • User Feature의 Inbound Port로서 외부 요청을 처리합니다.

  5. UserServiceImpl:

    • UserServicePort의 구현체로, 비즈니스 로직을 처리하며 JPA Repository(UserRepository)와 상호작용합니다.

...

추가 설명: 인터페이스와 Port

  • **인터페이스(interface)**는 추상화를 제공하는 도구이지만, 무조건 Port로 간주할 수는 없습니다. 헥사고날 아키텍처에서 Port는 Core Domain의 경계를 정의하며 외부와의 상호작용을 추상화하는 데 사용됩니다.

  • UserRepository는 JPA의 인터페이스를 확장한 기술적 세부사항을 포함하며, 이는 헥사고날 아키텍처에서 Adapter의 역할에 해당합니다. Core Domain은 UserRepository를 직접 알지 못하며, 대신 추상화된 Outbound Port를 통해 접근합니다.

...

Port와 Adapter를 사용한 설계의 장점

...