‘클린 아키텍처’ 기술 서적에 대해 학습했던 내용을 정리하기 위한 목적의 TIL 포스팅입니다.🙆♂️
경계 횡단하기
- ‘런타임에 경계를 횡단한다’ 함은 그저 경계 한 쪽에 있는 기능에서 반대편 기능을 호출하여 데이터를 전달하는 일에 불과하다.
- 적절한 위치에서 경계를 횡단하는 비결은 소스 코드 의존성 관리에 있다.
- 왜 소스 코드일까? 소스 코므 모듈 하나가 변경되면, 이에 의존하는 다른 소스 코드 모듈도 변경하거나 다시 컴파일 후 재배포해야할지도 모르기 때문이다.
- 경계는 이러한 변경이 전파되는 것을 막는 방화벽을 구축하고 관리하는 수단으로써 존재한다.
두려운 단일체
1) 저수준 클라이언트 -> 고수준 서비스 함수 호출
- 가장 단순한 형태의 경계 횡단이다.
- 이 경우 런타임 의존성과 컴파일 의존성은 모두 저수준 -> 고수준 컴포넌트로 향한다.
- 위 이미지에서 제어흐름은 왼쪽에서 오른쪽으로 경계를 횡단한다.
- Client는 Service의 f() 함수를 호출하는데 경계에서 호출되는 쪽에 Data에 정의가 위치한다는 사실이 중요하다.
2) 고수준 클라이언트 -> 저수준 서비스를 호출
- 의존성을 역전을 이용한다.
- 이렇게 하면 런타임 의존성은 컴파일 타임 의존성과 반대가 된다.
- 모두 오른쪽에서 왼쪽으로 경계를 횡단한다.
고수준은 저수준(세부사항)으로 부터 독립적으로 유지시켜야 함.
- 단일체에서 컴포넌트간 통신은 함수 호출이므로 매우 빠르고 값싸다.
- 소스 수준에서 결합이 분리되면 경계를 가로지르는 통신은 상당히 빈번할 수 있다.
배포형 컴포넌트
- 동적링크 라이브러리(ex: jar)
- => 배포시 컴파일 필요 없고 바이너리와같이 배포 가능한 형태로 전달된다.
- 배포수준 컴포넌트(ex: war)
- => 단순히 배포 가능 한 단위를 좀 더 편리한 형태로 묶은것.
- 위의 둘은 배포 과정에서만 차이 날 뿐, 동일하다.
- 모든 함수가 동일한 프로세서, 주소 공간에 위치하하며,
- 컴포넌트 분리, 의존성 관리하는 전략도 동일하기 때문이다.
스레드
- 단일체와 배포형 컴포넌트 모두 스레드를 활용 가능하다.
- 그러나 스레드는 아키텍처 경계나 배포 단위가 아니며, 단순히 실행 계획과 순서를 체계화하는 방법에 가깝다.
로컬 프로세스
- 훨씬 강한 물리적 형태를 띠는 아키텍처 경계이다.
- 주로 명령행이나 그와 유사한 시스템 호출을 통해 생성된다.
- 각 로컬 프로세스는 정적으로 링크된 단일체이거나 동적으로 링크된 여러 개의 컴포넌트로 구성될 수 있다.
- 전자는 여러 모노리틱 프로세스가 같은 컴포넌트들을 가지고 있을 수 있다.
- 후자의 경우 동적으로 링크된 배포형 컴포넌트들을 서로 공유할 수 있다.
- 로컬프로세스는 컴포넌트 간 의존성을 동적 다형성(의존성역전)을 통해 관리하는 저수준 컴포넌트로 구성된다.
- 로컬프로세스 간 분리 전략도 단일체나 바이너리 컴포넌트의 경우와 동일하다. 즉 항상 고수준 컴포넌트를 향한다.
- 즉 저수준의 프로세스가 고수준 프로세스의 플러그인이 되도록 만드는 것이 아키텍처 관점에서의 목표이다.
서비스
- 물리적인 형태를 띠는 가장 강력한 경계이다.
- 서비스들은 네트워크를 통해 모든 통신이 이뤄진다. 그러기에 함수 호출보단 느리다.
- 이 수준 통신에선 지연(latency)에 따른 문제를 고수준에서 처리할 수 있어야 한다.
- 로컬 프로세스와 마찬가지로 저수준 서비스는 고수준 서비스에 ‘플러그인’되어야 한다.
- 고수준 서비스의 소스 코드에는 저수준 서비스를 특정짓는 어떤 물리적인 정보(예를 들면, URI)도 절대 퐇마해선 안된다.
결론
- 대체로 한 시스템 안에서도 통신이 빈번한 로컬 경계와 지연을 중요하게 고려해야 하는 경계가 혼합되어 있음을 의미한다.