34장 빠져 있는 장

 

들어가며

 

온라인서점시스템에서 고객이 주문 상태를 조회할 수 있어야한다는 유스케이스를 통해 클린아키텍처는 잠시 제쳐놓고 설계나 코드 조직화와 관련된 몇가지 접근법을 살펴봄.

 

 

 

계층 기반 패키지

 

단순한 설계방식으로 수평 계층형 아키텍처가있음.

기술적인 관점에서 해당코드가 하는일(service, repository 등)에 기반해서 그 코드를 계층으로 분할하는것을 계층기반 패키지라 부름.

 

계층 기반 패키지

 

이러한 계층형 아키텍처는 복잡함을 겪지않고 무언가를 작동시켜주지만 업무 도메인에 대해 아무것도 말해주지 않음.

→ 전혀다른 업무도메인이라도 코드를 계층형 아케턱쳐로 만들어 나란히 놓고보면 웹, 서비스, 레파지토리로 구성된 모습이 비슷하게 보이기 때문

 

 

 

기능 기반 패키지

 

서로 연관된 기능, 도메인 개념, 또는 Aggregate Root에 기반하여 수직의 얇은 조각으로 코드를 나누는 방식임.

 

기능 기반 패키지

인터페이스와 클래스는 이전과 같지만 모두가 order라는 단 하나의 패키지에 속하게 됨.

따라서

→ 상위 수준 구조가 업무 도메인에 대해 무엇인가를 알려주게됨.

→ 또한 '주문 조회하기' 유스케이스가 변경될 경우 order라는 한 패키지만 찾으면되기 때문에 수월해짐.

계층기반, 기능기반 둘 중 무엇이 더 낫다 라기보단 저자는 두 접근법 모두 차선책이라고 말함.

 

 

 

포트와 어댑터

 

육각형 아키텍처 혹은 포트와 어댑터방식으로 접근하는 이유는 업무/도메인에 초점을 둔 코드가 프레임워크나 DB같은 세부사항에서 독립적이며 분리된 아키텍처를 만들기 위함임.

아래는 이를 요약한 그림

 

인프라(서드파티)가 도메인을 의존하며 절대 그 반대는 되면 안됨.

 

주문 조회하기 유스케이스

com.mycompany.myapp.domain 패키지가 '내부' 도메인 이며 나머지 패키지는 '외부'임.

추가로 Orders는 OrdersRepository라는 이름에서 바뀐 이름인데

DDD에서는 유비쿼터스 도메인 언어를 관점으로 기술하라고 했기때문에 반영된것임.

→ 예를들면 도메인에 대해 논의할 때 '주문'에 대해 말하는것이지 '주문 레파지토리'에 대해 말하는것이 아니기 때문임.

 

 

 

컴포넌트 기반 패키지

 

SOLID, REP, CCP, CRP 등 이 책에서 나온 여러 조언을 저자는 전적으로 동의하지만 '컴포넌트 기반 패키지'또 다른 선택지를 제시함.

컴포넌트 기반 패키지를 설명하기 앞서

 

 

계층형 아키텍처를 다시 말하자면 계층형아키텍처는 의존성 화살표가 항상 아래로 향해야하고 각 계층은 반드시 바로 아래 계층에만 의존해야 함.

이런 방식으로 깔끔한 비순환 의존성 그래프를 만들 수 있지만 코드베이스의 요소들이 서로 의존할 때는 몇가지 규칙을 반드시 지켜야함.

그러나 속임수를 써서 몇몇 의존성을 의도치 않은 방식으로 추가하더라도 여전히 좋은 비순환 의존성 그래프가 완성됨.

→ 신입사원의 완화된 계층형 아키텍처

 

완화된 계층형 아키텍처

 

여전히 의존성 화살표는 아래를 향하고 있지만 개별 레코드에 대해 인증된 접근만을 허용하는 일을 업무 로직이 책임지는 경우 바람직하지 못함.

 

추가로 컨트롤러가 업무규칙을 포함하게되기 때문에 더더욱 올바르지 못함. 여기서 필요한 지침은 "웹 컨트롤러는 절대로 레파지토리에 직접 접근해서는 안 된다" 임. 컴포넌트 기반 패키지라는 접근법은 서비스 중심적인 시각으로 소프트웨어 시스템을 바라보며, MSA가 가진 시각과도 동일함.

 

포트와 어댑터에서 웹을 그저 전달 메카니즘으로 취급하는 것과 같이 컴포넌트 기반패키지에서도 사용자 인터페이스를 큰 단위의 컴포넌트로 분리함.

 

주문 조회하기 유스케이스

 

'업무로직'과 영속성 관련 코드를 컴포넌트로써 하나로 묶음.

컴포넌트 기반 패키지 접근법의 주된 이점은 Orders와 관련된 무언가를 코딩할 때 오직 OrderComponent만 둘러보면 됨.

업무로직과 영속성 코드가 포함된 컴포넌트 내부의 관심사 분리는 여전히 유효(업무로직과 영속성은 분리되어있음) 함. 사용자는 이 세부사항을 알필요도 없음. 즉 MSA나 SOA를 적용했을 때 얻는 이점과 유사함.

(모노티릭 애플리케이션에서 컴포넌트를 잘 정의하면 MSA로 가기 위한 발판으로 삼을 수 있음.)

 

 

 

구현 세부사항엔 항상 문제가 있다.

 

앞서 말한 네가지 접근법이 코드를 조직화하는 서로다른 아키텍처 스타일로 여겨질 수 있지만 세부사항을 잘못 구현하면 이런 견해도 빠르게 흐트러짐. 예를들면 public 접근 지시자가 있음.

 

자바같은 언어에서 public을 지나칠 정도로 방만하게 사용함. 모든 타입에서 public 지시자를 사용한다는것은 프로그래밍 언어가 제공하는 캡슐화 관련 이점을 활용하지 않겠다는것임.

 

즉, 이로인해 누군가가 구체적인 구현 클래스의 인스턴스를 직접 생성하여 본인이 지향하는 아키텍처 스타일을 위한하게 될 수 있음.

 

 

 

조직화 VS 캡슐화

 

자바 애플리케이션에서 모든 타입을 public으로 지정한다면, 패키지는 단순히 조직화를 위한 매커니즘(폴더구조)으로 전략하여 캡슐화를 위한 메커니즘이 될수 없음. public 지시자를 과용하면 이 장의 앞에서 제시한 네 가지 아키텍처 접근법은 본질적으로 같아짐.

 

 

순서대로 계층기반 패키지, 기능기반 패키지, 업무도메인초점인 포트와 어댑터, 컴포넌트기반 패키지 임.

각 타입의 화살표는 채택하려는 아키텍처 접근법과 상관없이 모두 동일한 방향을 가리킴.

자바의 접근지시자를 적절하게 사용하면 타입을 패키지로 배치하는 방식에 따라서 각 타입에 접근할 수 있는 정도가 크게 달라짐.

 

 

더 제한적인 접근 지시자를 사용하는 타입은 흐리게 표현됨.

계층기반 패키지에선 OrdersService인터페이스와 OrdersRepository인터페이스는 외부의 패키지로부터 자신이 속한 내부로 들어오는 의존성이 존재하므로 public임. 반면 OrdersServiceImpl와 JdbcOrderRepository 구현체는 protected로 더욱 제한됨.

 

나머지 접근법들도 마찬가지임. 이제 패키지 외부의 코드에서 OrdersRepository 인터페이스나 구현체를 직접 사용할 방법이 전혀없음.

 

 

 

다른 결합 분리 모드

 

소스코드 의존성 분리는 프로그래밍언어가 제공하는 방법 외에도 자바 OSGi나 자바9에서제공하는 모듈 시스템으로 분리할 수 있음.

다른 선택지로는 소스코드 수준에서 의존성을 분리하는 방법임.

포트와 어댑터로 예로들면 

  1. 업무와 도메인용 소스 코드: OrderSerivce, OrdersServiceImpl, Orders
  2. 웹용 소스 코드: OrdersController
  3. 데이터 영속성용 소스 코드: JdbcOrderRepository

2, 3번 두 소스 트리는 컴파일 시점에서 업무와 도메인용 소스코드에 의존성을 가지며 업무와 도메인용 소스코드는 웹, 데이터영속성 코드에 대해 알지 못함.

 

이렇게 분리하려면 → 1, 2, 3 각각은 Maven, Gradle 같은 도구로 서로 분리되도록 구성해야함.

이상적인 해결책이지만 이렇게 나누다 보면 성능, 복잡성, 유지보수 문제에 부딪힘.

더 간단한 방법은

  1. 도메인 코드('내부')
  2. 인프라 코드('외부')

로 나누는 것임.

 

 

인프라는 도메인에 대해 컴파일시점에 의존성을 가짐.

단점으로는 페리페리크(프랑스에있는 원형 순환도로로써 북적대는 파리시내에 진입하지 않고도 파리 전체를 둘러볼 수 있음) 안티패턴임.

즉, 특정영역(인프라코드중 웹 컨트롤러 부분)이 애플리케이션의 다른 영역에 있는 코드를 도메인 코드를 통하지않고 직접 호출할 수 있음.

(적절한 접근 지시자 적용을 잊어버렸다면 더욱 막기 힘듦.)

 

 

 

결론

 

<고민해볼 사항>

설계를 어떻게해야 원하는 코드 구조로 매핑가능한가?

해당 코드를 어떻게 조직화 할 것인가?

런타임, 컴파일타임에 어떤 결합 분리모드를 적용할것인가?

 

<수행해야 할 사항>

가능하면 선택사항은 열어두되 실용주의적으로 행해야함.

팀규모, 기술 수준, 해결책의 복잡성을 일정과 예산이라는 제약과 동시에 고려해야함.

선택한 아키텍처 스타일을 강제하는 데 컴파일러의 도움을 받을 수 있는지 고민해야함.

데이터 모델과 같은 영역에 결합되지 않도록 주의해야함.