IT/책

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

Terriermon 2021. 12. 19. 04:47

클린 아키텍처

22장, 클린 아키텍처

육각형 아키텍처, DCI, BCE 등 다양한 아키텍처들은 모두 관심사 분리라는 목표를 가지고 있음

 

 

관심사 분리

- 프레임워크 독립성: 아키텍처는 프레임워크 존재 여부에 의존하지 않음

- 테스트 용이성: 업무 규칙은 UI, 데이터베이스, 웹서버 등의 외부 요인이 없어도 테스트 가능

- UI 독립성: 시스템의 나머지 부분을 변경하지 않아도 UI 쉽게 변경

- 데이터베이스 독립성: 오라클을 몽고DB, 빅테이블 등으로 변경 가능, 업무 규칙은 데이터베이스에 결합되지 않음

- 모든 외부 에이전시에 대한 독립성: 실제 업무 규칙은 외부 세계와의 인터페이스에 대해 전혀 알지 못함

 

아이디어 통합

 

의존성 규칙

각각의 동심원은 소프트웨어에서 서로 다른 영역을 표현함

> 안으로 들어갈 수록 고수준의 소프트웨어

> 바깥쪽: 매커니즘, 안쪽: 정책

 

- 소스 코드 의존성은 반드시 안쪽으로, 고수준의 정책을 향해야 함

> 내부 원에 속한 요소는 외부 원에 속한 어떤 것도 알지 못함 = 외부의 원에 선언된 데이터 형식은 내부의 원을 사용하면 안됨

 

 

엔티티

- 전사적인 핵심 업무 규칙을 캡슐화

 

- 외부의 무언가가 변경되더라도 엔티티가 변경될 가능성은 매우 낮음

 

- 특정 애플리케이션에 변경이 필요해도 엔티티는 계층에 영향을 주지 않음

 

 

유스케이스

- 소프트웨어 애플리케이션에 특화된 업무 규칙 포함

 

- 시스템의 모든 유스케이스를 캡슐화&구현

 

- 엔티티로 들어오고 나가는 데이터 흐름 조정

 

- 프레임워크와 같은 외부 요소로 인한 변경에 영향을 받지 않음

 

- 애플리케이션이 변경될 때, 유스케이스가 영향을 받음

 

 

인터페이스 어댑터

- 유스케이스와 엔티티에 가장 편리한 형식에서 데이터베이스나 웹 등 외부 에이전시에게 가장 편리한 형식으로 변환

 

- GUI, MVC 아키텍처, 프레젠터, 뷰, 컨트롤러 등

 

- 프레임워크가 이용하기 편리한 형식으로 변환되기 때문에, 해당 계층에 속한 어떤 코드도 데이터베이스에 대해 알면 안됨

 

- 데이터를 외부 서비스와 같은 외부적인 형식에서 유스케이스나 엔티티에 사용되는 내부적인 형식으로 변환되는 또 다른 어댑터가 필요

 

 

프레임워크와 드라이버

- 가장 바깥 계층

 

- 모든 세부사항이 위치하는 곳

 

 

경계 횡단하기

- 의존성은 항상 안쪽을 향함

 

- 제어 흐름과 반대여야 하는 경우, 의존성 역전을 이용

 

 

 

 

23장, 프레젠터와 험블 객체

프레젠터는 험블 객체(Humble Object) 패턴을 따른 형태로, 아키텍처 경계를 식별하고 보호하는 데 도움이 됨

 

 

험플 객체 패턴

디자인 패턴 종류 중 하나로, 테스트 하기 어려운 행위와 테스트 하기 쉬운 행위를 단위 테스트 작성자가 분리하기 쉽게 하는 방법

 

험블

행위들을 두 개의 모듈 또는 클래스(프레젠터)로 나눈 모듈

> 가장 기본적인 본질을 남기고 테스트 하기 어려운 행위롤 모두 험블 객체로 옮김

> 나머지 모듈에는 험블 객체에 속하지 않은 테스트하기 쉬운 행위를 옮김

 

 

테스트하기 어려운 객체

> 가능한 한 간단하게 유지

 

 

프레젠터

테스트하기 쉬운 객체

 

 

ex) GUI 테스트

- 뷰: 데이터를 화면하는 간단한 일만 처리 (데이터 직접 처리X)

 

- 프레젠터: 애플리케이션으로부터 데이터를 받아 화면에 표현

 

 

 

데이터베이스 게이트웨이

- 유스케이스 인터랙터와 데이터베이스 사이에 데이터베이스 게이트웨이 위치

 

- 게이트웨이는 다형적 인터페이스로, 애플리케이션이 데이터베이스에 수행하는 생성, 조회, 갱신 ,삭제 작업과 관련된 모든 메서드 포함

 

- 인터랙터: 애플리케이션에 특화된 업무 규칙을 캡슐화 (험블 객체X)

> 테스트 하기 쉬움: 게이트웨이는 스텁이나 테스트 더블로 적당히 교체

 

유스케이스 -> 게이트웨이 인터페이스 -> 인터페이스 구현체(데이터베이스 계층, 험블 객체) -> 구현체에서 SQL 사용

 

 

데이터 매퍼

- ORM(객체 관계 매퍼, Object Relational Mapper)는 존재하지 않음

> 객체는 데이터 구조가 아니기 때문

> 사용자 관점에서 ORM은 단순한 오퍼레이션의 집합

 

- ORM 시스템은 데이터베이스 계층에 존재

> 게이트웨이

 

 

서비스 리스너

서비스 리스너가 서비스 인터페이스로부터 데이터를 수신하고, 데이터를 애플리케이션에서 사용할 수 있게 간단한 데이터 구조로 포맷을 변경

> 데이터 구조가 서비스 경계를 가로질러 내부로 전달

 

 

결론

각 아키텍처 경계마다 험블 객체 패턴을 발견할 수 있음

 

 

 

24장, 부분적 경계

아키텍처 경계를 완벽하게 만드는 데는 많은 비용이 소모

> 부분적 경계

 

 

1. 마지막 단계 건너뛰기

독립적으로 컴파일하고 배포할 수 있는 컴포넌트를 만들기 위한 작업은 모두 수행한 후, 단일 컴포넌트에 그대로 모아 두는 것

> 모두 단일 컴포넌트로 컴파일해서 배포

 

2. 일차원 경계

쌍방향 Boundary 인터페이스 사용 시, 격리된 상태를 유지하기 위해 비용이 많이 듦

> Service Boundary를 통해 Client와 ServiceImpl 격리 (의존성 역전)

strategy 패턴

 

 

3. 퍼사드

Facade 클래스에는 모든 서비스 클래스를 메서드 형태로 정의, 서비스 호출이 발생하면 해당 서비스 클래스로 호출 전달

퍼사드 패턴

 

 

 

25장, 계층과 경계

텍스트 기반 UI는 그대로 유지하면서 게임 규칙과 UI를 분리

> 게임 규칙은 언어 독립적인 API를 사용하여 UI와 통신

> UI는 API를 사람이 이해할 수 있는 언어로 변환

> 게임 규칙을 재사용 할 수 있음

 

 

클린 아키텍처

API 컴포넌트에만 집중하여 다이어그램 단순화

- 모든 화살표가 위를 향함

why? GameRules이 최상위 컴포넌트이므로

 

- 데이터 흐름을 두 개로 나누어서 효과적으로 분리

> 두 흐름은 상단의 GameRules에서 만나서 데이터에 대한 최종적인 처리기가 됨

 

- 흐름은 여러 갈래가 될 수 있음

 

 

결론

- 아키텍처 경계는 어디에나 존재

 

- 경계는 비용이 많이 들지만, 경계가 무시되었을 때 추가 비용도 큼

 

 

 

26장, 메인 컴포넌트

메인 컴포넌트: 나머지 컴포넌트를 생성, 조정, 관리하는 컴포넌트

- 가장 낮은 수준의 정책

- 팩토리, 전략, 시스템 전반 기반 설비 생성 후, 제어권을 넘김

- 의존성 주입

- 가장 지저분한 저수준의 모듈

 

 

 

27장, 크고 작은 모든 서비스들

서비스 아키텍처나 마이크로 아키텍처는 개발, 배포, 독립성을 지원하고 상호 결합이 분리되어 있는 것 처럼 보임

 

서비스 아키텍처

서비스: 프로세스나 플랫폼 경계를 가로지르는 함수 호출

 

 

결합 분리의 오류

다른 프로세스에서 실행되더라도 네트워크 상 공유 자원으로 인해 결합될 가능성이 존재

 

개발 및 배포 독립성의 오류

데브옵스: 전담 팀에서 각 서비스를 작성, 유지보수, 운영 -> 확장 가능한 것으로 간주

- 항상 독립적으로 가능한 것이 아님

 

컴포넌트 기반 서비스

다형적으로 확장할 수 있는 클래스 집합을 생성하여 새로운 기능을 처리 -> 서비스 결합 최소화

 

- 서비스에서도 SOLID 원칙을 준수하여 컴포넌트 구조를 갖춰서 해결

 

횡단 관심사

서비스들은 모두 결합되어 있어, 독립적으로 개발 배포 유지가 될 수 없다.

- 서비스 내부는 의존성 규칙도 준수하는 컴포넌트 아키텍처로 설계해야 함

 

 

 

28장, 테스트 경계

아키텍처 관점에서는 모든 테스트가 동일

 

테스트

1. 테스트는 항상 의성 규칙을 따름

2. 독립적으로 배포 가능

3. 시스템 컴포넌트 중에서 가장 고립: 테스트가 시스템 운영에 꼭 필요하지 않음

 

테스트를 고려한 설계

변동성이 있는 것에 의존하지 않고 테스트를 수행할 수 있어야 함

> 테스트가 모든 업무 규칙을 검증하는 데 사용할 수 있는 특화된 API 생성

 

테스트 API

- 테스트를 애플리케이션으로부터 분리할 목적으로 사용

 

- 테스트 API를 통해 애플리케이션 구조를 테스트로부터 숨길 수 있음

> 테스트 리팩터링, 진화 시 상용 코드에 영향X

 

 

 

29장, 클린 임베디드 아키텍처

소프트웨어는 닳지 않지만, 펌웨어/하드웨어는 낡는다. 그러나 소프트웨어는 하드웨어에 의존한다.

앱-티튜드 테스트: 앱이 동작하도록 만드는 것

타깃-하드웨어 병목현상: 코드를 테스트할 수 있는 환경이 특정 타깃으로 국한되게 되면, 병목 현상 발생

 

클린 임베디드 아키텍처

(= 테스트 하기 쉬운 임베디드 아키텍처)

 

계층

 

안티 패턴: 소프트웨어와 펌웨어가 섞이는 패턴

 

HAL(하드웨어 추상화 계층)
펌웨어와 소프트웨어 사이의 경계
- 자신보다 위에 있는 소프트웨어를 위해 존재하기 때문에 HAL의 API는 소프트웨어의 필요에 맞게 만들어짐
- 하드웨어 세부사항을 HAL사용자에게 드러내지 않음

 

 

운영체제

소프트웨어는 운영체제를 통해 운영 환경이 제공하는 서비스에 접근

> 운영체제는 세부사항으로, 종속이 되면 안됨

 

OSAL(운영체제 추상화 계층)

소프트웨어를 운영체제로부터 격리