본문 바로가기
클린아키텍처

[클린아키텍처] 23장 프레젠터와 험블 객체, 24장 부분적 경계, 25장 계층과 경계

by bzerome240 2022. 3. 13.

험블 객체 패턴 Humble Object

: 테스트하기 어려운 행위테스트하기 쉬운 행위를 단위 테스트 작성자가 분리하기 쉽게 하는 방법으로 고안된 디자인 패턴

  • 행위들을 두개의 모듈 또는 클래스로 나눈다. 이 모듈 중 하나가 험블이다.
  • 가장 기본적인 본질을 남기고 테스트하기 어려운 행위를 모두 험블 객체로 옮긴다.
  • ex) GUI의 경우 화면에 각 요소가 필요한 위치에 적절히 표시되었는지 검사하는 테스트는 작성하기가 어렵다. 하지만 GUI에서 수행하는 행위의 대다수는 쉽게 테스트 가능하다. -> 험블 객체 패턴을 사용하여 프레젠터와 뷰라는 서로 다른 클래스로 만들 수 있다.

 

humble
1. 겸손한 2. (예의상 자기를 낮추는 표현에서) 변변치 않은, 1. 겸손한 2. 변변찮은, 작은, 겸손한
 
 
 
행위를 테스트하기 쉬운 부분과 테스트하기 어려운 부분으로 분리하면 아키텍처 경계가 정의 된다.
 
 

1) 뷰와 프레젠터 사이의 경계

뷰는 험블 객체이다. (보잘 것 없다)
프레젠터
테스트하기 어려운 객체 테스트하기 쉬운 객체
뷰는 데이터를 화면으로 전달하는 간단한 일만 처리한다. 애플리케이션으로부터 데이터를 받아 화면에 표현할 수 있는 포맷으로 만든다.

 

2) 인터랙터와 데이터베이스 사이의 경계

  • 유스케이스 인터랙터와 데이터베이스 사이에는 데이터베이스 게이트웨이가 위치한다.
  • 게이트웨이는 다형적 인터페이스이며, 생성, 조회, 갱신, 삭제와 관련된 모든 메서드를 포함한다.
  • ex) 애플리케이션에서 어제 로그인한 모든 사용자들의 성을 알아야할 때, UserGateway 인터페이스는 getLastNamesOfUsersWhoLoggedInAfter라는 메서드를 제공하고 이 메서드는 날짜를 인자로 받아서 사용자 성들을 담은 목록을 반환한다.
  • -> 유스케이스 계층은 SQL을 허용하지 않고, 필요한 메서드를 제공하는 게이트웨이 인터페이스를 호출한다.
  • -> 인터페이스 구현체(험블 객체)는 데이터베이스 계층에 위치한다.

 

3) 애플리케이션과 서비스 사이의 경계

ex) 외부로부터 데이터를 수신하는 서비스의 경우, 서비스 리스너가 서비스 틴터페이스로부터 데이터를 수신하고 데이터를 애플리케이션에서 사용할 수 있게 간단한 데이터 구조로 포맷을 변경한다.

 

 

결론

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

-> 테스트하기 어려운 무언가와 테스트하기 쉬운 무언가로 분리된다.

-> 시스템의 테스트 용이성을 높일 수 있다.

 


 

아키텍처 경계를 완벽하게 만드는데는 비용이 많이 든다.

ex) 쌍방향의 Boundary 인터페이스, Input과 Output을 위한 데이터 구조 생성

ex) 두 영역을 독립적으로 컴파일하고 배포할 수 있는 컴포넌트로 격리하는 데 필요한 모든 의존성 관리

-> 부분적 경계를 구현한다.

 

 

부분적 경계를 생성하는 방법

 

1) 마지막 단계를 건너뛰기

독립적으로 컴파일하고 배포할 수 있는 컴포넌트를 만들기 위한 작업은 모두 수행한 후, 단일 컴포넌트에 그대로 모아만 두는 것 (쌍방향 인터페이스, 입출력 데이터 구조)

부분적 경계를 만드려면 완벽한 경계를 만들 때 만큼의 코드량과 사전 설계가 필요하다

But 다수의 컴포넌트를 관리하는 작업은 하지 않아도 되며 배포 관리 부담도 없다.

 

2) 일차원 경계

완벽한 형태의 아키텍처 경계는 양방향으로 격리된 상태를 유지해야하므로 쌍방향 Boundary 인터페이스를 사용한다.

양방향으로 격리된 상태를 유지하려면 초기 설정할 때나 지속적으로 유지할 때도 비용이 많이 든다.

 

전략 패턴 Strategy

  • ServiceBoundary 인터페이스는 클라이언트가 사용하며 ServiceImpl 클래스가 구현한다.
  • Client를 ServiceImpl로 부터 격리 시키고자 의존성 역전 원칙을 적용함.
  • But 쌍방향 인터페이스가 없고 개발자와 아키텍트가 제대로 훈련되어 있지 않다면, 전략 패턴은 위에 점선과 같은 비밀 통로가 생길 수 있음.

 

 

3) 퍼사드 패턴 Facade

 

  • 모든 서비스 클래스를 메서드 형태로 정의하고, 서비스 호출이 발생하면 해당 서비스 클래스로 호출을 전달한다.
  • 클라이언트는 서비스 클래스에 직접 접근할 수 없다.
  • 하지만 이 경우 Client가 모든 서비스 클래스에 대해 추이 종속성을 가지게 된다.
  • 정적 언어라면 서비스 클래스 중 하나라도 변경되면 Client도 무조건 재컴파일 해야 한다.

 

결론

아키텍처 경계가 언제 어디에 존재해야할지, 그 경계를 완벽하게 구현할지 부분적으로 구현할지 결정하는 일은 아키텍트의 역할이다.

 

 


 

시스템은 UI, 업무 규칙, 데이터베이스 등등 여러 컴포넌트를 가지고있다.

 

지금부터 움퍼스 사냥 게임을 예시로 아키텍처를 설명해주어서, 한번 움퍼스 사냥게임이 뭔지 찾아봤다.

1972년에 발매됐다는데, 책에서 말하는 게임이 이게 맞는지는 모르겠음...!ㅎ

 

 

 

1 의존성 규칙 준수 ex) 움퍼스 사냥 게임 다이어그램

https://hwannny.tistory.com/44

  • UI 컴포넌트가 어떤 언어를 사용하더라도 게임 규칙을 재사용할 수 있다.
  • 어떤 데이터 저장소를 사용하더라도 게임 규칙에는 영향이 없다.

 

 

2 다양한 메커니즘을 위해 개선된 다이어그램

https://hwannny.tistory.com/44

  • Text Delivery, Language, Data Storage -> API를 정의하는 추상 컴포넌트
  • API 추상 컴포넌트는 위나 아래의 컴포넌트가 구현한다.
  • ex) Language API는 English나 Spanish 컴포넌트가 구현한다.

 

 

3 API 컴포넌트만 집중한 단순화된 다이어그램

https://hwannny.tistory.com/44

  • GameRules는 최상위 수준 정책을 가지므로 가장 위에 위치한다.
  • 데이터 흐름: 사용자 입력 -> TextDelivery -> Language -> GameRules -> DataStorage -> Language -> TextDelivery -> 사용자 출력
  • 왼쪽은 사용자와의 통신에 관여하는 데이터 흐름, 오른쪽은 데이터 영속성에 관여하는 데이터 흐름

 

4 네트워크 컴포넌트를 추가한 다이어그램

https://hwannny.tistory.com/44

  • 네트워크상에서 여러 사람들이 함께 플레이할 수 있다.
  • 데이터흐름은 세가지가 된다. 

 

5 고수준의 정책

https://hwannny.tistory.com/44

GameRules의 컴포넌트는 여러 정책 집합이 존재하므로 고수준 정책과 분리한다. -> Player Management, Move Management

 

 

이외에도 마이크로서비스 APi를 추가할수도 있다.

 

 

결론

  • 아키텍처 경계는 어디에나 존재할 수 있으며 언제 필요한지 신중하게 파악해야한다.
  • You Aren't Going to Need It (YAGNI) 철학처럼 추상화를 미리 예측해서는 안된다.
  • 오버 엔지니어링이 언더 엔지니어링보다 나쁠 때도 많다.
  • But 나중에 경계를 추가하려면 비용과, 위험이 크다.
  • -> 시스템이 발전함에따라 경계 시점이 보이면, 해당 경계를 구현하는 비용과 무시할 때 비용을 가늠해보면서 결정해야한다.

 

 

 

 

 

 

728x90
반응형

댓글