앞에 1탄에서는 아키텍처를 왜 배워야하는 지와 2탄에서 아키텍처는 무엇인지를 정리했다.
이제는 밥아저씨가 말한 클린 아키텍처가 무엇인지를 알아보고자 한다.
클린 아키텍처
지난 수년간 우리는 시스템 아키텍처와 관련된 여러 가지 아이디어를 봐왔다.
- 헥사고날 아키텍처 : https://mesh.dev/20210910-dev-notes-007-hexagonal-architecture/
- 오니온 아키텍처 : https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/
- 스크리밍 아키텍처 : https://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html
- DCI : https://en.wikipedia.org/wiki/Data,_context_and_interaction
- BCE : https://vaclavkosar.com/software/Boundary-Control-Entity-Architecture-The-Pattern-to-Structure-Your-Classes
위 아키텍처들은 모두 세부적인 면에서는 차이가 있더라도 내용은 비슷하다.
이들의 목표는 모두 같은데, 바로 관심사의 분리다. 위 아키텍처 모두 소프트웨어를 계층으로 분리함으로써 관심사의 분리라는 목표를 달성할 수 있었다.
각 아키텍처는 최소한 업무 규칙을 위한 계층 하나와 사용자와 시스템 인터페이스를 위한 또 다른 계층 하나를 반드시 포함한다.
그리고 이러한 여러 아키텍처는 다음과 같은 특징을 지니도록 만든다.
- 프레임워크 독립성 : 프레임워크 존재 여부에 의존하지 않는다. 이를 통해 이러한 프레임워크를 도구로 사용할 수 있으며, 프레임워크가 지닌 제약사항 안으로 시스템에 들어가도록 강제하지 않는다.
- 테스트 용이성 : 업무 규칙은 UI, DB, Web Server, 또는 여타 외부 요소가 없이도 테스트를 할 수 있다.
- UI 독립성 : 시스템의 나머지 부분을 변경하지 않고도 UI 를 쉽게 변경할 수 있다. [ex. 업무 규칙을 변경하지 않은 채 웹 UI 를 콘솔 UI 로 변경 가능]
- 데이터베이스 독립성 : 다양한 DB(oracle, mysql 등)로 교체할 수 있다. 업무 규칙이 데이터베이스에 결합되지 않는다.
- 모든 외부 에이전시에 대한 독립성 : 업무 규칙은 외부 세계와의 인터페이스에 대해 전혀 알지 못한다.
💡업무 규칙이란? 사업적으로 수익을 얻거나 비용을 줄일 수 있는 규칙 또는 그러한 절차이다. 엄밀히 말하자면 컴퓨터 상으로 구현 했는지와 상관없이 업무 규칙은 사업적으로 수익을 얻거나 비용을 줄일 수 있어야 한다.
예를 들어, 대출에 N% 의 이자를 부과한다는 사실은 은행이 돈을 버는 업무 규칙으로 이러한 부분을 컴퓨터 프로그램으로 구현을 하던, 직원이 직접 계산을 하던 상관없다. 이러한 규칙을 핵심 업무 규칙(Critical Business Rule) 이라고 한다.
위 그림은 마틴이 이들 아키텍처 전부를 단일 아이디어로 통합하길 원했다.
그래서 실행 가능한 하나의 아이디로 통합하려는 시도를 나타내는 다이어그램이다.
클릭 아키텍처 구조와 특징
아키텍처는 세부적인 차이는 있어도 공통적인 목표는 위에서 언급했듯 계층을 분리하여 관심사의 분리하는 것이라고 말하였다.
이런 아키텍처가 동작하기 위해서는 의존성 규칙을 지켜야 한다.
의존성 규칙
각각의 동심원은 소프트웨어에서 서로 다른영역을 표현한다는 것을 볼 수 있다.
보통 안으로 들어갈 수록 고수준의 소프트웨어가 된다.
바깥족 원은 메카니즘이고, 안쪽 원은 정책이다.
이러한 아키텍처가 동작하도록 하는 가장 중요한 규칙은 의존성 규칙이다.
소스코드 의존성은 반드시 안쪽으로, 고수준의 정책을 향해야 한다.
💡고수준이란? 상위 수준의 개념, 추상화된 개념으로 예를 들어 데이터를 저장한다. 구역 배달료를 구한다
💡저수준이란? 추상화된 개념을 실제 어떻게 구현할지에 대한 세부적인 개념 예를 들어 RDB에 데이터를 저장한다. 폴리곤 구역에 속한 배달 건에 대해서 배달료를 구한다.
내부의 원에 속한 요소는 외부의 원에 속한 어떤 것도 알지 못한다. 특히 내부의 원에 속한 코드는 외부의 원에 선언된 어떤 것에 대해서도 그 이름을 언급해서는 절대 안된다. 여기에는 함수, 클래스, 변수 그리고 소프트웨어 엔티티로 명명되는 모든 것이 포함된다.
같은 이유로, 외부의 원에 선언된 데이터 형식도 내부의 원에서 절대로 사용해서는 안 된다. 특히 그 데이터 형식이 외부의 원에 있는 프레임워크가 생성한 것이라면 더더욱 사용해서는 안 된다. 우리는 외부 원에 위치한 어떤 것도 내부의 원에 영향을 주지 않기를 바란다.
엔티티
이는 전사적인 핵심 업무 규칙을 캡슐화한다.
- 앤티티는 메서드를 가지는 객체이거나 일련의 데이터 구조와 함수의 집합일 수도 있다.
- 기업의 다양한 애플리케이션에서 엔티티를 재사용할 수만 있다면, 그 형태는 그다지 중요하지 않다.
- 전사적이지 않은 단순한 단일 애플리케이션을 작성하고 있다면 엔티티는 해당 애플리케이션의 업무 객체가 된다. 이 경우 엔티티는 가장 일반적이고 고수준인 규칙을 캡슐화 한다.
- 외부의 무언가가 변경되더라도 엔티티가 변경될 가능성은 지극히 낮다. 예를 들어 페이지 네이게이션이나 보안과 관련된 변경이 발생하더라도 업무 객체가 영향을 받지 않을 것이다.
- 운영 관점에서 특정 애플리케이션에 무언가 변경이 필요하더라도 엔티티 계층에는 절대로 영향을 주어서는 안된다.
유스케이스
애플리케이션에 특화된 업무 규칙을 포함한다.
쉽게 말해서 "시스템의 동작을 사용자 입장에서 표현한 시나리오" 라고 생각하면 된다.
또한 유스케이스 계층의 소프트웨어는 시스템의 모든 유스케이스를 캡슐화하고 구현한다.
- 유스케이스는 엔티티로 들어오고 나가는 데이터 흐름을 조정한다.
- 엔티티가 자신의 핵심 업무 규칙을 사용해서 유스케이스의 목적을 달성하도록 이끈다
- 유스케이스 계층에서 발생한 변경이 엔티티에 영향을 줘서는 안된다.
- 또한 데이터 베이스, UI, 또는 여타 공통 프레임워크와 같은 외부 요소에서 발생한 변경이 유스케이스 계층에서 영향을 줘서도 안된다.
- 유스케이스 계층은 이러한 관심사로부터 격리 되어 있다.
하지만 운영 관점에서 애플리케이션이 변경된다면 유스케이스가 영향을 받으며, 따라서 이 계층의 소프트웨어에도 영향을 줄 것이다.
유스케이스의 세부사항이 변하면 이 계층의 코드 일부는 분명 영향을 받을 것이다.
인터페이스 어댑터 [ = Presentation Layer]
MVC 아키텍처를 모두 포괄한다. Presenter, View, Controller 는 모두 인터페이스 어댑터 계층에 속한다
모델은 그저 데이터 구조 정도에 지나지 않으며,
컨트롤러에서 유스케이스로 전달되고, 다시 유스케이스에서 프레젠터와 뷰로 되돌아간다.
해당 계층 안에서 어떤 코드도 데이터베이스에 대해 조금도 알아서는 안 된다.
예컨대 SQL 기반의 데이터베이스를 사용한다면 모든 SQL은 이 계층을 벗어나서는 안된다.
또한 이 계층에는 데이터를 외부 서비스와 같은 외부적인 형식에서 유스케이스나 엔티티에서 사용되는 내부적인 형식으로 변환하는 또 다른 어댑터가 필요하다.
해당 계층은 MVP의 Presenter, MVVM의 ViewModel 및 게이트웨이(=Repository) 가 포함된다. 즉 순수한 비즈니스 로직만을 담당하는 역할을 한다.
프레임워크와 드라이버
가장 바깥쪽 계층은 일반적으로 데이터나 웹 프레임워크 같은 프레임워크 도구들로 구성된다.
일반적으로 이 계층에서는 안쪽 원과 통신하기 위한 집합 코드 외에도 특별히 더 작성해야 할 코드가 그다지 많지 않다.
프레임워크와 드라이버 계층은 모든 세부사항이 위치하는 곳으로 웹, 데이터베이스 모두 세부사항이다.
우리는 이러한 것들을 모두 외부에 위치시켜 피해를 최소화한다.
원은 네 개여야만 할까?
그렇지 않다. 하지만 어떤 경우에도 의존성 규칙은 적용된다.
소스 코드 의존성은 항상 안쪽으로 향한다.
안쪽으로 이동할수록 추상화와 정책의 수준은 높아진다.
그리고 안쪽으로 이동할수록 소프트웨어는 점점 추상화되고 더 높은 수준의 정책들을 캡슐화한다.
경계 횡단하기
위 사진을보면 Controller[Interface Adatper] > UserCase > Presenter[Interface Adatper] 로 선이 이동하는 것이 보인다.
그럼 결국 UseCase 가 Presenter 를 호출할 수도 있다.
하지만, 우린 위에서 의존성 규칙에 따라 소스코드 종속성은 안쪽으로만 향할 수 있다고 했다.
즉, 안쪽 원은 밖에있는 원을 모른다.
그런데? UseCase 가 Presenter 를 호출한다는 건 UseCase 가 Presenter 에 대해 안다는 소리다.
그런데 그러면 안된다.
그래서 UseCase Output Port 에 있는 인터페이스를 호출한다. 그리고 Presenter 는 이 Output port 인터페이스를 구현한다.
직접 Presenter 의 구현체에 접근 안하고 인터페이스를 통해 접근한다는 것이 가장 중요하다.
정리
밥 아저씨는 여러 아키텍처를 통합하여 단일 통합 아키텍처를 구성하려 했다.
그것이 클린 아키텍처라 불리우고 위에 설명한 구조와 특징을 가지고 있다.
밥 아저씨는 경계에 대해서 아래와 같은 말을 했다.
소프트웨어 아키텍처는 선을 긋는 기술이며, 나는 이러한 선을 경계(boundary)라고 부른다.
경계는 소프트웨어 요소를 서로 분리하고, 경계 한편에 있는 요소가 반대편에 있는 요소을 알지 못하도록 막는다. - Robert C. Martin, Clean Architecture
경계를 나눠 위에서 제시한 다이어그램 토대로 관심사를 분리하는 방법을 클린 아키텍처라 한다.
'Mobile App' 카테고리의 다른 글
[안드로이드] - DiffUtil 이란? (+AsyncListDiffer) (0) | 2023.02.02 |
---|---|
[안드로이드] - 클린아키텍처 - 3 (0) | 2023.02.01 |
[안드로이드] - 클린아키텍처 - 1 (0) | 2023.01.29 |
[안드로이드] - 클린아키텍처 - 0 (0) | 2023.01.27 |
[안드로이드] - ShardPreferences, Datastore 차이 (0) | 2023.01.27 |