Clean Architecture

Clean Architecture - Intro

kahnco 2022. 12. 28. 16:01

본 글은, Uncle Bob's Clean Architecture 내용을 해석한 글입니다.

 

그림 1

세상에는 다양한 아키텍처가 존재하는데, 그러한 아키텍처들은 모두 세부 사항이 다소 다르지만 매우 유사합니다. 그들은 모두 관심(저는 책임이라고 명하고 싶습니다)의 분리라는 동일한 목표를 가지고 있습니다. 그들은 모두 소프트웨어를 계층으로 나누어 이러한 분리를 달성합니다. 각각에는 비즈니스 규칙(1)에 대한 계층과 인터페이스에 대한 계층이 하나 이상 있습니다.

 

이러한 각 아키텍처는 다음과 같은 시스템을 생성합니다.

 

1. 프레임워크와 독립적입니다. 아키텍처는 일부 기능이 포함된 소프트웨어 라이브러리의 존재에 의존하지 않습니다. 이를 통해 시스템을 제한된 제약 조건에 집어넣지 않고 이러한 프레임워크를 도구로 사용할 수 있습니다.

2. 테스트가 용이합니다. 비즈니스 규칙은 UI, 데이터베이스, 웹 서버 또는 기타 외부 요소 없이 테스트할 수 있습니다.

3. UI와 독립적입니다. 시스템의 나머지 부분을 변경하지 않고도 UI를 쉽게 변경할 수 있습니다.

4. 데이터베이스와 독립적입니다. 비즈니스 규칙은 데이터베이스에 바인딩되지 않습니다.

5. 외부 모듈 혹은 기관과 독립적입니다. 비즈니스 규칙은 외부 세계에 대해 전혀 알지 못합니다.

 

그림 1은 이러한 모든 아키텍처를 실행 가능한 단일 아이디어로 통합하려는 시도입니다.

 


The Dependency Rule

 

각각의 원들의 영역은 소프트웨어의 다양한 영역을 나타냅니다. 일반적으로 더 깊이 들어갈수록 소프트웨어의 수준이 높아집니다. 외부 원은 메커니즘입니다. 내부 원은 정책입니다.

 

이 아키텍처가 작동하도록 만드는 최우선 규칙은 The Dependency Rule 입니다. 이 규칙에 따르면 소스 코드 종속성은 그 방향성이 내부로만 향할 수 있습니다. 내부 원의 어떤 것도 외부 원에 대해서 전혀 알 수 없습니다. 특히 외부 원에서 선언된 이름은 내부 원의 코드에서 언급되어서는 안 됩니다. 여기에는 함수, 클래스, 변수 혹은 기타 선언된 소프트웨어 엔티티도 포함됩니다.

 

마찬가지로 외부 원에서 사용되는 데이터 형식은 내부 원에서 사용하면 안 됩니다. 특히 이러한 형식이 외부 원의 프레임워크에서 생성되는 경우에는 더욱 그렇습니다. 우리는 외부 원의 어떤 것도 내부 원에 영향을 미치기를 원하지 않습니다.

 


Entity

 

엔티티는 enterprise wide 비즈니스 규칙을 캡슐화합니다. 엔티티는 메서드가 있는 객체이거나 데이터 구조 및 함수 집합일 수 있습니다. 엔티티가 엔터프라이즈의 여러 다른 응용 프로그램에서 사용될 수 있는 한 문제가 되지 않습니다.

 

엔터프라이즈가 없고 단일 애플리케이션을 구현하는 경우 이러한 엔티티는 애플리케이션의 비즈니스 개체입니다. 가장 일반적이고 높은 수준의 규칙을 캡슐화합니다. 그들은 외부의 어떤 것이 변할 때, 변할 가능성이 가장 적습니다. 예를 들어 이러한 개체가 페이지 탐색 또는 보안 변경에 의해 영향을 받지 않을 것이라고 예상할 수 있습니다. 특정 애플리케이션에 대한 운영상의 변경은 엔티티 계층에 영향을 미치지 않아야 합니다.

 


Use Case

 

이 계층의 소프트웨어에는 애플리케이션 별 비즈니스 규칙이 포함되어 있습니다. 시스템의 모든 usecase를 캡슐화하고 구현합니다. 이러한 usecase는 엔티티 간 데이터 흐름을 조율하고, 해당 엔티티가 enterprise wide 비즈니스 규칙을 사용하여 usecase의 목표를 달성하도록 지시합니다.

 

이 레이어의 변경 사항이 엔티티에 영향을 미치지 않을 것으로 예상합니다. 또한 이 계층이 데이터베이스, UI 또는 공통 프레임워크와 같은 외부 요소의 변경에 의해 영향을 받지 않을 것으로 예상합니다. 이 계층은 이러한 우려로부터 격리되어 있습니다.

 

그러나 우리는 애플리케이션 작동에 대한 변경 사항이 usecase와 이 계층의 소프트웨어에 영향을 미칠 것으로 예상합니다. 사용 사례의 세부 사항이 변경되면 이 계층의 일부 코드가 확실히 영향을 받습니다.

 


Interface Adapter

 

이 계층의 소프트웨어는 usecase 및 엔티티에 가장 편리한 형식에서부터, 데이터베이스 또는 웹과 같은 일부 외부 에이전시에 가장 편리한 형식으로 데이터를 변환하는 어뎁터 집합입니다. 예를 들어 GUI의 MVC 아키텍처를 완전히 포함하는 것은 이 계층입니다. Presenter, View, Controller가 모두 여기에 속합니다. Model은 Controller에서 UseCase로 전달된 다음, UseCase에서 Presenter 및 View로 다시 전달되는 데이터 구조일 가능성이 높습니다.

 

마찬가지로 데이터는 이 계층에서 Entity 및 UseCase에 가장 편리한 형식에서, 사용 중인 지속성 프레임워크(데이터베이스)에 가장 편리한 형식으로 변환됩니다. 이 원 안에 있는 코드는 데이터베이스에 대해 전혀 알지 못합니다. 데이터베이스가 SQL 데이터베이스인 경우 모든 SQL은 이 계층, 특히 데이터베이스와 관련이 있는 이 계층 부분으로 제한되어야합니다.

 

또한 이 계층에는 외부 서비스와 같은 일부 외부 형식의 데이터를 UseCase 및 Entity에서 사용하는 내부 형식으로 변환하는데 필요한 다른 어뎁터가 있습니다.

 


Framework & Driver

 

가장 바깥쪽 레이어는 일반적으로 데이터베이스, 웹 프레임워크 등과 같은 프레임워크와 도구로 구성됩니다. 일반적으로 이 레이어에는 내부의 다음 원과 통신하는 글루 코드 외에 많은 코드를 작성하지 않습니다.

 

이 레이어는 모든 세부 사항이 있는 곳입니다. 웹은 세부 사항입니다. 데이터베이스 또한 세부 사항입니다. 우리는 이러한 것들을 거의 해를 끼치지 않는 외부에 보관합니다.

 


Only Four Circles?

 

아닙니다. 원은 대략적인 개념입니다. 항상 이 4개만 있어야 한다는 규칙은 없습니다. 그러나 Dependency Rule은 항상 적용됩니다. 소스 코드 종속성은 항상 안쪽을 가리킵니다. 안쪽으로 이동하면 추상화 수준이 높아집니다. 가장 바깥쪽 원은 낮은 수준의 Concrete Detail입니다. 내부로 이동함에 따라 소프트웨어는 더욱 추상화되고 더 높은 수준의 정책을 캡슐화합니다. 가장 안쪽 원이 가장 일반적인 원입니다.

 


Crossing boundaries

 

그림1의 오른쪽 하단에는 원 경계를 어떻게 교차하는지에 대한 예시가 있습니다. 다음 계층에서 UseCase와 통신하는 Controller 및 Presenter를 보여줍니다. Flow of Control에 유의하세요. Controller에서 시작하여 UseCase를 통해 이동한 다음, Presenter에서 실행됩니다. 소스 코드 종속성에도 유의하세요. 그들 각각은 UseCase의 내부를 가리킵니다.

 

우리는 일반적으로 종속성 역전 원칙(2)을 사용하여 이러한 명백한 모순을 해결합니다. 예를 들어 Java와 같은 언어에서는 소스 코드 종속성이 경계를 넘어 올바른 지점에서 제어 흐름에 반대하도록 인터페이스와 상속 관계를 배열합니다.

 

예를 들어, Usecase에서 Presenter를 호출해야 한다고 가정합니다. 그러나 이 호출은 종속성 규칙을 위반하므로 직접 호출해서는 안됩니다. 외부 원의 이름은 내부 원에서 언급할 수 없습니다. 따라서 우리는 Usecase가 내부 원에서 인터페이스(그림1에서는 Usecase 출력 포트로 표시됨)를 호출하고, 외부 원의 발표자가 이를 구현하도록 합니다.

 

동일한 기술이 아키텍처의 모든 경계를 교차하는데 사용됩니다. 우리는 동적 다형성(3)을 활용하여 제어 흐름에 반대되는 소스 코드 종속성을 생성하므로 제어 흐름이 어떤 방향으로 진행되든 종속성 규칙을 준수할 수 있습니다.

 


What data crosses the boundaries.

 

일반적으로 경계를 넘는 데이터는 단순한 데이터 구조입니다. 원하는 경우 기본 구조체 또는 간단한 데이터 전송 객체를 사용할 수 있습니다. 또는 단순히 함수 호출의 파라미터일 수 있습니다. 또는 해시맵으로 압축하거나 개체로 구성할 수 있습니다. 중요한 것은 격리되고 단순한 데이터 구조가 경계를 넘어 전달된다는 것입니다. 우리는 Entity 또는 데이터베이스 값을 속이고 전달하고 싶지 않습니다. 우리는 데이터 구조가 종속성 규칙을 위반하는 어떤 종류의 종속성도 가지기를 원하지 않습니다.

 

예를 들어 많은 데이터베이스 프레임워크는 쿼리에 대한 응답으로 편리한 데이터 형식을 반환합니다. 이것은 RowStructure이라고 부를 수 있습니다. 경계를 넘어 안쪽으로 해당 행 구조를 전달하고 싶지 않습니다. 이는 내부 원이 외부 원에 대해 알도록 강제하기 때문에 종속성 규칙을 위반하는 것입니다.

 

따라서 경계를 넘어 데이터를 전달할 때는 항상 내부 원에 편리한 형식으로 해야합니다.

 


  1. 비즈니스 규칙 : 엔티티, 속성, 관계 및 제약 조건을 정의합니다. 그들은 이해하기 쉽고 간단해야하며, 모든 사람들이 비슷한 해석을 할 수 있도록 최대한 넓게 유지합니다. 또한 서면으로 유지 관리 할 수 있어야합니다.
  2. 종속성 역전 원칙 : 객체 지향 설계에서 소프트웨어 모듈을 느슨하게 결합하기 위한 방법론입니다. 이 원칙을 따를 때, 상위 수준의 정책 설정 모듈에서 하위 수준의 종속성 모듈로 설정된 기존 종속성 관계가 역전되어 하위 수준 모듈 구현 세부 사항과 독립적으로 상위 수준 모듈을 렌더링합니다. 기존의 종속성 원칙 상태는 다음과 같습니다.
    1. 고수준 모듈은 저수준 모듈에서 아무것도 가져오면 안 됩니다. 둘다 추상화에 의존해야 합니다.
    2. 추상화 는 세부 사항에 의존해서는 안 됩니다. 세부사항은 추상화에 의존해야 합니다. 
  3. 동적 다형성 : 컴파일 시점에서는 부모 혹은 인터페이스를 참조하지만, 런타임 시점에서는 객체 타입(구현 클래스 혹은 자식 클래스)을 기준으로 실행될 함수를 호출하는 것을 의미합니다.
반응형