하나하나 살펴보도록 하자.
아키텍처의 최우선 관심사는 유스케이스이며, 말그대로 시스템 아키텍처가 시스템의 유스케이스(의도)를 지원해야 한다. 시스템 아키텍처는 앞서 말하듯 행위보다는 구조에 집중하며 행위에는 영향을 거의 주지 않는데, 이 때 아키텍처는 행위 자체를 명확히 해서 외부를 드러내는 부분을 맡는다. 이렇게 해서 아키텍처를 잘 구성한다면 아키텍처만 보고도 시스템이 지닌 의도(유스케이스)를 파악할 수 있게 된다.
시스템 운영 지원 관점에서 아키텍처는 실질적인 역할을 맡는다. 만약 시스템이 초당 1만개의 고객을 처리해야 한다면, 아키텍처는 관련 유스케이스에 걸맞는 처리량과 응답시간을 보장해야한다. 반드시 운영 작업을 허용할 수 있는 형태로 아키텍처를 구성해야하며, 이는 시스템의 처리 요소들을 작은 서비스들로 배열해 많은 서버에서 병렬로 실행할 수 있게 만들어야함을 의미하고 운영관점에 따라 구성을 달리 해줘야한다. 만약 시스템이 단일체(monolitic) 구조를 갖는다면, 다중 프로세스, 다중 스레드, MSA 형태가 필요할 때 개선하기가 어렵다. 반면 컴포넌트를 적절히 격리해 유지하고 서로의 통신 방식을 제한하지 않는다면, 시간이 지나 요구사항이 바뀌더라도 기술을 전환하는 일이 쉬워질 것이다.
아키텍처는 개발환경을 지원하는데 있어 핵심적인 역할을 수행하며 이 때 콘웨이 법칙이 적용되어야 한다. (콘웨이법칙: 시스템을 설계하는 조직이라면 어디든지 그 조직의 의사소통 구조와 동일한 구조의 설계를 만들어 낼 것이다.) 즉 많은팀과 관심사가 다양한 조직이라면 각 팀이 독립적으로 행동하기 편하게끔 아키텍처를 설계(컴포넌트 단위로 분할)해 서로간의 방해가 없도록 해야한다.
좋은 아키텍처가 목표로 해야하는 것은 '즉각적인 배포'이며, 시스템이 빌드된 후 즉각 배포할 수 있도록 지원해야 한다. 이러한것이 가능하려면 시스템을 컴포넌트 단위로 적절하게 분리하고 격리해야한다.
좋은 아키텍처와 컴포넌트는 여러 관심사들 사이에서 균형을 맞추는 역할을 하며, 현실에서 적용할 때 우리의 목표는 시시각각 변하기 마련이다. 따라서 이러한 변화 속에서 균형을 잡기 위해 선택사항을 가능한한 많이, 오래 열어둔다면, 향후 시스템에 적합한 것에 맞도록 쉽게 변경이 가능해진다.(ex. DB를 관계형, 분산형, 계층형 어떤것으로 선택할 것인지 등..)
아키텍처는 SRP와 OCP를 적용하고 의도와 맥락에 따라서 서로 다른 이유로 변경되는 것들은 분리하고, 동일한 이유로 변경되는 것들을 묶어야한다. 적용 예시로는 UI와 업무 규칙을 분리해 독립적으로 변경할 수 있게 해야 하고, 업무 규칙으로는 입력 필드 유효성 검사, 이자계산, 재고품 집계에 따라 어플리케이션에 밀접한지, 도메인에 밀접한지 구분을 지을 수 있다. 또한 데이터베이스, 쿼리, 스키마 또한 기술적 세부사항으로 업무규칙, UI와 관련이 없게 분리하여 독립적으로 변경할수 있게 해야한다. 이렇게 시스템을 결합되지 않는 수평적인 계층으로 분리하고다뤄 SRP와 OCP를 준수할 수 있다.
서로 다른 이유로 변경 되는것에는 유스케이스도 포함되고, 각 유스케이스마다도 사용하는 UI, 업무규칙, 데이터베이스 기능이 다르다. 쉽게 말하면 앞서말한 수평적인 계층을 잘게 나눠 분리하는 방법이라고 생각할 수 있다. 예시로 장바구니 시스템에서 장바구니에 추가/삭제 부분은 서 다른 속도와 이유로 변경될 것이고 분할하는것이 자연스럽고 당연하다. 이렇게 시스템이 서로 다른 이유로 변경되는 요소들을 분리하면 기존 요소에 지장을 주지 않고 새로운 유스케이스를 추가할 수 있다. (각 유스케이스를 뒷받침하는 UI와 데이터베이스를 묶기)다음은 그 예시이다.
유스케이스를 위해 수행하는 작업들을 분리하면 운영에도 도움이 된다. 그 예시로 높은처리량/낮은처리량을 요구하는 유스케이스를 분리해 운영에 따라 쉽게 컴포넌트의 변경이 가능하다. 또한 분리된 컴포넌트를 서로 다른 서버에서 실행해야하는 상황이면, 서비스(또는 MS) 수준으로 분리해야한다. 결론은 선택권을 열어두면 좋다는 이야기이다.
컴포넌트가 완전히 분리되면 팀 사이의 간섭은 줄어든다. 기능 팀, 컴포넌트 팀, 계층 팀 등의 여러 형태의 팀들이 서로에 대해 알지 못하도록 개발을 할 수 있도록 시스템 아키텍처가 구조를 뒷받침 해주어야한다.
유스케이스와 계층 결합이 분리되면 배포에서도 유연성이 생겨 운영중에 계층과 유스케이스를 교체할 수 있다. 이렇게 하면 유스케이스를 새로 추가할 때 기존 시스템은 놔두고 서비스 몇개를 추가하는 방식으로 단순해진다.
아키텍트는 종종 '중복'이라는 함정에 빠진다, 이 때 소프트웨어에서 중복은 일반적으로 나쁜 의미를 쓰이며 중복은 크게 두가지 종류로 나뉜다.
보통은 통합을 고민할 때 서로 다른 방향으로 분기할 가능성이 높으며 우발적인 중복일 가능성이 높다. 따라서 정말로 진짜 중복이 맞는지 확인하는 습관이 필요하다.
계층과 유스케이스의 결합을 분리하는 방법은 다양하다. 소스 코드 수준의 분리와 바이너리 코드(배포) 수준의 분리, 실행 단위(서비스) 수준에서의 분리가 있다. 각 특징은 이러하다.
처음부터 어떤 모드를 선택하기란 어렵고 프로젝트를 진행하면서 달라질 수 있다. 현 시점에서 가장 인기 있는 정책은 처음부터 서비스 수준으로 분리하는 것이다. 하지만 이는 시스템 자원, 개발 비용이 많이든다는 단점이 있다. 저자는 컴포넌트가 서비스화 될 가능성이 있는 것은 서비스화 되기 직전까지 개발해 선택권을 열어두는 방식을 선호한다고 한다. 좋은 아키텍처는 초반 시스템이 모놀리틱이어도 이후 배포, 서비스 수준으로 분리하며 성장할 수 있도록 도와주는 역할을 가져야한다. 이렇게 결합 분리 모드를 선택사항으로 남겨두어 가장 적합한 모드를 사용할 수 있도록 해야 한다.