본문으로 건너뛰기

16. 독립성

좋은 아키텍처는 다음을 지원해야 한다.

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

유스 케이스

시스템 아키텍처는 시스템의 의도를 지원해야 한다. 아키텍트의 최우선 관심사는 유스 케이스이며, 아키텍처에서도 유스 케이스가 우선이다.

아키텍처는 시스템의 행위에 그다지 큰 영향을 주지 않는다. 행위와 관련하여 아키텍처가 열어둘 수 있는 선택사항은 거의 없다.

하지만 영향력이 전부는 아니다. 좋은 아키텍처가 행위를 지원하기 위해 할 수 있는 일 중에서 가장 중요한 사항은 행위를 명확히 하고 외부로 드러내며, 이를 통해 시스템이 지닌 의도를 아키텍처 수준에서 알아볼 수 있게 만드는 것이다.

운영

운영에 대한 결정은 뛰어난 아키텍트라면 열어 두어야 하는 선택사항 중의 하나이다.

만약 시스템이 모노리틱 구조를 갖는다면, 다중 프로세스, 다중 스레드 또는 마이크로 서비스 형태가 필요해질 때 개선하기가 어렵다.

그에 비해 아키텍처에서 각 컴포넌트를 적절히 격리하여 유지하고 컴포넌트 간 통신 방식을 특정 형태로 제한하지 않는다면, 시간이 지나 운영에 필요한 요구사항이 바뀌더라도 스레드, 프로세스, 서비스로 구성된 기술 스펙트럼 사이를 전환하는 일이 훨씬 쉬워질 것이다.

개발

콘웨이 법칙이 작용하는 지점이다. (시스템을 설계하는 조직이라면 어디든지 그 조직의 의사소통 구조와 동일한 구조의 설계를 만들어 낼 것이다.)

많은 팀으로 구성되며 관심사가 다양한 조직에서 어떤 시스템을 개발해야 한다면, 각 팀이 독립적으로 행동하기 편한 아키텍처를 확보하여 개발하는 동안 팀들이 서로를 방해하지 않도록 해야 한다.

이러한 아키텍처를 만들기 위해선 잘 격리되어 독립적으로 개발 가능한 컴포넌트 단위로 시스템을 분할할 수 있어야 한다.

배포

또한 아키텍처는 배포 용이성을 결정하는 데 중요한 역할을 한다. 이때 목표는 '즉각적인 배포'다.

선택사항 열어놓기

좋은 아키텍처는 컴포넌트 구조와 관련된 이 관심사들 사이에서 균형을 맞추고, 각 관심사 모두를 만족시킨다.

하지만 현실에선 이러한 균형을 잡기 매우 어렵다. 대부분의 경우 우리는 모든 유스 케이스를 알 수는 없으며, 운영하는 데 따르는 제약사항, 팀 구조, 배포 요구사항도 알지 못하기 때문이다. 우리가 도달하려는 목표는 뚜렷하지 않을 뿐만 아니라 시시각각 변한다. 이런 변화 속에서도 사라지지 않는 것이 있다.

몇몇 아키텍처 원칙은 구현하는 비용이 비교적 비싸지 않으며, 관심사들 사이에서 균형을 잡는 데 도움이 된다.

이들 원칙은 시스템을 제대로 격리된 컴포넌트 단위로 분할할 때 도움이 되며, 이를 통해 선택사항을 가능한 한 많이, 그리고 가능한 한 오랫동안 열어 둘 수 있게 해준다.

좋은 아키텍처는 선택사항을 열어 둠으로써, 향후 시스템에 변경이 필요할 때 어떤 방향으로든 쉽게 변경할 수 있도록 한다.

계층 결합 분리

아키텍트는 필요한 모든 유스 케이스를 지원할 수 있는 시스템 구조를 원하지만, 유스 케이스 전부를 알지는 못한다.

하지만 아키텍트는 시스템의 기본적인 의도는 분명히 알고 있다.

그 시스템이 장바구니 시스템인지, 자재 명세서 시스템인지, 또는 주문처리 시스템인지는 안다.

따라서 아키텍트는 단일 책임 원칙과 공통 폐쇄 원칙을 적용하여 그 의도의 맥락에 따라서 다른 이유로 변경되는 것들은 분리하고, 동일한 이유로 변경되는 것들은 묶는다.

뛰어난 아키텍트는 유스 케이스에서 UI 부분과 비즈니스 로직 부분을 서로 분리하고자 할 것이다. 이렇게 함으로써 두 요소를 서로 독립적으로 변경할 수 있을 뿐만 아니라, 유스 케이스는 여전히 가시적이며 분명하게 유지할 수 있다.

데이터베이스, 쿼리 언어, 심지어 스키마조차도 기술적인 세부사항이며, 비즈니스 로직이나 UI와는 아무런 관련이 없다. 이들은 시스템의 다른 측면과는 다른 속도로, 그리고 다른 이유로 변경된다. 결론적으로 아키텍트는 이들을 시스템의 나머지 부분으로부터 분리하여 독립적으로 변경할 수 있도록 해야 한다.

유스 케이스 결합 분리

유스 케이스는 시스템의 수평적인 계층을 가로지르도록 자른, 수직으로 좁다란 조각이기도 하다. 각 유스 케이스는 UI의 일부, 애플리케이션 특화 비즈니스 로직의 일부, 그리고 데이터베이스 기능의 일부를 사용한다.

따라서 우리는 시스템을 수평적 계층으로 분할하면서 동시에 해당 계층을 가로지르는, 얇은 수직적인 유스 케이스로 시스템을 분할할 수 있다.

시스템에서 서로 다른 이유로 변경되는 요소들의 결합을 분리하면 기존 요소에 지장을 주지 않고도 새로운 유스 케이스를 계속해서 추가할 수 있게 된다.

개발 독립성

컴포넌트가 완전히 분리되면 팀 사이의 간섭은 줄어든다. 업무 규칙이 UI를 알지 못하면 UI에 중점을 둔 팀은 업무 규칙에 중점을 둔 팀에 그다지 영향을 줄 수 없다.

배포 독립성

유스 케이스와 계층의 결합이 분리되면 배포 측면에서도 고도의 유연성이 생긴다.

실제로 결합을 제대로 분리했다면 운영 중인 시스템에서도 계층과 유스 케이스를 교체할 수 있다.

중복

소프트웨어에서 중복은 일반적으로 나쁜 것이다. 하지만 중복에도 여러 종류가 있다. 그중 하나는 진짜 중복이고, 다른 중복은 거짓된 또는 우발적인 중복이다.

중복으로 보이는 두 코드 영역이 각자의 경로로 발전한다면, 즉 서로 다른 속도와 다른 이유로 변경된다면 이 두 코드는 진짜 중복이 아니다. 몇 년이 지나 다시 보면 두 코드가 매우 다르다는 사실을 알게 될 것이다.

자동 반사적으로 중복을 제거해버리는 잘못을 저지르는 유혹을 떨쳐내라. 중복이 진짜 중복인지 확인하자. 그렇지 않으면 나중에 코드를 다시 분리하느라 큰 수고를 감수해야 한다.

결합 분리 모드

계층과 유스 케이스의 결합을 분리하는 방법은 다양하다. (소스 수준, 배포 수준, 서비스 수준) 프로젝트 초기 단계에서는 어떤 모드가 최선인지 알기 어렵다. 프로젝트가 성숙해갈수록 최적인 모드가 달라질 수 있다.

좋은 아키텍처는 시스템이 모노리틱 구조로 태어나서 단일 파일로 배포되더라도, 이후에는 독립적으로 배포 가능한 단위들의 집합으로 성장하고, 또 독립적인 서비스나 마이크로 서비스 수준까지 성장할 수 있도록 만들어져야 한다.

또한 필요에 따라 진행 방향을 거꾸로 돌려 원래 형태인 모노리틱 구조로도 되돌릴 수 있어야 한다.

좋은 아키텍처는 이러한 변경으로부터 소스 코드 대부분을 보호한다. 좋은 아키텍처는 결합 분리 모드를 선택사항으로 남겨두어서 배포 규모에 따라 가장 적합한 모드를 선택해 사용할 수 있게 만들어준다.

결론

시스템 결합 분리 모드는 시간이 지나면서 바뀌기 쉬우며, 뛰어난 아키텍트라면 이러한 변경을 예측하여 큰 무리 없이 반영할 수 있도록 만들어야 한다.