본문 바로가기

SearchDeveloper/클린 아키텍처

[8] 24~26 경계

23.10.10

[24] 부분적 경계

완벽한 경계를 만들기엔 비용과 노력이 너무 많이 들므로(쌍방향 바운더리 인터페이스, input/output 데이터 구조, 컴포넌트 격리), 부분적 경계를 활용해 볼 수도 있다.

부분적 경계 구현 방법 3가지

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

마지막 단계: 다중 컴포넌트로 각각 배포하는 것 같음

독립적으로 컴파일하고 배포할 수 있는 컴포넌트 만드는 작업은 모두 수행한 후, 단일 컴포넌트에 그대로 모아만 둔다. 컴파일이랑 배포도 단일 컴포넌트로 한다.

그래도 완벽한 경계를 만들 때만큼의 코드량과 설계 비용은 많이 들지만, 버전 번호나 배포 관리 부담이 없는 차이가 있다.

사례) Fitnesse 만들 때 초기 전략으로 웹 컴포넌트를 분리했으나 한 번에 다운로드하는게 목적이기도 하고 시간 흐르면서 분리할 이유가 없어져 결국 단일 컴포넌트로 마무리 되었다.

2. 일차원 경계

쌍방향 바운더리가 아닌 단방향으로 설계한다.

스트레티지 패턴을 사용.

client 와 전략 사이에 의존성 역전은 잘 됐는데, 구현체에서 바로 다른 컴포넌트를 참조할 수 있는 위험 존재

3. 퍼사드

Facade 클래스는 서비스 호출이 발생하면 해당 서비스 클래스로 호출을 전달한다.

단점: 서비스 클래스 하나에서 소스 변경돼도 클라이언트 까지 재컴파일 해야 한다.

의존석 역전을 희생?

완벽한 경계를 만들기엔 비용과 노력이 너무 많이 들므로 부분적 경계를만들어 볼수도 있다

부분적 경계 구현 방법 3가지: 설계랑 소스는 다중 컴포넌트인 것처럼 분리하되 배포는 단일로, 스트레티지 패턴으로 단방향으로 설계하기, 퍼사드 클래스로 서비스 클래스들한테 요청 전달하기

 

[25] 계층과 경계

움파스 사냥 게임 사례를 통해 UI, 업무 규칙 DB 외의 추가될 수 있는 다른 컴포넌트 구성도 살펴본다.

1단계) 다양한 언어 UI, 게임 규칙, 게임 상태 저장

게임 상태 저장은 휘발성인 Flash일수도, 비휘발성인 Cloud 저장일 수도 있음

2단계) 다양한 UI - 쉘, 텍스트 메시지/채팅

구현체를 제거하면, 화살표는 최상위 컴포넌트인 게임 규칙으로 흐른다.

3단계) 네트워크 컴포넌트, 이동 관리, 플레이어 관리 컴포넌트 추가

결론: 경계를 설계할 땐 너무 미리 예측하지 말자. 오버 엔지니어링이 언더 엔지니어링보다 나쁠 때가 많다고 한다. (YAGNI, You Aren’t Going to Need It)

 

[26] 메인 컴포넌트

메인 컴포넌트

  • 궁극적인 세부사항
  • 가장 낮은 수준의 정책
  • 시스템의 초기 진입점
  • 더 높은 수준 담당하는 부분으로 제어권을 넘기는 역할
  • ex) 메인 함수

입력 스트림 생성, 메인 루프, 명령어 해성은 메인해서 하지만 실제로 명령어를 처리하는 로직은 고수준에게 넘기고 있다.

public static void main(String[] args) throws IOException {
 game = HtwFactory.makeGame("htw.game.HuntTheWumpusFacade", new Main());
 createMap();
 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 game.makeRestCommand().execute();
 while (true) {
  System.out.println(game.getPlayerCavern());
  System.out.println("Health: " + hitPoints + " arrows: " +
  game.getQuiver()
 );
 HuntTheWumpus.Command c = game.makeRestCommand();
 System.out.println(">");
 String command = br.readLine();
 if (command.equalsIgnoreCase("e"))
  c = game.makeMoveCommand(EAST);
 else if (command.equalsIgnoreCase("w"))
  c = game.makeMoveCommand(WEST);
 else if (command.equalsIgnoreCase("n"))
 ...
 return;
 c.execute();
}

메인을 초기 조건 및 설정 구성, 외부 자원 수집, 제어권 넘기는 일을 하는 플러그인처럼 생각하면 개발용 메인 플러그인, 테스트용, 상용 등 여러 플러그인을 만들어 경계 바깥에서 설정 관련 문제를 쉽게 해결할 수 있다.

 

레퍼런스

클린 아키텍처 24~26 장