byungil

21장. 소리치는 아키텍처

아키텍처의 테마

  • 소프트웨어 애플리케이션의 아키텍처는 유스케이스에 대해 소리쳐야 함

  • 아키텍처는 프레임워크에 대한 것이 아니며, 프레임워크는 사용하는 도구일 뿐임

  • 아키텍처를 프레임워크 중심으로 만들어버리면, 유스케이스가 중심이 되는 아키텍처는 절대 나올 수 없음

아키텍처의 목적

  • 좋은 아키텍처는 유스케이스가 중심이므로, 프레임워크나 도구 등에 전혀 구애받지 않고 유스케이스를 지원하는 구조를 가짐

  • 좋은 아키텍처는 프레임워크, 데이터베이스, 웹 서버, 여타 개발 환경 문제나 도구에 대한 결정을 미룰 수 있음

  • 좋은 아키텍처는 프로젝트의 훨씬 후반까지 결정을 하지 않아도 되도록 도와줄 뿐만 아니라 결정을 쉽게 번복할 수 있음

  • 좋은 아키텍처는 유스케이스에 중점을 두며, 지엽적인 관심사에 대한 결합은 분리시킴

프레임워크는 도구일 뿐, 삶의 방식이 아니다.

  • 프레임워크는 매우 강력하고, 상당히 유용할 수 있음으며, 누군가는 “프레임워크가 모든 것을 하게 하자”라는 태도를 취함

  • 하지만 이는 우리가 취하고 싶은 태도가 아님

  • 우리는 어떻게 하면 아키텍처를 유스케이스에 중점을 둔 채 그대로 보존할 수 있을지를 생각해야 함

테스트하기 쉬운 아키텍처

  • 아키텍처가 유스케이스를 최우선으로 한다면, 프레임워크 없이도 필요한 유스케이스 전부를 단위 테스트 할 수 있어야 함

  • 테스트를 돌리는데 반드시 웹서버나 데이터베이스가 필요한 상황이어서는 안됨

  • 엔티티는 반드시 POJO여야 하며, 프레임워크나 데이터베이스 및 다른 것들에 의존해서는 안되고 유스케이스가 엔티티를 조작해야 함

  • 최종적으로 프레임워크로 인한 어려움을 겪지 않고도 반드시 이 모두를 있는 그대로 테스트할 수 있어야 함

22장. 클린 아키텍처

  • 프레임워크 독립성

  • 테스트 용이성

  • UI 독립성

  • 데이터베이스 독립성

  • 외부 에이전시에 대한 독립성

의존성 규칙

  • 각각의 동심원은 서로 다른 영역이며, 안으로 들어갈수록 고수준의 소프트웨어가 됨

  • 이러한 아키텍처가 동작하도록 하는 가장 중요한 것은 의존성 규칙임

  • 바깥은 메커니즘 안은 정책인데, “소스 코드 의존성은 반드시 안쪽으로, 고수준의 정책을 향해야 한다." 임

  • 우리는 외부 원에 위치한 어떤 것도 내부의 원에 영향을 주지 않기를 바람

  • 내부의 원에 속한 요소는 외부의 원에 속한 어떤 것(함수, 클래스, 변수 등의 모든 소프트웨어 엔티티)도 알지 못함

엔티티

  • 운영 관점에서 특정 애플리케이션에 무언가 변경이 필요하더라도 엔티티에는 절대로 영향을 주면 안됨

  • ex) 엔티티는 핵심 비즈니스 로직을 다루므로, UI 단의 페이징 처리 등이 필요해도 변경이 일어나서는 안됨

유스케이스

  • 유스케이스의 변경이 엔티티에 영향을 주면 안되며, 외부 요소의 변경이 이 계층에 영향을 주는 것도 안됨

  • 하지만 운영 관점에서 애플리케이션이 변경된다면 유스케이스가 영향을 받고, 유스케이스 세부사항이 변하면 일부 코드는 영향을 받음

인터페이스 어댑터

  • 프레젠터나 컨트롤러 등과 같은 어댑터들로 구성되며, 컨트롤러에서 유스케이스로 전달된 요청은 다시 컨트롤러로 되돌아 감

  • 어댑터는 유스케이스와 엔티티에 맞는 데이터에서 DB나 웹 등과 같은 외부 요소에 맞는 데이터로 변환함

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

프레임워크와 드라이버

  • 모든 세부사항이 위치하는 곳으로, 웹과 데이터베이스도 세부사항이므로 이를 외부에 위치시켜 피해를 최소화함

경계를 횡단하는 데이터는 어떤 모습인가?

  • 데이터 구조가 어떤 의존성을 가져 의존성 규칙을 위배되게 하는 일은 원치 않음

  • 따라서 경계를 가로 질러 데이터를 전달할 때, 데이터는 항상 내부의 원에서 사용하기에 가장 편리한 형태여야 함

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

험블 객체 패턴

  • 험블 객체 패턴은 디자인 패턴으로, 테스트하기 어려운 행위와 쉬운 행위를 단위 테스트 작성자가 쉽게 분리하는 방법으로 고안됨

  • 험블 객체 패턴은 행위들을 두 개의 모듈 또는 클래스로 나누는데, 이 중 하나가 험블임

    • 기본적인 본질은 남기고, 테스트하기 어려운 행위를 모두 험블 객체로 옮김

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

프레젠터와 뷰

    • 험블 객체이며, 테스트하기 어려움

  • 프레젠터

    • 애플리케이션으로부터 데이터를 받아 화면에 표현할 수 있는 포맷으로 만드는 것

    • 이를 통해 뷰는 데이터를 화면으로 전달하는 간단한 일만 처리하도록 만듬

테스트와 아키텍처

  • 테스트 용이성은 좋은 아키텍처가 지녀야 할 속성으로 오랫동안 알려짐

  • 험블 객체 패턴이 좋은 예인데, 테스트하기 쉬운 부분과 어려운 부분으로 분리하면 아키텍처가 정의되기 때문임

  • 프레젠터와 뷰는 이러한 경계 중 하나이며, 이 밖에도 수 많은 경계가 존재함

24장. 부분적 경계

  • 아키텍처 경계를 완벽하게 만드는 데는 비용이 많이 들며, 유지하는데도 엄청난 노력이 필요함

    • 쌍방향의 다형적 Boundary 인터페이스

    • Input/Output 데이터구조

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

  • 마지막 단계를 건너뛰기: 독립적으로 컴파일 및 배포 가능한 컴포넌트로 만들고, 단일 컴포넌트에 그대로 모아둠

  • 일차원 경계: 양방향 Boundary 인터페이스가 아닌 한방향만 경계를 인터페이스로 격리함

  • 파사드: Facade 클래스에 모든 서비스 클래스를 메소드 형태로 정의하고, 호출이 발생하면 해당 서비스 클래스로 전달함

25장. 계층과 경계

  • 시스템이 3가지 컴포넌트(UI, 업무 규칙, DB)로만 구성된다고 생각하기 쉬움

  • 몇몇 단순한 시스템에서는 이 정도로 충분하지만, 대다수의 시스템에서 컴포넌트는 이보다 많음

  • 아키텍처 경계는 어디에나 존재하며, 아키텍처 경계가 언제 필요한지를 신중하게 파악해내야 함

    • 이러한 경계를 제대로 구현하려면 비용이 많이 든다는 사실도 인지하고 있어야 함

    • 동시에 이러한 경계가 무시되었다면 나중에 다시 추가하는 비용이 크다는 사실도 알아야 함

  • 매우 똑똑한 일부 사람들이 수년 동안 말해왔듯이, 추상화가 필요하리라고 미리 예측해서는 안됨(YAGNI)

    • YAGNI(You Aren’t Going to Need It)에는 지혜가 담겨 있는데, 오버 엔지니어링이 언더 엔지니어링보다 나쁠때가 훨씬 많음

    • 반대로 어떤 아키텍처 경계도 존재하지 않는 상황에서 경계가 정말로 필요하다는 사실을 발견할 수 있음

    • 하지만 그때서야 경계를 추가하려면 비용이 많이 들고 큰 위험을 감수해야 함

  • 그러므로 소프트웨어 아키텍트는 미래를 내다보고 현명하게 추측해야 함

26장. 메인 컴포넌트

  • 모든 시스템에는 최소한 하나의 컴포넌트가 존재하고, 이 컴포넌트가 나머지 컴포넌트를 생성하고 조정하며 관리함

  • 이 컴포넌트를 메인 컴포넌트(Main)이라고 부름

궁극적인 세부사항

  • 메인 컴포넌트는 궁극적인 세부사항으로, 가장 낮은 수준의 정책임

  • 메인은 시스템의 초기 지입점으로, 운영체제를 제외하면 어떤 것도 메인에 의존하지 않음

  • 메인은 모든 팩토리와 전략 및 나머지 기반 설비를 생성한 후, 시스템에서 더 높은 수준을 담당하는 부분으로 제어권을 넘김

  • 의존성 주입 프레임워크를 이용해 의존성을 주입하는 일은 바로 이 메인 컴포넌트에서 이뤄져야 함

  • 메인은 고수준의 시스템을 위한 모든 것을 로드한 후, 제어권을 고수준의 시스템에게 넘김

결론

  • 메인은 초기 조건과 설정을 구성하고, 외부 자원을 모두 수집한 후 제어권을 애플리케이션의 고수준 정책으로 넘기는 플러그인임

  • 메인은 플러그인이므로 메인 컴포넌트를 애플리케이션의 설정 별로 하나씩 둬서 둘 이상의 메인 컴포넌트를 만들 수도 있음

  • 메인을 플러그인 컴포넌트로 여기고 아키텍처 경계 바깥에 위치한다고 보면, 설정 관련 문제를 훨씬 쉽게 해결할 수 있음

Last updated