아키텍처 경계 강제하기

경계와 의존성

  • 아키텍처 경계를 강제한다는 것은 의존성이 올바른 방향을 향하도록 강제하는 것을 의미한다. 아키텍처에서 허용되지 않은 의존성을 점선 화살표로 표시했다.

접근 제한자

package-private 제한자가 왜 중요한가?

자바 패키지를 통해 클래스들을 응집적인 모듈로 만들어 주기 때문이다. 이러한 모듈 내에 있는 클래스들은 서로 접근가능하지만, 패키지 바깥에서는 접근할 수 없다. 그럼 모듈의 진입점으로 활용될 클래스들만 골라서 public으로 만들면 된다. 이렇게 하면 의존성이 잘못된 방향을 가리켜서 의존성 규칙을 위반할 위험이 줄어든다.

컴파일 후 체크

의존성 규칙을 위반했는지 확인할 수단

  1. 컴파일 후 체크를 도입한다. (ArchUnit)

package com.book.cleanarchitecture.buckpal;

import com.book.cleanarchitecture.buckpal.archunit.HexagonalArchitecture;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import org.junit.jupiter.api.Test;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;

class DependencyRuleTests {

    // @formatter:off
    @Test
    void validateRegistrationContextArchitecture() {
        HexagonalArchitecture.boundedContext("com.book.cleanarchitecture.buckpal.account")
                    .withDomainLayer("domain")
                    .withAdaptersLayer("adapter")
                    .incoming("in.web")
                    .outgoing("out.persistence")
                    .and()
                .withApplicationLayer("application")
                    .services("service")
                    .incomingPorts("port.in")
                    .outgoingPorts("port.out")
                    .and()
                .withConfiguration("configuration")
                .check(new ClassFileImporter().importPackages("buckpal.."));
    }

    @Test
    void testPackageDependencies() {
        noClasses()
                .that()
                .resideInAPackage("io.reflectoring.reviewapp.domain..")
                .should()
                .dependOnClassesThat()
                .resideInAnyPackage("io.reflectoring.reviewapp.application..")
                .check(new ClassFileImporter().importPackages("io.reflectoring.reviewapp.."));
    }
}

ArchUnit API를 이용하면 적은 작업만으로도 육각형 아키텍처 내에서 관련된 모든 패키지를 명시할 수 있는 일종의 도메인 특화 언어(DSL)를 만들 수도 있고, 패키지 사이의 의존성 방향이 올바른지 자동으로 체크할 수 있다.

빌드 아티팩트

  • 빌드 아티팩트란 아마도 자동화된 빌드 프로세스의 결과물이다.

  • 빌드 도구의 주요한 기능 중 하나는 의존성 해결이다.

  • 각 모듈 혹은 계층에 대해 전용 코드베이스와 빌드 아티팩트로 분리된 빌드 모듈(JAR 파일)을 만들 수 있다. 각 모듈의 빌드 스크립트에서는 아키텍처에서 허용하는 의존성만 지정한다.

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

빌드 모듈로 아키텍처 경계를 구분하는 것은 패키지로 구분하는 방식과 비교했을때의 장점

  1. 빌드 도구가 순환 의존성을 극도로 싫어한다.

  2. 빌드 모듈 방식에서는 다른 모듈을 고려하지 않고 특정 모듈의 코드를 격리한채로 변경할 수 있다.

  3. 모듈 간 의존성이 빌드 스크립트에 분명하게 선언돼 있기 떄문에 새로 의존성을 추가하는 일은 우연이 아닌 의식적인 행동이 된다.

Last updated