clean-architecture-book-thumbnail

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

이런 경계는 다양한 형태로 나타난다.

Table of Contents

경계 횡단하기

  • 적절한 위치에서 경계를 횡단하게 하는 비결은 소스 코드 의존성 관리에 있다.

    • 왜냐하면 소스 코드 모듈 하나가 변경되면, 이에 의존하는 다른 소스 코드 모듈도 변경하거나, 다시 컴파일해서 새로 배포해야 할지도 모르기 때문이다.
      경계는 이러한 변경이 전파되는 것을 막는 방화벽을 구축하고 관리하는 수단으로써 존재한다.

두려운 단일체

가장 단순하고 흔한 경계는 물리적으로 엄격하게 구분되지 않는 형태이다.

  • 소스 수준 분리 모드
  • 배포 관점 → 단일체(monolith) .jar파일로 묶인 자바 클래스 파일, 링크된 C, C++ 프로젝트

    • 배포 관점에서 보면 단일체는 경계가 드러나지 않는다.
    • 그렇다고 해서 경계가 존재하지 않거나 무의미 하다는 뜻은 아니며, 그 안에 포함된 다양한 컴포넌트 개발을 독립적으로 수행할 수 있게 하는 일은 대단히 가치 있는 일이다.

가장 단순한 형태의 경계 횡단은 저수준 클라이언트에서 고수준 서비스로 향하는 함수 호출이다.

  • 런타임 의존성과 컴파일타임 의존성은 모두 같은방향, 즉 저수준 컴포넌트에서 고수준 컴포넌트로 향한다.
  • 만약 고수준 클라이언트에서 저수준 서비스를 호출해야 한다면 의존성 역전을 이용하면 된다.

배포형 컴포넌트

  • 아키텍처의 경계가 물리적으로 드러날 수 있는데 가장 단순한 형태는 동적 링크 라이브러리다.(npm, yarn workspace, Gem, jar 등)
  • DLL(Dynamic Link Library)로 배포하면 따로 컴파일하지 않고 바로 사용할 수 있다.
  • 컴포넌트는 바이너리와 같이 배포 가능한 형태로 전달된다.

    • 배포 수준 결합 분리 모드에 해당
  • 배포 과정에서만 차이가 날 뿐, 배포 수준의 컴포넌트는 단일체와 동일한다.
  • 일반적으로 모든 함수가 동일한 프로세서와 주소 공간에 위치하며, 경계를 가로지르는 통신은 순전히 함수 호출과 같아 매우 값싸다.

스레드

단일체와 배포형 컴포넌트는 모두 스레드를 활용할 수 있다.

  • 스레드: 아키텍처 경계x, 배포 단위x, 실행 계획과 순서를 체계화하는 방법에 가까움

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

로컬 프로세스

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

  • 명령행(terminal)이나 유사한 시스템 호출을 통해 생성된다.
  • 로컬 프로세스는 컴포넌트 간 의존성을 동적 다형성을 통해 관리하는 저수준 컴포넌트로 구성된다.
  • 소스 코드 의존성의 화살표는 항상 고수준 컴포넌트를 향한다.

서비스

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

  • 서비스는 프로세스로, 명령행 또는 그와 동등한 시스템 호출을 통해 구동된다.
  • 서비스는 자신의 물리적 위치에 구애받지 않는다.
  • 서비스는 모든 통신이 네트워크를 통해 이뤄진다고 가정한다.
  • 서비스 경계를 지나는 통신은 함수 호출에 비해 매우 느리다.

    • 이때 발생하는 지연(latency)에 따른 문제는 고수준에서 처리할 수 있어야 한다.
  • 저수준 서비스는 고수준 서비스에 '플러그인'되어야 한다.

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

결론

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

실제로 서비스는 상호작용하는 일련의 로컬 프로세스 퍼사드(Faacade)에 불과할 때가 많다.

즉, 대체로 한 시스템 안에서도 통신이 빈번한 로컬 경계와 지연을 중요하게 고려해야 하는 경계가 혼합되어 있음을 의미한다.

References

  • 모든 출처는 Clean Architecture 도서에 있습니다.