‘클린 아키텍처’ 기술 서적에 대해 학습했던 내용을 정리하기 위한 목적의 TIL 포스팅입니다.🙆♂️
- 소프트웨어 아키텍처는 선을 긋는 기술이다.
- 이러한 선은 경계라 부르며 소프트웨어 요소를 서로 분리하고, 경계 한 편에 잇는 요소가 반대편에 있는 요소를 알지 못하도록 막는다.
- 아키텍트의 목표는 필요한 시스템을 만들고 유지하는데 드는 인적 자원을 최소화하는 것이다.
- 인적 자원의 효율을 떨어뜨리는 요인은
결합
이다. 특히 너무 일찍 내려진 결정에 따른 결합이다.
- 인적 자원의 효율을 떨어뜨리는 요인은
- 어떤게 이른 결정일까?
- 시틈에의 업무 요구사항, 즉 유스케이스와 아무 관련 없는 결정이다.
- 프레임워크, 데이터베이스, 웹 서버, 유틸리티 라이브러리, 의존성 주입에 대한 결정 등이 여기 포함된다.
- 좋은 시스템 아키텍처란 이러한 결정이 부수적이며, 결정을 연기할 수 있는 아키텍처다.
- 이러한 결정을 가능한 최후의 순간에 내릴 수 있게 해주며, 결정에 따른 영향이 크지 않게 만든다.
두 가지 슬픈 이야기
- 1990년대 모 회사에서 서버 팜(서버 클러스터)이 춤추는 이상을 그리며 아키텍처를 설계하고 개발했지만, 실제 이 서버팜은 존재한적이 없게 되었다. 서버 팜을 필요로 하는 시스템을 판매하지 못한 것이다. => 과설계
- 이 비극은 아키텍트가 너무 이르게 결정을 내림으로써 개발 비용을 엄청나게 가중시킨 사례다.
- 그리고 또 다른 회사는 첨부터 SOA 로 개발하고자하여 새로운 기능을 추가하려면 서비스들 사이의 결합으로 엄청난 양의 WSDL 을 변경해야 하며, 변경에 영향받는 모든 것을 재배포 해야 했다. 이는 엄청 고통이었다.
- 이 회사의 실수는 SOA 를 약속하는 일련의 도구들을 너무 일찍 채택하여 적용했다는 것이다.
- 즉, 거대한 일련의 도메인 객체 서비스를 너무 이르게 채택한 것이다.
- 이러한 실수로 인적 시간, 그것도 엄청난 양의 인적 시간에 따른 비용이 SOA 의 소용돌이에 휩쓸려 떠내려간 것이다.
FitNesse
- 저자는 2001년에 Fitnesse 를 만들기 시작했는데, 그 과정에서 두 가지를 잘한일로 생각했다.
1) 웹 서버를 직접 작성하기로 한 일
- 기본 뼈대만 갖춘 웹 서버는 단순한 단일 소프트웨어이기에 구현이 간단할 뿐 아니라, 어떤 웹 프레임워크를 사용할지에 대한 결정을 훨씬 나중으로 연기할 수 있도록 해주었기 떄문이다.
2) 데이터베이스에 대해 고민하지 않은 것
- 어떤 DB를 사용하더라도 상관없는 형태로 설계함으로써, 의도적으로 DB에 대한 결정을 미루었다.
- 모든 데이터 접근 영역과 데이터 저장소 영역 사이에 인터페이스를 추가하는 간단한 설계 방식을 사용했다.
- 실제 영속성을 구현할 시점이 되자 MySQL을 다시 한 번 고민했지만 단기적으론 필요치 않다는 결정을 내렸다.
- 왜냐하면 해시 테이블을 플랫 파일에 저장하도록 구현하는 일은 정말 쉬운일이었기 떄문이다.
- 석달 후 플랫 파일을 사용한 해결책이 적합했단 결론을 내렸다.
- MySQL 과 관련된 아이디어는 완전히 폐기하기로 했다.
- MySQL을 선택사항으로 추가하는 것도 쉽게 가능했다.
- 하지만 아무도 사용하지 않았다…
- FitNesse 개발 초기에 우리는 업무 규칙과 데이터베이스 사이에 경계선을 그었다.
- 경계선을 통해 업무 규칙은 데이터 접근 메서드 외에는 데이터 베이스에 관한 어떤 것도 알지 못하게 되었다.
- 파일 시스템을 선택하여 시도해볼 수도 있었고, 더 나은 해결책이 보이면 방향을 바꿀 수도 있었다.
- 더군다나 누군가 애초의 방향(MySQL)으로 가기를 원할 때 방해가 되거나 지연이 되지도 않았다.
- 또한 DB가 없다는 사실은 스키마 관련 문제, 쿼리 문제, DB 서버 문제 등등 여타 모든 고약한 문제가 없게 되었다.
- 또한 테스트를 느리게 만드는 DB가 없으니 테스트를 더 빨리 돌릴 수 있었다.
- 간단히 말해서 경계선을 긋는 행위는 결정을 늦추고 연기하는데 도움이 되었고, 궁극적으로 시간을 엄청나게 절약해주었으며, 골치를 썩지 않게 해주었다.
어떻게 선을 그을까? 그리고 언제 그을까?
- 관련이 있는 것과 없는 것 사이에 선을 긋는다.
- GUI 는 업무 규칙과는 관련 없기 떄문에, 이 둘 사이에는 반드시 선이 있어야 한다.
- 데이터 베이스는 GUI와는 관련 없으므로, 이 둘 사이에도 반드시 선이 있어야 한다.
- 데이터베이스는 업무 규칙과 관련 없으므로, 이 둘 사이에는 반드시 선이 있어야 한다.
- 데이터베이스는 업무 규칙이 간접적으로 사용할 수 있는 도구다.
- 업무 규칙은 데이터베이스와 관련된 세부사항에 대해 어떤 것도 알아선 안된다.
- 업무 규칙이 알아야 할 것은 데이터를 가져오고 저장할 때 사용할 수 있는 함수 집합이 있다는 사실이 전부다.
- 이러한 함수 집합을 통해 우리는 데이터베이스를 인터페이스 뒤로 숨길 수 있다.
- 아래 이미지에서 이 점을 분명하게 볼 수 있다.
- BusinessRules 는 DatabaseInterface 를 사용하여 데이터를 로드하고 저장한다.
- DatabaseAccess 는 DatabaseInterface 를 구현하며, Database 를 실제로 조작하는 일을 맡는다.
- 경계선은 상속 관계를 횡단하며 DatabaseInterface 바로 아래에 그어진다.
- DatabaseAccess 에서 출발하는 두 화살표는 DatabaseAccess 클래스로부터 바깥쪽으로 향한다.
- 즉, 이 도표에서 DatabaseAccess 가 존재한다는 사실을 알고 있는 클래스는 없다는 뜻이다.
- 이제 조금 물러나서 보자.
- 위 이미지의 화살표 방향에 주목하자.
- Database 는 BuisinessRules 에 대해 알고 있다.
- BusinessRules 는 Database 에 관해 알지 못한다.
- 이는 DatabaseInterface 클래스는 BusinessRules 컴포넌트에 속하며, DatabaseAccess 클래스는 Database 컴포넌트에 속한다는 사실을 의미한다.
- 이 선의 방향이 중요하다. BusinessRules 에 있어 Database 는 문제가 되지 않지만, Database는 BusinessRules 없이는 존재하라 수 없다는 사실을 이 방향을 통해 알 수 있다.
- 두 컴포넌트 사이에 이러한 경계선을 그리고 화살표의 방향이 BusinessRules 를 향하도록 만들었으므로, BusinessRules 에선 어떤 종류의 데이터베이스도 사용할 수 있음을 알 수 있다.
- Database 컴포넌트는 다양한 구현체로 교체될 수 있으며, BusinessRules 는 조금도 개의치 않는다.
- 데이터베이스를 결정하기에 앞서 업무 규칙을 먼저 작성하고 테스트하는데 집중할 수 있음을 의미한다.
입력과 출력은?
- 입력과 출력은 중요치 않다. 중요한건 업무 규칙이다.
- 이번에도 마찬가지로 GUI와 BusinessRules 컴포넌트가 경계선에 의해 분할된다는 사실을 볼 수 있다.
- 관련성이 낮은 컴포넌트(GUI)가 관련성이 높은 컴포넌트(BusinessRules)에 의존한다는 사실을 다시 한 번 살펴볼 수 있다.
- GUI는 다른 종류의 인터페이스로 얼마든지 교체 가능하며, BusinessRules 는 전혀 개의치 않는다는 사실을 알 수 있다.
플러그인 아키텍처
- 선택적이거나 또는 수많은 다양한 형태로 구현될 수 있는 나머지 컴포넌트로부터 핵심적인 업무 규칙은 분리되어 있고, 또한 독립적이다.
- 위 설계에서 UI는 플러그인 형태로 고려되었기에, 수많은 종류의 UI를 플러그인 형태로 연결할 수 있게 된다.
- 웹 기반일 수 있고, 클라이언트/서버 기반이거나, SOA, 콘솔 기반, 또는 임의의 어떤 UI도 가능하다..
- 이는 데이터베이스에도 동일하게 적용가능한데 데이터베이스를 플러그인으로 다루기로 결정했기 떄문에, 임의의 다양한 RDB, NoSQL DB, 파일 시스템 기반 DB 등 다양하게 바꿀 수 있는 것이다.
- 업무 규칙과 새로운 UI 간 통신 일부는 재작업해야 할 가능성이 높더라도 플러그인 구조를 가정한채 시작함으로써, 최소한 우리는 이러한 변경 작업을 현실성 있도록 만들었다.
플러그인에 대한 논의
- 시스템을 플러그인 아키텍처로 배치함으로써 변경이 전파될 수 있는 방화벽을 생성 가능하다.
- GUI 가 업무 규칙에 플러그인 형태로 연결되면 GUI에서 발생한 변경은 절대로 업무 규칙에 영향을 미칠순 없다.
- 경계는 변경의 축이 있는 지점에 그어진다.
- 경계의 한쪽에 위치한 컴포넌트는 다른 경계 반대편 컴포넌트와는 다른 속도 및 다른 이유로 변경된다.
- GUI는 업무 규칙과는 다른 시점에 다른 속도로 변경되므로, 이 둘 사이에는 반드시 경계가 필요하다.
- 업무 규칙은 의존성 프레임워크와는 다른 시점에 그리고 다른 이유로 변경되므로, 둘 사이에도 반드시 경계가 필요하다.
- 이 역시도 순전히 단일 책임 원칙에 해당한다. 이는 어디에 경계를 그어야 할지를 알려준다.
결론
- 경계선을 그리려면 먼저 시스템을 컴포넌트 단위로 분할해야 한다.
- 일부 컴포넌트는 핵심 업무 규칙에 해당하며 나머지 컴포넌트는 플러그인으로, 핵심 업무 규칙과는 직접적인 관련이 없지만 필수 기능을 포함한다.
- 그런 다음 컴포넌트 사이의 화살표가 특정 방향, 즉 핵심 업무를 향하도록 이들 컴포넌트의 소스를 배치한다.
- 이는 의존성 역전 원칙과 안정된 추상화 원칙을 응용한 것임을 눈치챌 수 있어야 한다.
- 의존성 화살표는 저수준 세부사항에서 고수준의 추상화를 향하도록 배치된다.