책 일지/만들면서 배우는 클린 아키텍쳐

9장 애플리케이션 조립하기

worldi 2024. 1. 9. 22:34

모든 의존성은 안쪽으로, 애플리케이션의 도메인 코드 방향으로 향해야한다. → 도메인 코드가 바깥 계층의 변경으로부터 안전하다.

 

유스케이스는 인터페이스만 알아야 하고, 런타임에 구현을 제공받는다.

⇒ 생성자를 통해 객체를 전달 받기 때문에 실제 객체 대신 목으로 전달할 수 있고, 격리된 단위 테스트를 생성하기 쉬워진다.

 

그렇다면 객체 인스턴스를 생성할 책임은 누구에게 있을까?

 

모든 클래스에 대한 의존성을 가지는 설정 컴포넌트가 있어야 한다.

 

 

다음과 같이 중립적인 설정 컴포넌트는 인스턴스 생성을 위해 모든 클래스에 접근 가능하다.

설정 컴포넌트의 책임과 역할

  • 웹 어댑터 인스턴스 생성, HTTP 요청 → 웹 어댑터 전달 보장
  • 유스케이스 인스턴스 생성, 웹어댑터에 유스케이스 인스턴스 제공
  • 영속성 어댑터 인스턴스 생성, 유스케이스에 영속성 어댑터 인스턴스 제공
  • 영속성 어댑터가 실제로 데이터베이스에 접근할 수 있도록 보장
  • 설정 컴포넌트는 설정 파일이나 커맨드 라인 파라미터 등과 같은 설정 파라미터의 소스에 접근할 수 있다.

만약 이를 평범한 코드로 조립할라면,,?

  • 엔터프라이즈 애플리케이션을 실행하기 위햇, 인스턴스 생성하는 로직을 다 작성해야 한다.
  • 각 클래스가 속한 패키지 외부에서 인스턴스를 생성하기 때문에 클래스가 모두 public이어야 한다. → 이는 추후 의존성 주입 프레임 워크(스프링과 같은) 로 인해 package-private 의존성을 유지한다.

설정 컴포넌트 방법 1 : 스프링의 클래스 패스 스캐닝

스프링은 클래스 패스에서 접근 가능한 모든 클래스를 확인해서, @Component 애너테이션이 붙은 클래스를 찾는다. 그리고 이에 대한 객체를 생성한다.

애너테이션과 생성자 관리를 통해 편리하게 애플리케이션을 조립할 수 있다.

또한, 커스텀 애너테이션을 만들 수 있다.

package com.book.cleanarchitecture.buckpal.shared;

import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface PersistenceAdapter {

    @AliasFor(annotation = Component.class)
    String value() default "";
}

 

이를 통해 아키텍처에 대해 쉽게 파악할 수 있다.

단점

  • 클래스에 프레임워크에 특화된 애너테이션을 붙여야 한다. 라이브러리나 프레임 워크를 만들 때는 스프링 프레임워크 의존성이 생겨버리기 때문에 사용하지 말아야 한다.
  • 애플리케이션 컨텍스트에 기대하지 않은 클래스가 올라갈 수 있다.

설정 컴포넌트 2 : 스프링의 자바 컨피그

애플리케이션 컨텍스트에 추가할 빈을 생성하는 설정 클래스를 만드는 방법이다.

 

@Configuration 애너테이션을 통해 스프링의 클래스패스 스캐닝에서 발견해야 할 설정 클래스임을 표시한다.

 

@EnableJpaRepositories 애너테이션을 이용하면, 스프링이 우리가 정의한 스프링 데이터 레포지터리 인터페이스의 구현체를 직접 생성해서 제공한다.

@EnableJpaRepositories는 메인 애플리케이션에도 붙일 수 있다. 이는, 애플리케이션이 시작할 때 마다 JPA를 활성화 시켜 영속성이 실질적으로 필요없는 테스트에서 애플리케이션을 실행할 때도 JPA 리포지토리들을 활성화한다. 따라서 이를 모듈에다가 옮기는 것이 한꺼번에 시작할 필요없이 좋다.

장점

  • 컨피그파일을 통해 특정 모듈만 포함하고, 다른 모듈은 빈을 모킹해서 애플리케이션을 만들 수 있다.
    • 이러면 테스트가 유연해진다. 리팩터링을 많이 하지 않고도, 각 모듈의 코드를 자체 코드베이스, 자체 패키지, 자체JAR 파일로 밀어넣을 수 있다.
  • 애플리케이션 계층을 스프링 프레임 워크에 대한 의존성 없이 깔끔하게 유지할 수 있다.

단점

  • 설정 클래스가 생성하는 빈이 설정 클래스와 같은 패키지에 존재하지 않는 다면 이 빈들을 public 으로 만들어야 한다.
  • 패키지를 모듈 경계로 사용하고, 각 패키지 안에 전용 설정 클래스를 만들 수 있지만 하위 패키지를 사용할 수 없다 (??) → 10장에서 다룬다.

유지보수 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?

클래스패스 스캐닝 방식은 빠르게 개발할 수 있지만, 코드의 규모가 커지면, 투명성이 낮아진다. 적, 어떤 빈이 애플리케이션 컨텍스트에 올라오는지 알 수없고, 테스트 할 때에도, 애플리케이션 컨텍스트 일부분을 독립적으로 띄우기 힘들다.

 

반면, 전용 설정 컴포넌트 같은 경우는 이에 대해 자유로워지고, 응집도가 매우 높다. 하지만 유지보수하는데 시간이 걸린다.