IT/책

[Clean Architecture] 5부, 아키텍처(1)

Terriermon 2021. 12. 12. 00:36

Clean Architecture

5부, 아키텍처

 

15장, 아키텍처란

- 아키텍처는 코드와 동떨어져서는 안됨

 

- 시스템을 구축했던 사람들이 만들어낸 시스템의 형태

> 소프트웨어 시스템이 쉽게 개발, 배포, 운영, 유지보수가 되어야 함

> 선택지를 가능한 많이, 오래 남겨야 한다.

 

- 시스템 아키텍처는 시스템의 동작 여부와 관련이 없음

why? 형편없는 아키텍처들도 잘 동작하기 때문

 

따라서, 아키텍처는 시스템의 수명과 관련된 비용을 최소화하고 프로그래머의 생산성을 최대화 해야한다.

 

 

개발

시스템 아키텍처는 개발팀이 시스템을 쉽게 개발할 수 있도록 뒷받침해야한다.

 

 

배포

배포 비용이 높을수록 시스템의 유용성은 떨어짐

> 시스템을 단 한번에 쉽게 배포할 수 있도록 만들어야 함

 

마이크로 서비스 아키텍처

- 컴포넌트 경계가 뚜렷해지고 인터페이스가 안정화 되어, 시스템을 매우 쉽게 개발 할 수 있음

 

- 단, 배포 시기가 되면 위협적일 만큼 늘어난 마이크로서비스를 발견할 수 있음

 

 

운영

- 운영은 개발, 배포, 유지보수에 미치는 영향보다 덜 극적임

 

- 시스템 아키텍처가 개발자에게 시스템의 운영방식을 잘 드러내줌

 

- 유스케이스, 기능, 시스템의 필수 행위를 일급 엔티티로 격상시키고 개발자에게 목표가 되도록 인식 필요

 

 

유지보수

- 소프트웨어 시스템에서 비용이 가장 많이 듦

> 탐사(spelunking)와 이로 인한 위험부담

탐사
기존 소프트웨어에 새로운 기능을 추가하거나 결함을 수정할 때, 소프트웨어를 파헤쳐서 어디를 고쳐치는 것이 최선인지, 어떤 전략을 쓰는게 최적인지 결정하는 데 드는 비용

 

 

선택사항 열어 두기

소프트웨어어는 행위적 가치와 구조적 가치를 지님

> 구조적가치: 소프트웨어를 부드럽게 만듦

 

소프트웨어 유연성은 선택사항을 가능한 많이, 오랫동안 열어두어야 함

 

 

정책(policy)과 세부사항(detail)

 

정책: 모든 업무 규칙과 업무 절차를 구체화

 

세부사항: 사람, 외부 시스템, 프로그래머가 정책과 소통할 때 필요한 요소, 정책에는 영향을 미치지 않음

ex) 입출력장치, 데이터베이스, 웹 시스템, 서버, 프레임워크, 통신 프로토콜 등

 

> 세부사항에 몰두하지 않은 채, 고수준의 정책을 만들면 더 많은 정보를 얻고 제대로 된 결정을 내릴 수 있음

> 선택사항에 대해서 오랫동안 열어두어야 함

 

 

장치 독립성

- 장치 종속성이 ㄴ경우, 다른 장치에 연결 시 작동하지 않을 수 있음 > 개방 폐쇄의 원칙

 

 

결론

좋은 아키텍트

- 세부사항을 정책으로부터 신중하게 가려내고, 정책이 세부사항과 결합되지 않도록 엄격하게 분리함

- 세부사항에 대한 결정을 가능한 오랫동안 미룰 수 있어야 함

 

 

 

 

16장, 독립성

좋은 아키텍처: 시스템의 유스케이스, 운영, 개발, 배포를 지원해야 함

 

 

유스케이스

시스템 아키텍처는 시스템의 의도를 지원해야 한다.

> 아키텍처의 최우선 관심사는 유스케이스임

 

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

 

 

운영

운영 관점에서 아키텍처는 더 실질적이며 덜 피상적인 역할을 맡음

ex) 시스템이 초당 100,000명의 고객을 처리해야 한다면, 아키텍처는 요구와 관련된 각 유스케이스에 맞는 처리량과 응답시간을 보장

 

- 시스템이 단일체로 작성되어 모노리틱 구조를 갖게 되면, 이후 다중 프로세스 등 형태 개선에 어려워짐

 

 

개발

- 콘웨이의 법칙: 시스템을 설계하는 조직이라면 어디든지 그 조직의 의사소통 구조와 동일한 구조의 설계를 만들어내야 함

 

- 각 팀이 독립적으로 행동하기 편한 아키텍처를 반드시 확보하여 개발하는 동안 팀들이 서로 방해를 하지 않도록 해야 함

 

- 컴포넌트 단위로 팀을 나눔

 

 

배포

즉각적인 배포: 좋은 아키텍처는 시스템이 빌드된 후 즉각 배포할 수 있도록 지원해야 함

> 컴포넌트 단위로 적절하게 분리하고 격리

> 선택사항을 열어둠

 

 

계층 결합 분리

아키텍트는 시스템의 나머지 부분으로부터 분리하여 독립적으로 변경할 수 있어야 함 -> 수평적인 계층 분리

ex) UI, 애플리케이션 특화 업무 규칙, 독립적 업무 규칙, 데이터베이스 등

 

 

유스케이스 결합 분리

서로 다른 이유로 변경되는 요소들의 결합을 분리하면, 기존 요소에 지장을 주지 않는 새 유스케이스 추가 가능

 

- 개발 독립성, 배포 독립성 등 유스케이스와 계층 결합이 분리되면 뒷받침 될 수 있음

 

 

중복

- 중복으로 보이는 두 코드가 각자 경로로 발전하면, 코드는 진짜 중복이 아님

- 유사해 보일 지라도, 나중에 다른 경로로 뻗어나갈 수 있으므로 합치지 않아도 됨

 

 

결합 분리 모드

소스 수준 분리 모드

- 모든 컴포넌트가 같은 주소 공간에서 실행

- 통신 시 같은 간단한 함수 호출

- 모노리틱 구조

 

배포 수준 분리 모드

- jar, DLL, 공유 라이브러리와 같이 배포 가능한 단위 사이에서 의존성 제어

- 모듈의 소스코드가 변하더라도 모듈을 재빌드하거나 재배포하지 않아도 됨

 

서비스 수준 분리 모드

- 의존하는 수준을 데이터 구조 단위로 낮추고, 네트워크 패킷을 통해 통신

- 실행 가능한 단위논 소스와 바이너리 변경에 대해 서로 독립적

ex) 마이크로 서비스

 

 

 

17장, 경계: 선긋기

소프트웨어 아키텍처는 선(경계)를 긋는 기술이다.

- 결정을 늦춰 어떤 곳에서도 사용 가능한 소프트웨어를 만들도록 함

- 입력과 출력은 크게 상관 없음

GUI, DB, Business Rules 모두 분리

 

 

18장, 경계 해부학

시스템 아키텍처는 소프트웨어 컴포넌트와 컴포넌트들을 분리하는 경계에 의해 정의됨

 

런타임 경계 횡단

- 경계 한쪽에 있는 기능에서 반대편 기능을 호출하여 데이터를 전달

- 소스코드 의존성 관리: 변경 시, 다른 소스코드 컴파일 등을 막기 위해

 

두려운 단일체

물리적으로 엄격하게 구분되지 않은 형태: 소스 수준 분리 모드

 

저수준 클라이언트 -> 고수준 서비스

- 왼쪽에서 오른쪽으로 경계를 횡단

- Client는 Service의 함수 f()를 호출, Client는 Data 인스턴스를 전달

 

 

제어의 역행, 고수준 Client -> 저수준 Service

- 경계를 횡단 할 때, 의존성은 모두 오른쪽에서 왼쪽으로 고수준의 컴포넌트를 향함

- 데이터 구조 정의가 호출 쪽에 위치함

 

배포형 컴포넌트

- 동적 링크 라이브러리: 아키텍처 경계가 물리적으로 드러남

ex) .NET DLL, jar 등 라이브러리

 

- 배포 수준의 컴포넌트는 단일 체와 동일

> 모든 함수가 동일한 프로세서와 주소 공간에 위치

> 컴포넌트를 분리하거나 컴포넌트 간 의존성을 관리하는 전략도 단일체와 동일함

 

- 단일체와 배포형 컴포넌트는 모두 스레드를 활용할 수 있음

> 스레드는 아키텍처 경계도 배포 단위도 아님, 실행 계획과 순서를 체계화하는 방법.

 

 

로컬 프로세스

물리적 형태를 띠는 아키텍처 경계, 일종의 최상위 컴포넌트

 

- 명령행이나 시스템 호출을 통해 생성

 

- 동일한 프로세서 또는 하나의 멀티 코어 시스템에 속한 여러 프로세서들에서 실행

> 각각 독립된 주소 공간에서 실행

> 메모리를 공유하지 못함(공유 메모리 파티션 제외)

 

- 소켓, 메일박스, 메세지 큐 등을 이용해 서로 통신

 

 

서비스

가장 강력한 경계의 물리적 형태

 

- 자신의 물리적 위치에 구애받지 않음

 

- 모든 통신이 네트워크를 통해 이뤄짐

> 통신은 함수 호출에 비해 느림

 

 

 

19장, 정책과 수준

소프트웨어 시스템이란 정책을 기술한 것

 

- 동일한 이유로 동일한 시점에 변경되는 정책은 동일한 수준에 위치해야 하며, 동일한 컴포넌트에 속해야 함

 

- 아키텍처 개발은 재편성된 컴포넌트들을 비순환 방향 그래프로 구성하는 기술을 포함

> 정점: 동일한 수준의 정책을 포함하는 컴포넌트

> 간선: 컴포넌트 사이의 의존성(소스코드, 컴파일타임의 의존성) ex. import, using 등

 

- 좋은 아키텍처는 의존성 방향이 컴포넌트 수준을 기반으로 연결되어야 함

> 저수준의 컴포넌트가 고수준의 컴포넌트에 의존하도록 설계

 

 

수준

입력과 출력까지의 거리, 시스템과 입출력으로부터 멀리 위치할수록 정책의 수준이 높아짐

 

 

결론

정책에 대한 논의는 단일 책임 원칙, 개방 폐쇄 원칙, 공통 폐쇄 원칙, 의존성 역전 원칙, 안정된 의존성 원칙, 안정된 추상화 원칙을 모두 포함함

 

 

 

20장, 업무 규칙

업무 규칙은 사업적으로 수익을 얻거나 비용을 줄일 수 있어야 함

 

- 핵심 업무 규칙: 사업 자체에 핵심적이며 규칙을 자동화하는 시스템이 없어도 업무 규칙이 그대로 존재함

- 핵심 업무 데이터: 핵심 업무 규칙에 필요한 데이터

- 엔티티: 핵심 업무 규칙 + 핵심 업무 데이터

 

 

엔티티

컴퓨터 시스템 내부의 객체, 핵심 업무 데이터를 기반으로 동작하는 일련의 조그만 핵심 업무 규칙을 구체화

> 핵심 업무 데이터를 직접 포함 또는 직접 접근

 

 

유스케이스

- 시스템이 사용자에게 어떻게 보이는지를 설명하지 않음

 

- 애플리케이션에 특화된 규칙 설명, 사용자와 엔티티에 대한 참조 데이터 등의 데이터 요소 포함

 

- 엔티티는 자신을 제어하는 유스케이스에 대하 아무것도 알지 못함

> 유스케이스는 엔티티에 의존하지만, 엔티티는 유스케이스에 의존하지 않음

 

 

요청 및 응답 모델

유스케이스는 입력 데이터를 받아 출력 데이터를 생성

 

- 엔티티와 요청/응답 모델은 상당히 많은 데이터를 공유하지ㅏ만, 의존성을 제거해야 함

 

- 유스케이스 클래스 코드가 HTML이나 SQL에 대해 알게 되면 안됨

 

 

결론

업무 규칙: 소프트웨어 시스템이 존재하는 이유, 핵심적인 기능

 

- 인터페이스나 데이터베이스 같은 저수준의 관심사로 인해 오염되면 안됨

 

- 가장 독립적이며 가장 재사용할 수 잇어야 함

 

 

 

21장, 소리치는 아키텍처

유스케이스 주도 접근법

 

- 소프트웨어 아키텍처는 시스템의 유스케이스를 지원하는 구조

 

- 아키텍처는 프레임워크에 대한 것이 아님

 

 

아키텍처의 목적

프레임워크나 도구, 환경에 구애받지 않고 유스케이스를 지원하는 구조를 기술

 

좋은 아키텍처는 프레임워크, 데이터베이스, 웹 서버, 개발 환경 등의 결정을 미룰 수 있도록 만듦

 

 

시스템이 웹을 통해 전달된다는 사실이 시스템 아키텍처에 영향을 주지 않음

: 전달 메커니즘

 

 

프레임워크

프레임워크는 도구일 뿐. 어떻게 하면 아키텍처를 유스케이스에 중점을 둔 채 보존할 수 있을 지 생각해야 함

 

 

테스트

프레임워크와 적당한 거리가 있다면, 필요한 유스케이스에 대해 단위 테스트를 전부 할 수 있음

 

- 테스트를 돌릴 때, 웹 서버가 반드시 필요한 상황, 데이터베이스가 반드시 연결되어야만 테스트를 돌릴 수 있으면 안됨

 

- 엔티티 객체는 반드시 오래된 방식의 간단한 객체여야 하며, 다른 것에 의존해서는 안됨

 

 

결론

소스 저장소를 봤을 때, 첫 인상이 어떤 프로그램인지 바로 파악할 수 있어야 함