😃 안녕하세요. 휴먼스케이프 loowin입니다. 😃
오늘은 휴먼의 공통 교양책인 [Clean Architecture]의 부분적 경계, 계층과 경계, 메인 컴포넌트, ‘크고 작은 모든' 서비스들에 대해 알아보겠습니다.
부분적 경계
뛰어난 아키텍트를 위하여
나중에 필요할 수도 있으므로 경계에 필요한 공간을 확보하기 vs 경계를 만드는 비용이 너무 크다고 판단하기
둘 사이에 고민이 있으실 겁니다. 이럴땐! YAGNI(you Aren’t Going to Need It-당신은 그것을 필요로하지 않을거야) 원칙을 생각하세요.
“그래, 하지만 어쩌면 필요할지도.” 라는 생각이 든다면 부분적 경계를 구현해보세요.
마지막 단계를 건너뛰기
: 독립적으로 컴파일하고 배포할 수 있는 컴포넌트를 만들기 위한 작업은 모두 수행한 후, 단일 컴포넌트에 그대로 모아만 두는 것입니다.
부분적 경계를 만들려면 완벽한 경계를 만들 때 만큼의 코드량과 사전 설계가 필요합니다. 하지만 다수의 컴포넌트를 관리하는 작업은 하지 않아도 됩니다.
일차원 경계
: 완벽한 형태의 아키텍처 경계는 양방향으로 격리된 상태를 유지해야 하지만 초기 설정할 때나 지속적으로 유지할 때도 비용이 많이 듭니다. 그렇기 때문에 인터페이스는 클라이언트가 사용하며 service 인터페이스를 사용하여 serviceImpl 클래스와의 의존성을 끊어내는 일차원 경계를 만듭니다.
부분적 경계의 결론은 아키텍처 경계가 언제, 어디에 존재해야 할지, 그리고 그경계를 완벽하게 구현할지 아니면 부분적으로 구현할지를 결정하는 일 또한 아키텍트의 역할입니다.
— — —
계층과 경계
시스템이 세가지 컴포넌트로(UI, 업무규칙, 데이터베이스)로만 구성된다고 생각하기 쉽습니다.
몇몇 단순한 시스템에서는 이 정도로 충분합니다. 대다수의 시스템에서 컴포넌트의 개수는 이보다 훨씬 많습니다.
옴퍼스 사냥 게임
: 매우 단순한 게임에서도 아키텍처 경계가 어디에나 존재한다는 사실을 보여준다.
옴퍼스 사냥게임 (https://www.youtube.com/watch?v=EkWJMU16GrU)
의존성 규칙 준수하기
UI 컴포넌트가 어떤 언어를 사용하더라도 게임 규칙을 재사용 할 수 있습니다.
게임 규칙은 어떤 종류의 인간 언어가 사용되는지 알지도 못할 뿐만 아니라 신경 쓸 이유도 없습니다.
의존성 규칙을 준수할 수 있도록 의존성이 적절한 방향을 가리키게 만들어야 합니다.
클린 아키텍처?
: 클린 아케텍처 접근법을 적용해보겠습니다.
단순화된 다이어그램
순전히 API 컴포넌트만 집중하면 다이어그램을 단순화 할 수 있습니다.
모든 화살표가 위로 향하도록 맞춰져 있습니다.
GameRules는 최상위 수준의 정책을 가지는 컴포넌트이므로 맨 위에 배치해 놨습니다.
모든 입력은 사용자로부터 전달받아 Text Delivery 컴포넌트로 전달되고 이 정보는 Language 컴포넌트를 거쳐서 위로 올라가며, GameRules에 적합한 명령어로 변역이 됩니다.
GameRules는 사용자 입력을 처리하고, 우측 하단의 DataStorage로 적절한 데이터를 내려 보냅니다. 그런 후 GameRules는 Language로 출력을 되돌려 보내고, Language는 API를 다시 적절한 언어로 번역한 후 번역된 언어를 TextDelivery를 통해 사용자에게 전달합니다.
흐름을 분리
이 구성은 데이터 흐름을 두개의 흐름으로 효과적으로 분리가 가능합니다.
왼쪽의 흐름은 사용자와의 통신에 관여하며, 오른쪽의 흐름은 데이터 영속성에 관여합니다. 두 흐름은 GameRules에서 서로 만나며, GameRules는 두 흐름이 모두 거치게 되는 데이터에 대한 최종적인 처리기가 됩니다.
흐름 횡단하기
: 데이터 흐름을 분리해보겠습니다.
만약에 옴퍼스 사냥게임을 네트워크상에서 여러 사람이 함께 플레이 할 수 있게 만든다고 한다면 아래 그림처럼 네트워크 컴포넌트를 추가해야 합니다.
Network 컴포넌트 추가
데이터 흐름을 세 개의 흐름으로 분리하며, 이들 흐름은 모두 GameRules가 제어합니다. 따라서 시스템이 복잡해질수록 컴포넌트 구조는 더 많은 흐름으로 분리될 것입니다.
단 200줄이면 구현 할 수 있는 터무니없이 간단한 프로그램을 가져와서, 이처럼 정신없는 아키텍처 경계를 모두 추론해 내는 이유는 무엇일까요?
아키텍처 경계가 어디에나 존재한다는 사실을 보여주기 위함이였습니다. 아키텍트로서 우리는 아키텍처 경계가 언제 필요한지를 신중하게 파악해내야 합니다. 또한 우리는 이러한 경계를 제대로 구현하려면 비용이 많이 든다는 사실도 인지하고 있어야 합니다.
매우 똑똑한 일부 사람들이 우리에게 수년 동안 말해왔듯이, 추상화가 필요하리라고 미리 예측해서는 안됩니다. 위에서 한번 설명 드렸던 YAGNI(you Aren’t Going to Need It-당신은 그것을 필요로하지 않을거야) 원칙을 한번 더 생각해봐야합니다. 오버엔지니어링이 언더엔지니어링보다 나쁠 때가 훨씬 많기 때문입니다.
계층과 경계의 결론은 프로젝트 초반에는 구현할 경계가 무엇인지와 무시할 경계가 무엇인지를 쉽게 결정할 수 없습니다. 대신 꾸준히 지켜보고 어렴풋한 경계의 첫 조짐을 신중하게 관찰해야 합니다. 첫 조짐이 보이는 시점이 되면, 해당 경계를 구현하는 비용과 무시할 때 감수할 비용을 가늠해보고 그 결정된 사항을 자주 검토해야합니다.
우리의 목표는 경계의 구현 비용이 그걸 무시해서 생기는 비용보다 적어지는 바로 그 변곡점에서 경계를 구현하는 것입니다.! 목표를 달성하려면 빈틈없이 지켜봐야 합니다.
— — —
메인(Main) 컴포넌트
모든 시스템에서는 최소한 하나의 컴포넌트가 존재하고, 이 컴포넌트가 나머지 컴포넌트를 생성하고, 조정하며, 관리한다. 이 컴포넌트를 책에서는 메인(Main)이라고 부릅니다.
메인 컴포넌트는 궁극적인 세부사항으로, 가장 낮은 수준의 정책입니다. 메인에 의존성이 일단 주입되고 나면, 메인은 의존성 주입 프레임워크를 사용하지 않고도 일반적인 방식으로 의존성을 분배할 수 있어야 합니다. 메인은 고수준의 시스템을 위한 모든 것을 로드 한 후 제어권을 고수준의 시스템에게 넘깁니다.
메인 컴포넌트
이미지 출처(https://www.freecodecamp.org/news/a-quick-introduction-to-clean-architecture-990c014448d2/)
메인(Main) 컴포넌트의 결론은 메인을 애플리케이션의 플러그인이라고 생각하고 메인은 초기 설정을 구성하고 외부 자원을 모두 수집한 후, 제어권을 애플리케이션의 고수준 정책으로 넘기는 플러그인이라고 생각하면 됩니다. 메인을 플러그인 컴포넌트로 여기고, 그래서 아키텍처 경계 바깥에 위치 한다고 보면 설정 관련 문제를 훨씬 쉽게 해결할 수 있습니다.
— — —
‘크고 작은 모든’ 서비스들
단순히 애플리케이션의 행위를 분리할 뿐인 서비스라면 값비싼 함수 호출에 불과하며, 아키텍처 관점에서 꼭 중요하다고 볼 수는 없습니다.
서비스는 시스템의 확장성과 개발 가능성 측면에서 유용하지만, 그 자체로는 아키텍처적으로 그리 중요한 요소는 아닙니다. 시스템의 구성 요소가 통신하고 실행되는 물리적인 매커니즘에 의해 아키텍처가 정의되는 것이 아닙니다.
‘크고 작은 모든’ 서비스들의 결론은 서비스는 단 하나의 아키텍처 경계로 둘러싸인 단인 컴포넌트로 만들 수 있고 혹은 여러 아키텍처 경계로 분리된 다수의 컴포넌트로 구성할 수도 있습니다.
이상! [클린아키텍처]의 5부 아키텍트의 역할에 대한 요약이었습니다.
😃 YAGNI를 기억해주세요 😃
Get to know us better! Join our official channels below.
Telegram(EN) : t.me/Humanscape KakaoTalk(KR) : open.kakao.com/o/gqbUQEM Website : humanscape.io Medium : medium.com/humanscape-ico Facebook : www.facebook.com/humanscape Twitter : twitter.com/Humanscape_io Reddit : https://www.reddit.com/r/Humanscape_official Bitcointalk announcement : https://bit.ly/2rVsP4T Email : support@humanscape.io