헥사고날 아키텍처는 의존성 방향이 도메인 방향으로 향해야 합니다. 그래서 포트 인터페이스를 만들고 어댑터가 포트 인터페이스를 구현하도록 만들었으며, 런타임에 어댑터를 제공해야 합니다.

이러한 어댑터를 만드는 책임은 설정 컴포넌트에게 있습니다. 의존성 규칙을 어기지 않고 어댑터를 만들려면 아키텍처에 대해 중립적이어야 하며, 인스턴스 생성을 위해 모든 클래스에 대한 의존성을 가져야 합니다. 그래서 모든 내부 계층에 접근할 수 있는 원의 가장 바깥쪽에 위치하고, 우리가 만든 포트와 어댑터를 조립하는 것을 책임집니다.

뿐만 아니라 설정 컴포넌트는 설정 파일이나 커맨드라인 파라미터 등과 같은 설정 파라미터의 소스에도 접근할 수 있어야 합니다. 애플리케이션의 조립 과정에서 설정 컴포넌트는 이러한 설정 파라미터를 애플리케이션 컴포넌트에 제공해서 행동 양식을 제어해야 하기 때문입니다. 예를 들어 어떤 데이터베이스에 접근하는지는 설정 컴포넌트가 설정 파일에 등록된 데이터베이스 설정 정보를 이용해 제어합니다.

설정 컴포넌트는 ‘모든’ 계층의 어댑터 인스턴스를 생성하고 이를 다음 어댑터에게 제공하는 역할을 수행합니다. 이렇게 책임이 많기 때문에 단일 책임의 원칙에 위반합니다. 그러나 애플리케이션의 나머지 부분을 깔끔히 유지하고 싶다면 설정 컴포넌트를 사용하는 것이 좋은 방법입니다.

평범한 코드로 조립하기

의존성 주입 프레임워크의 도움 없이 코드로 직접 조립하는 방법입니다. 이 방법은 애플리케이션을 조립하는 가장 기본적인 방법입니다. 하지만 완전한 엔터프라이즈 애플리케이션을 실행하기 위해서는 어마어마한 양의 코드를 작성해야 할 수도 있습니다. 또한 패키지 외부에서 인스턴스를 생성해야 하기 때문에 전부 public 접근자를 가져야 하므로 원치 않는 의존성을 막을 방법이 없습니다.

스프링의 클래스패스 스캐닝으로 조립하기

이 방법은 package-private 접근자를 이용하면서 위에서 살펴본 번거로운 작업을 대신해 줄 수 있는 의존성 프레임워크를 사용하는 방법입니다. 자바 세계에서 가장 인기 있는 스프링 프레임워크로 알아보겠습니다.

스프링은 클래스패스에서 접근 가능한 클래스들 중 @Component 애노테이션이 붙은 클래스를 찾아, 각 클래스의 인스턴스를 생성하여 애플리케이션 컨텍스트에 추가합니다.

이런 방식은 아키텍처를 더 쉽게 파악할 수 있게 하고 의존성 생성과 의존성 주입을 프레임워크가 대신해 주기 때문에 코드 작성에만 집중할 수 있어 좋습니다. 하지만 이렇게 프레임워크에 특화된 애노테이션을 사용하면 해당 프레임워크에 의존하게 되므로 숨겨진 부수 효과가 발생할 가능성을 염두에 두어야 합니다.

스프링의 자바 컨피그로 조립하기

애플리케이션 컨텍스트에 추가할 빈을 생성하는 설정 클래스를 직접 만들고 빈을 등록할 수 있습니다. 클래스 패스 스캐닝처럼 모든 빈을 가져오는 대신에 설정 클래스만 선택하기 때문에 명시적으로 컴포넌트를 생성하는 것을 관리할 수 있습니다.

이 방식에서는 클래스패스 스캐닝 방식과 달리 @Component 애노테이션을 코드 여기저기에 붙이도록 강제하지 않습니다. 그래서 스프링 프레임워크에 대한 의존성 없이 깔끔하게 유지할 수 있습니다.

하지만 설정 클래스가 생성하는 빈이 설정 클래스와 같은 패키지에 존재하지 않는다면 이 빈들을 publlic으로 만들어야 합니다. 그래서 가시성을 제한하기 위해 패키지를 모듈 경계로 사용하고 각 패키지 안에 전용 설정 클래스를 만들어야 합니다.

이렇게 설정 컴포넌트를 조립해서 쓰면 다른 모듈로부터 독립된 응집도가 높은 모듈을 만들 수 있습니다.