6부 세부사항
30장 데이터베이스는 세부사항이다
- 애플리케이션 내부 데이터의 구조는 시스템 아키텍처에서 대단히 중요하다. 하지만 데이터 베이스는 데이터 모델이 아니다. 이러한 유틸리티는 저수준의 세부사항일 뿐이라서 아키텍처와는 관련이 없다.
관계형 데이터 베이스
- 애플리케이션의 유스케이스는 데이터를 테이블에 행단위로 배치한다는 사실 등 방식을 알아서는 안되며 관여해서도 안된다. 데이터가 테이브 구조를 가진다는 사실은 오직 아키텍처의 외부 원에 최하위 수준의 유틸리티만 알아야 한다
데이터베이스 시스템은 왜 이렇게 널리 사용되는가?
- 디스크는 계속 발전해왔지만 느리다는 특성이 치명적이었다
- 이런 단점을 극복하기 위해 색인, 캐시, 쿼리, 계획 최적화가 필요했고 이러한 시스템은 파일 시스템과 관계형 데이터베이스 관리 시스템(RDBMS)의 유형으로 분리되었다
- 파일 시스템은 문서 기반이다. 문서 전체를 편리하게 저장하는 방법을 제공하지만 내용을 기준으로 검색할 때는 크게 도움이 되지 않는다
- 데이터베이스 시스템은 내용 기반이다.
- 레코드가 서로 공유하는 일부 내용에 기반해서 다수의 레코드를 연관 짓는 데 매우 탁월하지만 문서를 저장하고 검색하는데는 부적합하다
- 이들 두 시스템은 데이터를 디스크에 체계화해서 효율적으로 데이터를 저장하고 검색할 수 있도록 한다.
디스크가 없다면 어떻게 될까?
- 디스크는 RAM으로 대체되고 있다.
- 많은 데이터들을 연결 리스트, 트리, 해시테이블, 스택, 큐 등의 데이터 구조로 체계화 할 것이고 포인터나 참조를 사용할 것이다.
- 지금도 사실 RAM으로 읽은 후에는 다루기 편리한 형태로 그 구조를 변경한다.
- 그 이유로 데이터베이스는 세부사항이라고 말한다.
하지만 성능은?
- 데이터 저장소의 측면에서 성능은 완전히 캡슐화하여 업무 규칙과는 분리할 수 있는 관심사다. 데이터 저장소에서 데이터를 빠르게 넣고 뺄 수 있어야 하는 것은 맞지만, 이는 저수준의 관심사다
결론
- 체계화된 데이터 구조와 데이터 모델은 아키텍처적으로 중요하다. 반면 기술과 시스템은 아키텍처적으로 중요하지 않다.
31장 웹은 세부사항이다
끝없이 반복하는 추
- IT 역사 전체로 시야를 넓히면 웹은 아무것도 바뀌지 않았다. 브라우저에 서버 팜, 브라우저에 애플릿 추가, 서버로 이동, Ajax, Node.js 등 수 많은 변화 중 하나에 불과하다.
- 아키텍트로서 우리는 멀리 내다봐야 한다. 이 진동은 그저 핵심 업무 규칙의 중심에서 밀어내고 싶은 단기적인 문제이다.
- 하나의 예로 업그레이드된 운영체제에서는 모든 애플리케이션의 룩앤필을 완전히 변경하게 되는데 이 경우에도 아키텍트가 UI와 업무 규칙을 서로 격리해야하기를 바랬다.
32장 프레임워크는 세부사항이다
혼인 관계의 비대칭성
- 당신과 프레임워크 제작자 사이의 관계는 놀라울 정도로 비대칭적이다.
- 프레임워크 제작자 입장에서는 프레임워크와 이러한 결합이 위험요소가 되지 않는다. 오히려 프레임워크와 결합되기를 바란다. 왜나하면 제작자는 그 프레임워크에 대해 절대적인 제어권을 쥐고있기 때문이다.
위험요인
- 업무 객체를 만들 때, 프레임워크 제작자는 자신의 코드를 상속할것을 요구한다. 당신만의 고유한 엔티티를 만들 때 말이다
- 프레임워크는 애플리케이션 초기 기능을 만드는데 도움이 되겠지만 프레임워크에서 제공하는 기능과 틀을 벗어나게 될것이다.
- 프레임워크는 도움이 되지 않는 신규 버전으로 업그레이드를 하느라 다른 일을 못할 수도 있다.
- 새롭고 더 나은 프레임워크가 등장해서 갈아타고 싶을 수 있다.
해결책
- 프레임워크와 결합해서는 안되고 바깥쪽 원에 속하는 세부사항으로 취급하라. 핵심코드 안으로 들어오지 못하게 하고 플러그인할 수 잇는 컴포넌트에 프레임워크를 통합하고 의존성 규칙을 준수하라
이제 선언합니다.
- 자바를 사용한다면 표준 라이브러리와 반드시 결혼해야 하는데 이건 정상이다. 그 프레임워크와 항상 함께 해야 한다는 사실을 명심해야 한다.
33장 사례연구: 비디오판매
제품
웹사이트에서 비디오를 판매하는 소프트웨어
유스케이스 분석
- 단일 책임 원칙에 따르면 이들 네 액터가 시스템이 변경되어야 할 네가지 주요 근원이 된다.
- 시스템을 분할하여 특정 액터를 위한 변경이 나머지 액터에게는 전혀 영향을 미치지 않게 만들고자 한다.
- 점선으로 된 유스케이스는 추상 유스케이스인데 범용적인 정책을 담고 있으며, 다른 유스케이스에서 이를 더 구체화 한다.
- 굳이 추상 유스케이스를 만든 이유는 두 유스케이스가 너무 비슷하기 때문에 분석 초기에 통합하는 방법을 찾는 편이 더 현명하다고 판단
컴포넌트 아키텍처
- 이중으로 된 선은 아키텍처 경계를 나타대고 뷰, 프레젠터, 인터랙터, 컨트롤러로 분리되어있다. 또한 대응하는 액터에 따라 카테고리를 분리했다는 사실도 확인할 수 있다.
- 시스템을 이러한 컴포넌트들로 모두 분할 해 컴파일과 빌드 환경을 나누어 독립적으로 전달할 수 있도록 할 수 있다.
- 이렇게 나눠두면 후에 시스템이 변경되는 양상에 맞춰 시스템 배포 방식을 조절할 수 있다.
의존성 관리
- 제어 흐름은 오른쪽에서 원쪽으로 이동하지만 모든 화살표가 그렇지는 않다. 항상 더 높은 수준의 정책을 포함하는 컴포넌트를 향한다
- 또한 열린 화살표는 제어흐름과 같은 방향을 가리키며 닫힌 화살표는 제어흐름과 반대방향을 가리킴을 의미한다.
결론
- 위 다이어그램은 단일 책임원칙에 기반한 액터의 분리, 의존성 규칙을 포함하고있다.
- 이런 방식으로 코드를 한번 구조화하고 나면 시스템을 실제로 배포하느느 방식은 다양하게 선택할 수 있게 된다.
34장 빠져 있는 장
- 악마는 항상 디테일(구현 세부사항)에 있는 법이며 이점을 심사숙고하지 않는다면 마지막 고비에 걸려 넘어지기 십상일 것이다.
계층 기반 패키지
- 전통적인 수평 계층형 아키텍처다. 기술적인 관점에서 해당 코드가 하는 일에 기반해 그 코드를 분할한다. 이 방식을 '계층 기반 패키지라고 부른다'
- OrderController: 웹 컨트롤러이며 웹 기반 요청을 처리한다
- OrdersService: 주문관련 업무규칙을 정의하는 인터페이스
- OrdersRepository: 영구저장된 주문정보에 접근하는 방법을 정의하는 인터페이스
- 마틴 파울러는 프레젠터, 도메인, 데이터 계층화에서는 처음 시작하기에 계층형 아키텍처가 적합하다고 얘기하지만 소프트웨어가 커지고 복잡해지면 머지않아 큰 그를 세개만으로 모든 코드를 담기에 부족하다고 한다.
기능 기반 패키지
- 이는 서로 연관된 기능, 도메인 개념, 또는 Aggregate Root에 기반하여 수직의 얇은 조각들로 코드를 나누는 방식이다.
- 이전과 다르게 세개의 패키지로 나누는게 아닌, 단 하나의 패키지에 속하게 만드는 것
- 장점
- 코드의 상위 수준 구조가 업무 도메인에 대해 무언가를 알려주게 된다.
- 또한 주문 조회하기 유스케이스가 변경될 경우 변경해야할 코드를 모두 찾는 작업이 더 쉬워질 수 있다. 한 페이지에 담겨있기 때문
- 책에서는 더 나은 방법을 다음에 권장하고 있다.
포트와 어댑터
- 엉클 밥에 따르면 포트와 어댑터, 혹은 육각형 아키텍처, 경계-컨트롤러-엔티티 등의 방식으로 접근하는 이유는 업무/도메인에 초점을 둔 코드가 프레임워크나 데이터베이스 같은 기술적인 세부 구현과 독립적이며 분리된 아키텍처를 만들기 위해서다
- 위의 방식으로 구현한 예시인데 의존성이 내부를 향해 흐로고 있고, OrdersRepository가 Orders라는 간단한 이름으로 바뀌었는데 이는 도메인 주도 설계라는 세계관에서 비롯된 명명법으로 내부에 존재하는 모든것의 이름은 유비쿼터스 도메인 언어(주문 리포지터리가 아니라 주문에 대해 말하라는 것) 관점에서 기술하라고 한다
컴포넌트 기반 패키지
- 엄격한 계층형 아키텍처에서는 의존성 화살표는 항상 아래를 향해야 하며, 각 계층은 반드시 바로 아래 계층에만 의존해야 한다. 여기서 속임수를 써서 몇몇 의존성을 의도치 않은 방식으로 추가하더라도 보기 좋은 비순환 의존성 그래프가 생성된다.
![스크린샷 2022-10-02 오후 3.39.15](/Users/okstring/Library/Application Support/typora-user-images/스크린샷 2022-10-02 오후 3.39.15.png)
- 여기서는 업무로직과 영속성 관련 코드를 하나로 묶는데 이 묶음을 컴포넌트라고 부른다.
엉클밥은 이 책 앞부분에서 컴포넌트에 대한 정의를 아래와 같이 제시했는데
컴포넌트는 배포 단위다. 컴포넌트는 시스템의 구성요소로, 배포할 수 있는 가장 작은 단위다. 자바의 경우 jar의 경우가 컴포넌트다
이 장에서는
컴포넌트는 멋지고 깔끔한 인터페이스로 감싸진 연관된 기능들의 묶음으로 애플리케이션과 같은 실행 환경 내부에 존재한다
라고 말한다.
이 방법론에서는 소프트웨어 시스템은 하나 이상의 컨테이너(웹 애플리케이션, 모바일 애플리케이션 등)로 구성되며 각 컨테이너는 하나 이상의 컴포넌트를 포함한다. 또한 각 컴포넌트는 하나 이상의 클래스로 구현된다.
- 주된 이점은 주문과 관련된 무언가를 코딩해야 할 때 오직 한 곳, 즉 OrdersComponnet만 둘러보면 된다는 점이다.
조직화 vs 캡슐화
- 만약 자바 애플리케이션에서 모든 타입을 public으로 지정하면 패키지는 단순히 폴더처럼 묶는 것밖에 안된다. 즉 캡슐화나 은닉을 하는데 아무런 도움이 되지 않는다.
- 접근지시자를 적절하게 사용하면 타입을 패키지로 배치하는 방식에 따라서 각 타입에 접근할 수 있는 정도가 실제로 크게 달라질 수 있다.
- 계층기반 패키지에서 Service와 Repository인터페이스는 외부 패키지의 클래스로부터 자신이 속한 패키지 내부로 들어오는 의존성이 존재하므로 public으로 선언되어야 한다
- 기능기반 패키지에서 OrdersController가 패키지로 들어올 수 있는 유일한 통로를 제공하므로 public
- 포트와 어댑터에서 Service와 Orders 인터페이스는 외부로부터 들어오는 의존성을 가지므로 public으로 지정해야 한다.
- 컴포넌트 기반 패키지에서는 Controller, component의존성을 가지므로 public
다른 결합 분리 모드
- 이 접근법은 잠재적으로 절충해야 할 부분이 있는데 인프라코드를 단일 소스코드에 모아둔다는 말은 애플리케이션에서 특정 영역에 있는 인프라코드가 애플리케이션의 다른 영역에 있는 코드를 직접 호출할 수 있다는 뜻이다. 도메인을 통하지 않고 말이다.
결론: 빠져있는 조언
- 설계를 어떻게 해야만 원하는 코드 구조로 매필할 수 있을 지, 그 코드를 어떻게 조직화할지, 런타임과 컴파일 타임에 어떤결합 분리 모드를 적용할 지 고민해야한다.
- 가능하다면 선택사항을 열어두되 실용주의적으로 행하고 팀의 규모, 기술수준, 해결책의 복잡성을 일정과 예산이라는 제약과 동시에 고려해라
클린 아키텍처 소프트웨어 구조와 설계의 원칙
로버트 C. 마틴 저
http://www.yes24.com/Product/Goods/77283734