2023. 5. 26. 20:07ㆍDDD/CleanArchitecture
소개
클린아키텍처: 소프트웨어 구조와 설계의 원칙 책을 읽고 정리하며 소감을 적는 포스트입니다.
경계 해부학
시스템 아키텍처는 일련의 소프트웨어 컴포넌트와 그 컴포넌트들을 분리하는 경계에 의해 정의 된다.
이번 장에서는 경계의 다양한 형태를 알아본다.
경계 횡단하기
런타임에 경계를 횡단한다
함은 그저 경계 한쪽에 있는 기능에서 반대편 기능을 호출하여 데이터를 전달하는 일에 불과하다.
적절한 위치에서 경계를 횡당하게 하는 비결은 소스 코드 의존성 관리에 있다.
소스 코드 모듈이 변경되면 의존되는 다른 소스 코드도 다시 컴파일하고 배포해야 한다.
경계는 이러한 변경이 전파 되는 것을 막는 방화벽을 구축하고 관리하는 수단으로써 존재한다.
두려운 단일체
아키텍처 경계 중에서 가장 단순하며 가장 흔한 형태는 물리적으로 엄격하게 구분되지 않는 형태다.
이 형태에서는 함수와 데이터가 단일 프로세서에서 같은 주소 공간을 공유하며 그저 나름의 규칙에 따라 분리되어 있을 뿐이다. 이전 장에서는 나는 이를 소스 수준 분리 모드라고 불렀다.
배포 관점에서는 소위 단일체(monolith)라고 불리는 단일 실행 파일에 지나지 않는다. 배포 관점에서 볼 때 단일체는 경계가 드러나지 않는다.
가장 단순한 형태의 경계 횡단은 저수준 클라이언트에서 고수준 서비스로 향하는 함수 호출이다. 이 경우 런타임 의존성과 컴파일타임 의존성은 모두 같은 방향, 즉, 저수준 컴포넌트에서 고수준 컴포넌트로 향한다.
제어흐름은 경계를 횡단할 때 저수준에서 고수준으로 향한다.
고수준 클라이언트가 저수준 서비스를 호출해야 한다면 동적 다형성을 사용하여 제어흐름과는 반대 방향으로 의존성을 역전시킬 수 있다. 이렇게 하면 런타임 의존성은 컴파일타임 의존성과는 반대가 된다.
제어흐름은 반대로 경계를 횡단한다.
정적 링크된 모노리틱 구조의 실행 파일이라도 이처럼 규칙적인 방식으로 구조를 분리하면 프로젝트를 개발, 테스트, 배포하는 작업에 큰 도움이 된다.
팀들은 서로의 영역에 침범하지 않은 채 자신만의 컴포넌트를 독립적으로 작업할 수 있다. 고수준 컴포넌트는 저수준 세부사항으로부터 독립적으로 유지된다.
배포형 컴포넌트
아키텍처의 경계가 물리적으로 드러날 수도 있는데 그중 가장 단순한 형태는 동적 링크 라이브러리다. .NET DLL, 자바 jar파일 등이 그예이다.
단일체와 마찬가지로 배포형 컴포넌트의 경계를 가로지르는 통신은 순전히 함수 호출에 지나지 않으므로 매우 값싸다. 동적 링크와 런타임 로디으로 인해 최초의 함수 호출은 오래 걸릴 수도 있지만, 이들 경계를 가로지르는 통신은 매우 빈번할 것이다.
스레드
단일체와 배포형 컴포넌트는 모두 스레드를 활용할 수 있다. 스레드는 아키텍처 경계도 아니며 배포 단위도 아니다. 이보다 스레드는 실행 계획과 순서를 체계화하는 방법에 가깝다. 모든 스레드가 단 하나의 컴포넌트에 포함될 수도 있고, 많은 컴포넌트에 걸쳐 분산될 수도 있다.
로컬 프로세스
훨씬 강한 물리적 형태를 띠는 아키텍처 경계로는 로컬 프로세스가 있다. 로컬 프로세스는 주로 명령행이나 그와 유사한 시스템 호출을 통해 생성 된다.
각 로컬 프로세스는 정적으로 링크된 단일체이거나 동적으로 링크된 여러개의 컴포넌트로 구성될 수 있다. 전자의 경우, 여러 모노리틱 프로세스가 같은 컴포넌트들을 가지고 있을 수 있다.(컴파일하고 정적 링크하는 과정에서 각 컴포넌트의 바이너리가 단일체에 물리적으로 복사되어 들어가기 때문이다.). 반면 후자의 경우, 동적 링크된 배포형 컴포넌트들을 서로 공유할 수 있다.
로컬 프로세스를 일정의 최상위 컴포넌트라고 생각하자. 즉, 로컬 프로세스는 컴포넌트 간 의존성을 동적 다형성을 통해 관리하는 저수준 컴포넌트로 구성된다.
로컬 프로세스 간 분리 전략은 단일체나 바이너리 컴포넌트의 경우와 동일하다. 소스 코드 의존성의 화살표는 단일체나 바이너리 컴포넌트와 동일한 방향으로 경계를 횡단한다. 즉, 항상 고수준 컴포넌트를 향한다.
서비스
물리적인 형태를 띠는 가장 강력한 경계는 바로 서비스다. 서비스는 프로세스로, 일반적으로 명령행 또는 그와 동등한 시스템 호출을 통해 구동된다.
서비스 경계를 지나는 통신은 함수 호출에 비해 매우 느리다. 가능하다면 빈번하게 통신하는 일을 피해야 한다. 이 수준의 통신에서는 지연(latency)에 따른 문제를 고수준에서 처리할 수 있어야 한다.
저수준 서비스는 반드시 고수준 서비스에 플러그인
되어야 한다. 고수준 서비스의 소스 코드에는 저수준 서비스를 특정 짓는 어떤 물리적인 정보(예를 들면 URI)도 포함해서느 ㄴ안된다.
결론
단일체를 제외한 대다수의 시스템은 한 가지 이상의 경계 전략을 사용한다. 서비스 경계를 활용하는 시스템이라면 로컬 프로세스 경계도 일부 포함하고 있을 수 있다. 실제로 서비스는 상호작용하는 일련의 로컬 프로세스 퍼사드(Facade)에 불과할 때가 많다. 또한 개별 서비스 또는 로컬 프로세스는 거의 언제나 소스 코드 컴포넌트로 구성된 단일체이거나, 혹은 동적으로 링크된 배포형 컴포넌트의 집합니다.
즉, 대체로 한 시스템 안에서도 통신이 빈번한 로컬 경계와 지연을 중요하게 고려해야 하는 경계가 혼합되어 있음을 의미한다.
'DDD > CleanArchitecture' 카테고리의 다른 글
20장. 업무 규칙 (0) | 2023.05.27 |
---|---|
19장. 정책과 수준 (0) | 2023.05.27 |
17장. 경계,선긋기 (0) | 2023.05.26 |
16장. 독립성 (1) | 2023.05.25 |
15장. 아키텍처란? (0) | 2023.05.25 |