‘클린 아키텍처’ 기술 서적에 대해 학습했던 내용을 정리하기 위한 목적의 TIL 포스팅입니다.🙆♂️
- 프레젠터는
험블 객체(Humble Object) 패턴
을 따른 형태로, 아키텍처 경계를 식별하고 보호하는데 도움이된다.
험블 객체 패턴
- 디자인 패턴으로, 테스트하기 어려운 행위와 쉬운 행위를 단위 테스트 작성자가 분리하기 쉽게 하는 방법으로 고안되었다.
- 아이디어는 매우 단순한데, 행위들을 두 개의 모듈 또는 클래스로 나눈다.
- 하나는 가장 기본적인 본질을 남기고(테스트 하기 쉬운), 나머지 하나(험블 객체)는 테스트하기 어려운 행위를 모두 옮긴다.
- 예를 들어, GUI 의 경우 단위 테스트가 어려운데, 화면을 보면서 각 요소가 필요한 위치에 적절히 표시되었는지 검사하는 테스트는 작성하기 매우 어렵기 때문이다.
- 하지만 GUI 에서 수행하는 행위의 대다수는 쉽게 테스트할 수 있다.
- 험블 객체 패턴을 사용하면 두 분류의 행위를 분리하여 프레젠터와 뷰라는 서로 다른 클래스로 만들 수 있다.
프레젠터와 뷰
- 뷰는 험블 객체고 테스트하기 어렵다.
- 이 객체에 포함된 코드는 가능한 간단하게 유지한다.
- 뷰는 데이터를 GUI로 이동시키지만, 데이터를 직접 처리하진 않는다.
- 프레젠터는 테스트하기 쉬운 객체며, 역할은 애플리케이션으로부터 데이터를 받아 화면에 표현할 수 있는 포맷으로 만드는 것이다.
- 이를 통해 뷰는 데이터를 화면으로 전달하는 간단한 일만 처리하도록 만든다.
프레젠터의 예시
- 어떤 필드에 날짜를 표시하고자 한다면, 애플리케이션은 프레젠터에 Date 객체를 전달하고, 해당 프레젠터는 데이터를 적절한 포맷의 문자열로 만들어, 뷰 모델에 담는다. 그러면 뷰는 뷰 모델에서 이 데이터를 찾는다.
- 화면에 금액 표시, 버튼 이름, 메뉴 아이템 이름, 라디오 버튼, 체크 박스, 테스트 필드의 이름 모두 동일한 방식으로 처리된다.
테스트와 아키텍처
- 테스트 용이성은 좋은 아키텍처가 지녀야 할 속성이다.
- 험블 객체 패턴이 좋은 예인데, 행위를 테스트하기 쉬운 부분과 어려운 부분으로 분리하면 아키텍처 경계가 정의되기 때문이다.
- 프레젠터와 뷰 사이의 경계는 이러한 경계 중 하나이며, 이 밖에도 수많은 경계가 존재한다.
데이터베이스와 게이틔웨이
- 유스케이스 인터랙터와 데이터베이스 사이에는 데이터베이스 게이트웨이(Database Gateway)가 위치한다.
- 이 게이트웨이는 다형적 인터페이스로, 애플리케이션이 DB에 수행하는 생성, 조회, 갱신, 삭제 작업과 관련된 모든 메서드를 포함한다.
- 예를 들어, 애플리케이션에서 어제 로그인한 모든 사용자의 성(last name)을 알 수 있어야 한다면, UserGateway 인터페이스는
getLastNamesOfUsersWhoLoggedInAfter
라는 메서드를 제공할 것이고, 이 메서드는 날짜를 인자로 받아서 사용자 성들을 담은 목록을 반환할 것이다.
- 예를 들어, 애플리케이션에서 어제 로그인한 모든 사용자의 성(last name)을 알 수 있어야 한다면, UserGateway 인터페이스는
- 유스케이스 계층은 SQL 을 허용하지 않는다.
- 따라서 유스케이스 계층은 필요한 메서드를 제공하는 게이트웨이 인터페이스를 호출한다.
- 그리고 인터페이스의 구현체는 데이터베이스 계층에 위치한다.
- 이 구현체는 험블 객체이다.
- 인터렉터는 애플리케이션에 특화된 업무 규칙을 캡슐화하기 떄문에 험블 객체가 아니다.
- 따라서 테스트하기 쉬운데, 게이트웨이는 스텁(stub)이나 테스트 더블(test-double)로 적당히 교체할 수 있기 때문이다.
데이터 매퍼
- 분명히 해야 할 점은 객체 관계 매퍼(ORM) 같은건 사실 존재하지 않는다.
- 객체랑 데이터 구조랑은 다르기 때문이다.
- 최소한 객체를 사용하는 사람 관점에서 객체는 데이터 구조가 아니다.
- 데이터는 private 으로 선언되므로 객체의 사용자는 데이터를 볼 수 없다.
- 사용자는 객체에서 public 메서드만 볼 수 있기에 객체는 단순한 오퍼레이션의 집합이다.
- 데이터 구조는 함축된 행위를 가지지 않는 public 데이터 변수의 집합이다.
- 그러기에 ORM 보단 ‘데이터 매퍼(Data Mapper)’라 부르는 편이 나아 보이는데, RDB 테이블로부터 가져온 데이터를 데이터 구조에 맞게 담아주기 때문이다.
- 이러한 ORM 시스템은 데이터베이스 계층에 위치하는데 실제로 ORM 은 게이트웨이 인터페이스와 데이터베이스 사이에서 일종의 또 다른 험블 객체를 경계를 형성한다.
서비스 리스너
- 애플리케이션 다른 서비스와 반드시 통신해야 한다면, 또는 애플리케이션에서 일련의 서비스를 제공해야 한다면, 우리는 여기서 서비스 경계를 생성하는 험블 객체 퍁너을 발견할 수 있다.
- 애플리케이션은 데이터를 간단한 데이터 구조 형태로 로드한 후, 이 데이터 구조를 경계를 가로질러 특정 모듈로 전달한다.
- 그러면 해당 모듈은 데이터를 적절한 포맷으로 만들어 외부 서비스로 전송한다.
- 반대로 외부로부터 데이터를 수신하는 서비스의 경우, 서비스 리스너가 서비스 인터페이스로부터 데이터를 수신하고, 데이터를 애플리케이션에서 사용할 수 있게 간단한 데이터 구조로 포맷을 변경한다.
- 그런 후 이 데이터 구조는 서비스 경계를 가로질러 내부로 전달된다.
결론
- 각 아키텍처 경계마다 경계 가까이 숨어 있는 험블 객체 패턴을 발견할 수 있을 것이다.
- 경계를 넘나드는 통신은 거의 모두 간단한 데이터 구조를 수반할때가 많고, 대개 그 경계는 테스트하기 어려운 무언가와 쉬운 무언가로 분리될 것이다.
- 그리고 이러한 아키텍처 경계에서 험블 객체 패턴을 사용하면 전체 시스템의 테스트 용이성을 크게 높일 수 있다.