경계와 의존성

  • 가장 안쪽 계층에는 도메인 엔티티가 있다.
  • 애플리케이션 계층은 애프리케이션 서비스 안에 유스케이스를 구현하기 위해 도메인 엔티티에 접근한다.
  • 어댑터는 인커밍 포트를 통해 서비스에 접근하고, 반대로 서비스는 아웃고잉 포트를 통해 어댑터에 접근한다.
  • 설정 계층은 어댑터와 서비스 객체를 생성할 팩터리를 포함하고 있고, 의존성 주입 메커니즘을 제공한다.

접근 제한자

  • package-private 제한자: 패키지 내에 있는 클래스들은 서로 접근 가능하지만, 패키지 바깥에서는 접근할 수 없다.
  • 모듈의 진입점으로 활용될 클래스들만 골라서 public으로 만들면, 의존성이 잘못된 방향을 가리켜서 의존성 규칙을 위반할 위험이 줄어든다.
  • 어댑터와 포트의 구현체인 서비스는, 외부에서 접근할 필요가 없기 때문에 package-private으로 만들 수 있다.
    • 의존성 주입 메커티즘은 일반적으로 리플렉션을 이용해 클래스를 인스턴스로 만들기 때문에 package-private이라도 여전히 인스턴스를 만들 수 있다.
  • 입출력 포트와 도메인 엔티티 클래스는 public이어야 한다.
  • package-private 제한자는 몇 개 정도의 클래스로만 이뤄진 작은 모듈에서 가장 효과적이다.
    • 그러나 패키지 내의 클래스가 특정 개수를 넘어가기 시작하면 하나의 패키지에 너무 맣은 클래스를 포함하는 것이 혼란스워지게 된다.
    • 패키지 내 클래스가 많아지면 하위 패키지를 만드는 방법을 주로 선호한다.
    • 하지만 이렇게 하면 자바는 하위 패키지를 다른 패키지로 취급하기 때문에 하위 클래스의 package-private 멤버에 접근할 수 없게 된다.
    • 그래서 하위 패키지의 멤버는 public으로 만들어서 바깥 세계에 노출시켜야 하기 때문에 우리의 아키텍처에 의존성 규칙이 깨질 수 있는 환경이 만들어진다.

컴파일 후 체크

  • 컴파일 후 체크: 코드가 컴파일된 후에 런타임에 체크한다.
    • 이러한 런타임 체크는 지속적인 통합 빌드 환경에서 자동화된 테스트 과정에서 가장 잘 동작한다.
  • 이러한 체크를 도와주는 자바용 도구로 ArchUnit이 있다.
    • JUnit과 같은 단위 테스트 프레임워크 기반에서 가장 잘 동작하며 의존성 규칙을 위반할 경우 테스트를 실패시킨다.

빌드 아티팩트

  • 코드베이스를 빌드 아티팩트로 변환하기 위해 빌드 도구가 가장 먼저 할 일은 코드베이스가 의존하고 있는 모든 아티팩트가 사용 가능한지 확인하는 것이다.
    • 만약 사용 불가능한 것이 있다면 아티팩트 리포지토리로부터 가져오려고 시도하고, 이마저도 실패한다면 코드를 컴파일도 하기 전에 에러와 함께 빌드가 실패한다.
    • 이를 통해서 모듈과 아키텍처의 계층 간의 의존성을 강제할 수 있다.
    • 각 모듈 혹은 계층에 대해 전용 코드베이스와 빌드 아티팩트로 분리된 빌드 모듈을 만들 수 있다.
    • 각 모듈은 빌드 스크립트에서는 아키텍처에서 허용하는 의존성만 지정한다.
  • 빌드 모듈로 아키텍처 경계를 구분하는 것은 패키지로 구분하는 방식에 비해 얻는 장점
    • 빌드 도구는 순환 의존성을 허용하지 않는다.
    • 빌드 모듈 방식에서는 다른 모듈을 고려하지 않고 특정 모듈의 코드르 격리한 채로 변경할 수 있다.
    • 모듈 간 의존성이 빌드 스크립트에 분명하게 선언하게 선언돼 있기 때문에 새로 의존성을 추가하는 일은 의식적인 행동이 된다.