본문 바로가기

Design/Architecture

[Clean Architecture] 아키텍처 - 독립성

아키텍처의 독립성을 알아보기전 좋은 아키텍처가 지원해야하는 것에 대해서 먼저 알아보도록하자.

 

좋은 아키텍처가 지원해야하는 것

좋은 아키텍처는 다음 네가지를 지원해야한다.

  • 시스템의 유스케이스
  • 시스템의 운영
  • 시스템의 개발
  • 시스템의 배포

 

1) 유스케이스

  • 기본적으로 아키텍처는 시스템의 행위에 그다지 큰 영향을 주지 않음
  • 하지만 좋은 아키텍처가 행위를 지원하기 위해 할 수 있는 일 중 가장 중요한 일은 행위를 명확히 하고 외부로 드러내며, 이를통해 시스템이 지닌 의도롤 아키텍처 수준에서 알아볼 수 있게 만드는 것이다.
  • 따라서 개발자가 일일이 일급요소(행위)를 찾아 헤매지 않아도 된다.
  • "소리치는 아키텍처" 챕터를 꼭 참고해보자

2) 운영

  • 시스템 운영의 차원에서 아키텍처는 더 실질적인 역할을 맡는다.
  • 아키텍처는 각 유스케이스에 걸맞는 처리량과 응답시간을 보장해야한다.
    ex)
    1. 서로 다른 많은 서버에서 병렬로 실행할 수 있게 해야함.
    2. 경량의 수많은 스레드가 단일 프로세서에서 같은 주소 공간을 공유하도록 해야함.
    3. 독립된 주소 공간에서 실행되는 소수의 프로세스만으로 충분.
  • 뛰어난 아키텍트라면 이러한 결정은 열어두어야 하는 선택사항 중 하나.
  • 즉, 모노리틱 <-> 다중 프로세스 <-> 다중 스레드 <-> 마이크로서비스 형태가 필요해질 때 개선하기 쉬운 구조로 설계.

 

3) 개발

  • 아키텍처는 개발환경을 지원하는데 핵심적인 역할을 수행.
  • "시스템을 설계하는 조직이라면 어디든지 그 조직의 의사소통 구조와 동일한 구조의 설계를 만들어 낼 것이다." - 콘웨이의 법칙
  • 각 팀이 독립적으로 행동하기 편한 아키텍처를 확보하여 개발하는 동안 서로 방해하지 않도록 해야한다.
  • 즉, 독립적으로 개발 가능한 컴포넌트 단위로 시스템을 분할 할 수 있어야 한다.

 

4) 배포

  • 아키텍처는 배포 용이성을 결정하는데 중요한 역할을 맡으며, 목표는 "즉각적인 배포"다.
  • 좋은 아키텍처는 배포를 할때, 수십 개의 작은 설정 스크립트나 속성 파일을 약간씩 수정하는 방식을 사용하지 않는다.
  • 좋은 아키텍처는 시스템이 빌드된 후 즉각 배포할 수 있도록 지원해야 한다.
    - 컴포넌트 단위로 적절하게 분할, 격리해야함.
    - 마스터 컴포넌트는 시스템 전체를 하나로 묶고, 각 컴포넌트를 올바르게 구동하고 통합하고 관리해야함.

선택사항 열어놓기

  • 좋은 아키텍처는 컴포넌트 구조와 관련된 이 관심사들 사이에서 균형을 맞추고, 각 관심사 모두를 만족시킨다.
  • 실제로 이러한 균형을 실천하는 것은 어렵지만, 목표점이 명확치 않더라도 독립성과 관련된 아키텍처 원칙을 지켜 구현한다면 도움이 된다.
  • 또한 선택사항을 가능한 한 많이 그리고 가능한한 오랫동안 열어 둘 수 있게 해준다.

 

독립성과 관련된 원칙

아키텍트는 시스템구조를 정할때 단일 책임 원칙공통 폐쇄 원칙을 적용하여야 한다.
- 단일 책임 원칙 : 다른 이유로 변경되는 것은 분리
- 공통 폐쇄 원칙 : 동일한 이유로 변경되는 것들은 묶는다.

 

계층 결합 분리

  • UI의 변경은 업무규칙의 변경과는 아무런 관련이 없다. -> 분리 
  • 업무규칙의 변경에 관해서 데이터베이스 그리고 쿼리언어 심지어 스키마까지 아무런 관계가 없다. -> 분리
  • 이러한 방식으로 수평적인 계층으로 분리 할 수 있다.

 

유스케이스 결합 분리

  • 유스케이스 자체는 서로 다른 이유로 변경되는 것이라고 할 수 있다.
  • 또한 시스템을 분할하는 매우 자연스러운 방법.
  • 유스케이스는 시스템의 수평 계층을 가로지르도록 자른, 수직으로 좁다란 조각이기도 하다. (수직적인 계층으로 분리)
  • 유스케이스는 UI, 핵심업무, 데이터베이스등 맨 아래 계층까지 각 계층에서 서로 겹치지 않게 해야한다.
  • 시스템에서 서로 다른 이유로 변경되는 요소들의 결합을 분리하면 기존 요소에 지장을 주지 않고도 새로운 유스케이스를 계속 추가할 수 있다.

 

[Clean Architecture] 수직,수평 계층분할 예시

 

효과

이제 이러한 결합 분리의 효과가 어떻게 아키텍처가 지원해야하는 것과 관련이 되는지 살펴보도록 하자.

 

1) 개발

  • 컴포넌트가 완전히 분리되면 팀 사이의 간섭은 줄어든다.
    ex) UI에 중점을 둔 팀은 업무 규칙에 중점을 둔 팀에 그다지 영향을 줄 수 없다. 서로 다른 유스케이스를 다루는 팀에서도 마찬가지다!
  • 기능 팀, 컴포넌트 팀, 계층 팀, 혹은 또 다른 형태의 팀이라도 아키텍처는 그 팀의 구조를 뒷받침해 줄 것이다.

 

2) 배포

  • 유스케이스와 케층의 결합이 분리되면 배포 측면에서 고도의 유연성이 생긴다.
  • 결합을 제대로 분리했다면 운영 중인 시스템에서 계층과 유스케이스를 교체(hot swap)할 수 있다. (단순히 jar 혹은 DLL 파일 교체)

 

3) 운영

  • 수직 계층 분리가 이루어진 상태라면 높은 처리량을 보장해야 하는 유스케이스와 낮은 처리량으로도 충분한 유스케이스는 이미 분리되어 있을 것.
  • 수평 계층 분리가 이루어진 상태라면 UI와 데이터베이스는 업무 규칙과는 다른 서버에서 실행 될 수 있다.
  • 운영 측면 이점을 살리기 위해 결합 분리 모드 선택이 필요하다.
    1. 소스 수준 분리 모드 :
       - 소스 코드 모듈 사이의 의존성을 제어할 수 있다.
       - 서로 통신할때는 함수호출을 사용.
       - 같은 주소공간에서 실행되며, 메모리에는 하나의 실행 파일만 로드된다.
       - 모노리틱 구조
    2. 배포 수준 분리 모드 : 
       - jar, DLL, 공유 라이브러리와 같이 배포 가능한 단위들 사이의 의존성을 제어할 수 있다.
       - 한 모듈의 수정이 다른 모듈의 재배포에 영향을 주지 않는다.
       - 함수 호출을 통해 사용.
       - 컴포넌트가 같은 주소공간에 실행.
       - 어떤 컴포넌트는 동일 프로세서의 다른 프로세스에 상주하고, 프로세스간 통신, 소켓, 또는 공유 메모리를 통해 서로 통신.
    3. 서비스 수준 분리 모드 : 
       - 의존 수준을 데이터 구조 단위까지 낮출 수 있음.
       - 순전히 네트워크 패킷을 통해서만 통신하도록 만들 수 있다.
       - 모든 실행 가능한 단위는 소스와 바이너리 변경에 대해 서로 완전히 독립적이게 된다.
       - 예로는 service 또는 micro service가 있다.

 

이 요소들 외에 아래에 대해서 추가로 고민해 보도록 하자.

중복

- 소프트웨어에서 중복은 나쁜 것이지만, 이 중복이 진짜 중복인지 아니면 거짓 또는 우발적 중복인지 구분되어야한다.

- 진짜 중복은 한 인스턴스가 변경되면, 동일 변경을 그 인스턴스의 모든 복사본에 적용해야하는 케이스이다.

- 가짜 중복은 중복으로 보이는 두 코드 영역이 서로 다른 속도와 다른 이유로 변경이 되는 것을 이야기한다.

- 유스케이스를 수직으로 분리할 때 마주칠 수 있는 문제이다.

- 계층을 수평으로 분리하는 경우는 특정 데이터베이스 레코드의 데이터 구조가 특정 화면의 데이터 구조와 상당히 비슷하여 레코드를 있는 그대로 UI까지 전달하고 싶을 수 있지만, 이는 가짜 중복이다. 따라서 뷰 모델로 두 계층을 분리해주어야 한다.

 

결합 분리 모드

- 위에서 알아본 세가지 모드들은 프로젝트 초기 단계에서는 어떤 모드가 최선인지 알기 어렵다.

- 프로젝트가 성숙해갈수록 최적 모드가 달라질 수 있다.

- 서비스 수준의 결합 분리는 비용이 많이 들고, 결합이 큰 단위에서 분리된다. 그리고 서비스 경계 처리에 대한 작업은 비싸다.

- 따라서 서비스가 되기 직전에서 멈추는 것을 추천.

- 서비스화 가능성이 있는 부분은 컴포넌트 결합을 분리하되 가능한한 오랫동안 동일 주소 공간에 남겨두도록 하자.

- 좋은 아키텍처는 시스템이 모노리틱 -> 마이크로서비스 수준까지 성장할 수 있도록 만들어진다.

- 반대로 상황에 따라 역순 진행도 가능해야한다. (마이크로 서비스 -> 모노리틱)

 

결론

뛰어난 아키텍트라면 변경을 예측하여, 큰 무리 없이 시스템의 결합 분리 모드를 반영할 수 있도록 만들어야 한다.