Programming/기타

핵사고날 아키텍처의 설계와 구현 - 유스케이스 작성 방법

armyost 2023. 10. 25. 13:14
728x90

 

 

드라이버 액터는 SuD 행위 중 하나를 트리거하는 사람이나 시스템이고, 드리븐 엑터는 SuD가 소비하는 외부 시스템이다. 

 

우선 액션을 정의한다. 예를들어 

1. 라우터 ID를 찾는다. 

2. 네트워크 주소가 이미 존재하는지 확인한다. 

3. CIDR이 최솟값 아래인지 확인한다. 

4. 이전 검사에서 문제가 없다면 통보된 라우터에 네트워크를 추가한다. 

 

형식을 갖춘 작성 기법과 간단한 작성 기법 외에, 자동화된 테스트로 직접 사용자의 의도를 코드로 표현하는 것이 가능하다. 이 방법은 발견, 형식화, 자동화와 관련된 행위 주도 설계(BDD) 원칙에 의존한다. 이 방법에서는 비즈니스 관련자의 요구를 발견하기 위해 그들과 이야기 하는 것으로 시작한다. 이러한 발견 과정의 결과는 비즈니스 요구를 기술하는 상황과 행동에 대한 예제를 포함한다. 그다음은 이러한 예제를 기반으로 구조화된 문서가 만들어지는 형식화 과정이다. 마지막으로 자동화 과정은 이전 단계에서 기술되고 구조화된 예제에서 나온 행동을 검증하기 위해 테스트가 만들어지고 실행되는 단계다. 스프트웨어 개발 초기 BDD를 사용할 때 비즈니스 아이디어를 검증하기 위해 만든 예제와 테스트를 기반으로 반복적으로 유스케이스를 생성할 기회를 갖게 된다. 

 

@addNetworkToRouter

@And("The network address is valid and doesn't already exist")
public void check_address_validity_and_existence(){
	var availabilitySpec = new NetworkAvailabilitySpecification(
		network.getAddress(), network.getName(), network.getCidr());

	if(!availabilitySpec. isSatisFiedBy(router))
		throw new IllegalArgumentException("Address already exist");
}

@Given("The CIDR is valid")
public void check_cidr(){ ...
}

@Then("Add the network to the router")
public void add_network() { ...
}

여기까지 입력 포트가 없는 유스케이스(인터페이스) 구현으로 볼 수 있다. 

 

입력 포트를 갖는 유스케이스 구현

네트워크를 라우터에 추가하는 오퍼레이션을 설명하는 유스케이스 인터페이스를 정의했다. 해당 유스케이스를 구현애 입력 포트를 만드는 방법을 살펴보자. 아래와 같이 입력포트 구현을 통해 라우터에 네트워크를 추가하는 유스케이스 목표를 만족시키기 위해  소프트웨어가 수행하는 동작에 대한 명확한 뷰를 갖게 된다. 

public class RouterNetworkInputPort implements RouterNetworkUseCase {
	private final Router(NetworkOutputPort routerNetworkOutpuPort;

	public RouterNetworkInputPort(RouterNetworkOutputPort routerNetworkOutputPort){
		this.routerNetworkOutputPort = routerNetworkOutputPort;
	}

	@Override
	public Rotuer addNetworkToRouter(RouterId routerId, Network network) {
		var router =fetchRouter(routerId);
		return createNetwork(router, network);
	}

	private Router fetchRouter(RouterId routerId) {...
	}

	private Router createNetwork(Router router, Network network){...
	}

	private boolean persisNetwork(Router router) {...
	}
}

출력포트를 이용한 외부 데이터 처리

출력포트는 보조포트 로도 알려져 있으며, 외부 데이터를 처리하려는 애플리케이션의 의도를 나타낸다. 여기서는 출력 포트를 통해 시스템이 외부 세계와 통신할 수 있도록 준비한다. 이러한 통신은 허용함으로써 출력포트를 드리븐 액터와 오퍼레이션에 연결할 수 있다. 드리븐 액터는 외부 시스템이지만 드리븐 오퍼레이션은 이런 시스템과 통신하는 데 사용된다는 것을 기억하자. 

앞에서 헥사고날 애플리케이션이 외부 세계와 통신을 준비중이라고 했는데, 애플리케이션 핵사곤 수준에서 통신이 발생하는 방법을 아직 모르기 때문에 그렇다. 이 방법은 애플리케이션의 요구사항을 충족시키기 위해 어떤 기술이 사용될 것인지에 대한 모든 결정을 가능한한 연기하라는 밥 아저씨의 현명한 조언을 기반으로 한다. 이렇게 함으로써 기술적인 세부사항보다 문제 영역에 더 중점을 둘 수 있다.

 

리포지토리만 문제가 아니다.

데이터베이스에서 지속성과 관련된 애플리케이션의 행위를 설명하기 위해 리포지토리나 데이터접근객체(DAO) 같은 용어를 사용하는데 익숙할 수 있다. 핵사고날 어플리케이션에서는 리포지토리를 출력 포트로 대체한다. 어떠한 경우는 RDBMS에서 데이터를 가져올 수 있고 어떠한 경우는 REST API에서 데이터를 가져올 수 있다. 애플리케이션 핵사곤 내의 컴포넌트는 데이터를 얻는 방법과 무관하기 때문에 이러한 세부사항은  애플리케이션 핵사곤 관점에서는 필요하지 않다.

 

이들의 주된 관심사는 액티비티를 수행하는 데 필요한 데이터의 종류를 표현하는 것이다. 그리고 이러한 애플리케이션 핵사곤 컴포넌트가 어떤 데이터를 필요로 하는지 정의하는 방법은 도메인 핵사곤의 엔티티와 값 객체를 기반으로 한다. 이러한 방법으로 출력 포트가 필요로 하는 데이터 타입을 명시하는 경우 같은 출력 포트로 다양한 어댑터를 연결할 수 있다. 즉 출력 포트의 주된 목표는 데이터를 가져오는 방법을 지정하지 않고 어떤 종류의 데이터가 필요한지 지정하는 것이다.