김동형수 개발기

만들면서 배우는 클린 아키텍처 - 10 정리 본문

책 스터디/[완료] 만들면서 배우는 클린아키텍처

만들면서 배우는 클린 아키텍처 - 10 정리

김동형수 2022. 4. 19. 01:02

아키텍처 경계 강제하기

일정규모 이상의 프로젝트에서는 시간이 지나면서 아키텍처가 서서히 무너지게 된다. 계층간의 경계가 약화되고 코드는 점점 더 테스트하기 어려워진다.

이번 장에서는 아키텍처 내의 경계를 강제하는 방법과 아키텍처 붕괴에 맞서는 몇가지 조치를 살펴본다.

 


경계와 의존성

애플리케이션 계층은 애플리케이션 서비스안에 유스케이스를 구현하기 위해 도메인 엔티티에 접근한다.

어댑터는 인커밍 포트를 통해 서비스에 접근, 반대로 서비스는 아웃고잉 포트를 통해 어댑터에 접근한다.

설정 계층은 어댑터와 서비스 객체를 생성할 팩터리를 포함하고 있고, 의존성 주입 메커니즘을 제공한다.

 

아키텍처의 경계는 명확하다. 각 계층 사이, 안쪽 인접 계층과 바깥쪽 인접 계층 사이에 경계가 있다.

의존성은 항상 안쪽 방향으로 향해야 한다.

 


접근 제한자

접근제한자의 종류

  • public
  • protected
  • private
  • package-private(default) 

package-private

자바 패키지를 통해 클래스들을 응집적인 '모듈'로 만들어 준다.

패키지 내에서는 클래스끼리 서로 접근은 가능하지만, 패키지 바깥에서는 접근할 수 없다.

모듈의 진입점이 되는 클래스는 public으로 선언하면 된다.

이랗게 하면 의존성이 잘못된 방향을 가리켜서 의존성 규칙을 위반할 위험이 줄어든다.

 

persistence 패키지에 있는 클래스들은 외부에서 접근할 필요가 없기 때문에 package-private로 만들 수 있다.

영속성 어댑터는 자신이 구현하는 출력 포트를 통해 접근된다.

의존성 주입 메커니즘은 일반적으로 reflection을 이용하기 때문에 package-private으로 만들어도 인스턴스를 생성할 수 있다.

이 방법은 클래스패스 스캐닝 방법으로 애플리케이션을 조립할 수 있다. 그 외의 방법은 public 접근지정자로 설정해야한다.

 

domain 패키지는 다른 계층에서 접근할 수 있어야하고, application 속성은 web 어댑터와 persistence 어댑터에서 접근 가능해야 한다.

 

package-private는 적은 수의 클래스가 있을 때 가장 효과적이다. 하나의 패키지 내에 클래스가 특정 개수를 넘어가게 되면 패키지가 너무 많은 클래스를 포함해서 혼란스러워진다.

코드를 쉽게 찾을 수 있도록 하위 패키지를 만드는 방법을 선호한다.

자바는 하위 패키지를 다른 패키지로 취급하기 때문에 하위 패키지의 package-private 클래스에 접근할 수 없다.

그래서 하위 패키지의 클래스는 public으로 만들어서 노출시켜야 하기 때문에 의존성 규칙이 깨질 수 있는 환경이 만들어진다.

 


컴파일 후 체크

의존성 규칙의 위반여부를 체크하기 위해서 컴파일 후 체크를 도입하는 것이다.

컴파일 후 런타임에 체크한다는 뜻이다. 런타임 체크는 지속적인 통합 빌드 환경에서 자동화된 테스트 과정에서 가장 잘 동작한다.

이러한 체크를 도와주는 자바용 도구로 ArchUnit이 있다.

 

 

 

ArchUnit API를 이용한 헥사고날 아키테거 내에 관련된 모든 패키지를 명시할 수 있는 일종의 DSL을 만들 수 있고 패키지 사이의 의존성이나 방향이 올바른지 자동으로 체크할 수 있다.

 

패키지명의 오타가 있다면 해당 테스트코드는 계층간 오류를 찾지 못한다.

이런 상황을 커버할 수 있는 테스트 코드가 필요하다.

여전히 리팩터링에 취약한 것은 사실이다. 컴파일 후 체크는 언제나 코드와 함께 유지보수해야한다.

 


빌드 아티팩트

자바에서 인기있는 빌드 도구들은 메이븐과 그래이들이다.

빌드 도구의 주요한 기능 중 하나는 의존성 해결이다. 코드베이스가 의존하고 있는 모든 아티팩트가 사용 가능한지 확인한다.

없다면 아티펙트 리포지토리(maven centenal, nexus, 등등)에서 가져오려고 시도한다. 이마저도 실패한다면 컴파일 에러가 발생한다.

 

계층간 유효성 검사를 위한 빌드 도구 활용

계층간 의존성 강제

각 계층에 대해 전용 코드베이스, 빌드 아티팩트로 분리된 모듈을 만들 수 있다.

각 모듈의 빌드스크립트에는 아키텍처에서 허용하는 의존성만 지정 -> 존재하지 않는 클래스일 경우 에러 발생

실수로 인한 잘못된 의존성을 만들 수 없다.

영속성 계층의 변경이 웹 계층에 영향을 미치거나 웹 계층의 변경이 영속성 계층에 영향을 미치는 것을 바라지 않을 것이다. ( 단일 책임 원칙 )

 

모듈을 더 세분화 할수록, 모듈 간 의존성을 더 잘 제어할 수 있게 된다는 것이다. 하지만 더 작게 분리할수록 모듈간에 매핑을 더 많이 수행해야한다.

 

빌드 모듈로 경계 구분할때 패키지 구분보다 나은점

  1. 빌드 도구가 순환 의존성을 극도로 싫어한다.
  2. 빌드 모듈 방식에는 다른 모듈을 고려하지 않고 특정 모듈의 코드를 경리한 채로 변경할 수 있다.
  3. 모듈간 의존성이 빌드스크립트에 선언이 되어있기 떄문에 새로운 의존성을 추가하는 일은 일반적인 행동이 된다.

빌드 스크립트를 유지보수하는 비용도 있다. 

 


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

의존성이 올바른 방향을 가리키고 았는지 지속적으로 확인 필요.

package-private 접근 제한자를 이용해서 의도치 않은 의존성을 피한다.

하나의 모듈 안에서 아키텍처 경계를 강제해야하고, 패키지 구조상 package-private 접근 제한자를 사용할 수 없다면, ArchUnit 같은 컴파일 후 체크 도구를 이용한다.

 

아키텍처 경계를 강화하고 시간이 지나도 유지보수하기 좋은 코드를 만들기 위해 세 가지 접근 방식 모듀를 함께 조합해서 사용할 수 있다.

Comments