본문 바로가기
프로그래밍 놀이터/디자인 패턴, 리펙토링

[책 정리] 18장. 경계 해부학 - Clean Architecture

by 돼지왕 왕돼지 2022. 10. 24.
반응형

 

-

시스템 아키텍처는 일련의 소프트웨어 컴포넌트와 그 컴포넌트들을 분리하는 경계에 의해 정의된다.

 

 

 

경계 횡단하기

 

-

'런타임에 경계를 횡단한다' 함은 그저 경계 한쪽에 있는 기능에서 반대편 기능을 호출하여 데이터를 전달하는 일에 불과하다.

경계는 변경이 전파되는 것을 막는 방화벽을 구축하고 관리하는 수단으로써 존재한다.

 

 

 

두려운 단일체

 

-

아키텍처 경계 중에서 가장 단순하며 가장 흔한 형태는 물리적으로 엄격하게 구분되지 않는 형태다.

이 형태에서는 함수와 데이터가 단일 프로세서에서 같은 주소 공간을 공유하며 그저 나름의 규칙에 따라 분리되어 있을 뿐이다.

이를 소스 수준 분리 모드라고 부를 수 있다.

 

배포 관점에서 보면 소위 단일체(monolitic)라고 불리는 단일 실행 파일에 지나지 않는다.

이 파일은 정적으로 링크된 C, C++ 프로젝트이거나, 실행 가능한 jar 파일로 묶인 일련의 자바 클래스 파일이거나, 단일 .EXE 파일로 묶인 일련의 .NET 라이브러리 등일 것이다.

 

배포 관점에서 볼 때 단일체는 경계가 드러나지 않는다.

이것이 경계가 실제로 존재하지 않거나, 경계 자체가 무의미하다는 뜻은 아니다.

그 안에 포함된 다양한 컴포넌트를 개발하고 바이너리로 만드는 과정을 독립적으로 수행할 수 있게 하는 일은 대단히 가치 있는 일이다.

 

 

-

제어흐름은 경계를 횡단할 때 저수준에서 고수준으로 향한다.

주목할 점은 경계에서 호출되는 쪽에 Data 에 대한 정의가 위치한다는 사실이다.

고수준 클라이언트가 저수준 서비스를 호출해야 한다면 동적 다형성을 사용하여 제어흐름과는 반대 방향으로 의존성을 역전시킬 수 있다.

 

 

-

정적 링크된 모노리틱 구조의 실행 파일이라도 이처럼 규칙적인 방식으로 구조를 분리하면 프로젝트를 개발, 테스트, 배포하는 작업에 큰 도움이 된다.

 

 

-

단일체에서 컴포넌트 간 통신은 매우 빠르고 값싸다.

단일체를 배포하는 일은 일반적으로 컴파일과 정적 링크 작업을 수반하므로, 대체로 이러한 시스템에서 컴포넌트는 소스 코드 형태로 전달된다.

 

 

 

배포형 컴포넌트

 

-

아키텍처의 경계가 물리적으로 드러날 수 있는데 그 중 가장 단순한 형태는 동적 링크 라이브러리다.

.NET DLL, 자바 jar 파일, 루비 잼(Gem), 유닉스 공유 라이브러리 등이 그 예다.

컴포넌트를 이 형태로 배포하면 따로 컴파일하지 않고 곧바로 사용할 수 있다.

 

 

-

단일체와 마찬가지로 배포형 컴포넌트의 경계를 가로지르는 통신은 순전히 함수 호출에 지나지 않으므로 매우 값싸다.

동적 링크와 런타임 로딩으로 인해 최초의 함수 호출은 오래 걸릴 수 있지만, 대체로 이들 경계를 가로지르는 통신은 매우 빈번하다.

 

 

 

스레드

 

-

스레드는 실행 계획과 순서를 체계화하는 방법이지 아키텍처 경계도 아니고 배포 단위도 아니다.

모든 스레드가 단 하나의 컴포넌트에 포함될 수도 있고, 많은 컴포넌트에 걸쳐 분산될 수도 있다.

 

 

 

로컬 프로세스

 

-

훨씬 강한 물리적 형태를 띠는 아키텍처 경계로는 로컬 프로세스가 있다.

로컬 프로세스들은 동일한 프로세서 또는 하나의 멀티코어 시스템에 속한 여러 프로세서들에서 실행되지만, 각각이 독립된 주소 공간에서 실행된다.

대개의 경우 로컬 프로세스는 소켓(socket)이나 메일박스(mailbox), 메시지 큐(message queue)와 같이 운영체제에서 제공하는 통신 기능을 이용하여 서로 통신한다.

 

 

-

로컬 프로세스 간 분리 전략 역시 의존성의 화살표는 항상 고수준 컴포넌트를 향한다.

따라서 로컬 프로세스에서는 고수준 프로세스의 소스 코드가 저수준 프로세스의 이름, 물리 주소, 레지스트리 조회 키를 절대로 포함해서는 안 된다.

저수준 프로세스가 고수준 프로세스의 플러그인이 되도록 만드는 것이 아키텍처 관점의 목표라는 사실을 기억하자.

 

 

-

로컬 프로세스 경계를 지나는 통신에는 운영체제 호출, 데이터 마샬링 및 언마샬링, 프로세스 간 문맥 교환 등이 있으며, 이들은 제법 비싼 작업에 속한다.

 

 

 

서비스

 

-

물리적인 형태를 띠는 가장 강력한 경계는 바로 서비스다.

서비스는 프로세스로 일반적으로는 명령행 또는 그와 동등한 시스템 호출을 통해 구동된다.

서비스는 자신의 물리적 위치에 구애받지 않는다.

서로 통신하는 두 서비스는 물리적으로 동일한 프로세서나 멀티코어에서 동작할 수도 있고, 아닐 수도 있다.

서비스들은 모든 통신이 네트워크를 통해 이뤄진다고 가정한다.

 

 

-

서비스 경계를 지나는 통신은 함수 호출에 비해 매우 느리다.

이 수준의 통신에서는 지연(latency)에 따른 문제를 고수준에서 처리할 수 있어야 한다.

이를 제외하고는 나머지와 동일하게 저수준 서비스는 반드시 고수준 서비스에 '플러그인' 되어야 한다.

고수준 서비스의 소스 코드에는 저수준 서비스를 특정 짓는 어떤 물리적인 정보도 절대 포함해서는 안 된다. (URI 도)

 

 

 

결론

 

-

단일체를 제외한 대다수의 시스템은 한 가지 이상의 경계 전략을 사용한다.

그리고 저수준의 것이 고수준에 플러그인 된다.

 

 

반응형

댓글