우찬쓰 개발블로그

클린 아키텍처 (원본 블로그 번역) 본문

안드로이드

클린 아키텍처 (원본 블로그 번역)

이우찬 2022. 9. 7. 17:48
반응형

* 엉클 밥 블로그 포스팅을 공부겸 직접 번역해본 글입니다. 원본 블로그 링크
(오역은 댓글로 알려주세요!)

 

Clean Coder Blog

The Clean Architecture 13 August 2012 Over the last several years we’ve seen a whole range of ideas regarding the architecture of systems. These include: Though these architectures all vary somewhat in their details, they are very similar. They all have

blog.cleancoder.com

지난 몇년동안 우리는 시스템 아키텍처에 대한 많은 범위의 아이디어를 보아왔습니다. 이 아키텍처들은 다음의 것들을 포함합니다.

이 아키텍처들은 세부적인 부분에 있어서는 약간씩 다르지만, 모두 굉장히 비슷합니다. 이 아키텍처들은 모두 다 관심의 분리라는 같은 목표를 가지고 있으며, 소프트웨어를 레이어로 나눔으로써 분리를 달성합니다. 각각은 적어도 하나의 비지니스 규칙을 위한 레이어와 인터페이스들을 위한 레이어를 가지고 있습니다.

 

이러한 아키텍처들은 아래의 시스템들을 제공합니다.

  1. 프레임워크들의 독립. 아키텍처는 어떠한 기능이 많은 소프트웨어 라이브러리의 존재에 의존하지 않습니다. 이 점은 당신의 시스템이 프레임워크들의 제한된 제약에 빠지기 보다 프레임워크들을 도구로써 사용하도록 해줍니다.
  2. 테스트 가능. 비지니스 규칙들을 UI, 데이터베이스, 웹 서버등의 외부 요소와 관계없이 테스트 가능하도록 해줍니다.
  3. UI로부터의 독립. UI는 다른 부분의 시스템과 관계없이 쉽게 변화할 수 있습니다. 예를들면, 웹 UI는 비지니스 규칙의 변경없이 콘솔 UI로 대체 될 수도 있습니다.
  4. 데이터베이스의 독립. 당신은 오라클이나 SQL 서버를 몽고DB, BigTable, CouchDB등으로 교체할 수 있습니다. 당신의 비지니스 규칙은 데이터베이스에 묶여있지 않습니다.
  5. 어떠한 외부 에이전시로 부터의 독립. 사실상 당신의 비지니스 규칙은 모든 외부 세상에 대해 아무것도 모릅니다.

이 글의 최상단의 있는 다이어그램 이미지는 이러한 모든 아키텍처들을 하나의 실행가능한 아이디어로 통합하기 위한 시도입니다.

 

의존성 규칙

저 동심원들은 소프트웨어의 다른 영역들을 의미합니다. 일반적으로 더 깊이 들어갈수록 높은 레벨의 소프트웨어가 됩니다. 외부 원은 메커니즘이고 내부 원은 정책입니다.

 

이 아키텍처가 동작하도록 하는 최우선 규칙은 의존성 규칙입니다. 이 규칙은 소스코드 의존성들은 내부만을 향한다는 것입니다. 내부에 있는 원들은 외부 원의 어떠한 것도 알지 못합니다. 특히 외부 원 안의 선언된 어떠한 이름도 내부 원의 코드의 이름에 의해 언급되어서는 안됩니다. 여기에는 함수, 클래스, 변수, 어떠한 명명된 소프트웨어 실체도 포함됩니다.

 

같은 이유로, 외부 원에서 사용되는 데이터 포멧은 내부 원에서 사용되서는 안되며, 특히 이 데이터 포멧들이 외부 원 안에있는 프레임워크에서 생성된 경우에도 그렇습니다. 우리는 외부의 원이 내부 원에 영향을 주길 원치 않습니다.

 

엔티티

엔티티는 기업적인 비지니스 규칙을 캡슐화합니다. 하나의 엔티티는 여러 메소드들과 함께 객체가 될 수 있고, 또는 데이터 구조와 함수들의 세트가 될 수도 있습니다. 이것은 엔티티가 기업의 여러 다른 어플리케이션에 사용될 수만 있다면 상관없습니다.

 

당신이 기업을 가진것이 아니라 하나의 어플리케이션을 작성하고 있다면, 이러한 엔티티들은 그 어플리케이션의 비지니스 객체입니다. 이 객체들은 가장 일반적이고 높은 레벨인 규칙들을 캡슐화합니다. 이것들은 외부적 변경사항이 있을때 변경될 가능성이 가장 작습니다. 예를들면, 당신은 페이지 네비게이션이나 보안적 변경에 의해 이러한 객체들이 변경되길 원치 않을 것입니다. 어떠한 특정 어플리케이션의 운영적인 변경사항도 엔티티 레이어에 영향을 주어서는 안됩니다.

 

유즈케이스

유즈케이스 레이어 안에 있는 소프트웨어는 어플리케이션에 특정된 비지니스 규칙입니다. 이것은 모든 시스템의 유즈 케이스들을 캡슐화하고 구현합니다. 이러한 유즈케이스들은 엔티티에 오고가는 데이터 흐름을 조정하고 엔티티들이 유즈 케이스의 목표를 이루기 위해 전사적인 비지니스 규칙을 사용하도록 지시합니다.

 

우리는 유즈케이스 레이어의 변경사항들이 엔티티에 영향을 미치리라고 기대하지 않습니다. 또한 우리는 유즈케이스 레이어가 데이터베이스나 UI등의 어떠한 일반적 외부 프레임워크의 변경사항으로 부터 영향을 받을것을 기대하지 않습니다. 유즈케이스 레이어는 이러한 우려로 부터 분리되어 있습니다.

 

그러나 우리는 어플리케이션의 작동 변경사항이 유즈케이스와 그에따라 이 유즈케이스 레이어의 소프트웨어에 영향을 줄 것을 기대합니다. 만약 유즈케이스의 세부사항들이 변경된다면, 이 레이어의 몇몇 코드는 반드시 영향을 받을 것입니다.

 

인터페이스 어뎁터

인터페이스 어뎁터 레이어에 있는 소프트웨어는 데이터를 유즈케이스와 엔티티를 위한 가장 편리한 포멧으로 부터, 데이터베이스나 웹같은 외부 에이전시를 위한 가장 편리한 포멧으로 변경하는 어뎁터의 세트입니다. 예를들면 이 레이어는 GUI의 MVC 아키텍처를 완전히 포함합니다. Presenter나 View, Controller는 모두 여기에 포함됩니다. Model은 그저 컨트롤러에서 유즈케이스로 전달되는 데이터구조들일 것이고 이후에 유즈케이스로부터 Presenter나 View로 돌아갑니다.

 

이와 비슷하게, 인터페이스 어뎁터에서 데이터는 엔티티와 유즈케이스를 위한 가장 편리한 형태에서 어떤 지속성 프레임워크를 사용하든 가장 편리한 형태로 변경됩니다. 예를들면 데이터베이스가 있습니다. 이 원 안의 어떠한 코드도 데이터베이스에 대해서 알면 안됩니다. 만약 데이터베이스가 SQL 데이터베이스라면 모든 SQL은 이 레이어에 한정되어야 하며, 특히 이 레이어의 데이터베이스에 관련이 있는 일부분에서만으로 한정되어야 합니다.

 

또한 이 레이어 안에는 유즈케이스나 엔티티에 의해 사용되는 데이터를 외부서비스 같은 어떠한 외부 형태로부터 내부 형태로 바꿔주는 중요한 어뎁터들도 있습니다.

 

프레임워크와 드라이버

가장 바깥의 레이어는 일반적으로 프레임워크와 데이터베이스나 웹 프레임워크 같은 도구들의 구성입니다. 일반적으로 당신은 다음 내부 원과 소통하기 위한 연결 코드를 제외하고는 많은 코드를 작성하지 않습니다.

 

이 레이어는 모든 세부사항이 들어가는 곳입니다. 웹은 세부사항입니다. 데이터베이스도 세부사항입니다. 우리는 이러한 것들이 적은 악영향을 주도록 외부에 둡니다.

 

네개의 원뿐인가요?

아닙니다. 이 원들은 대략적인 것입니다. 당신은 이 네개보다 더 필요할 수도 있습니다. 당신에게 이 네개의 원만 사용해야된다고 말하는 룰은 없습니다. 그러나 의존성 규칙은 항상 적용됩니다. 소스코드 의존성들은 항상 내부를 향해야 합니다. 당신이 내부로 향할때 추상화의 레벨은 증가합니다. 가장 바깥원은 구체적인 세부사항의 낮은 레벨입니다. 당신이 내부로 향할때 소프트웨어는 점점더 추상화되고 높은 레벨의 정책으로 캡슐화됩니다. 가장 내부 원은 가장 일반적인 것들 입니다.

 

경계를 넘어.

저 다이어그램의 오른쪽 아래에는 우리가 어떻게 원의 경계를 넘어야 하는가의 예시가 있습니다. 이것은 Controller와 Presenter가 다음레이어 안에있는 유즈케이스와의 소통하는 것을 보여줍니다. Flow of control에 주목하세요. 이것은 컨트롤에서 시작되어 유즈케이스를 통해 이동하고 Presenter에서 실행을 종료합니다. 소스 코드 의존성들 또한 주목하세요. 이 의존성의 각각은 유즈케이스로 향하는 내부를 가리킵니다.

 

우리는 주로 의존성 역전 원칙을 이용함으로써 이 명백한 모순을 해결합니다. 자바같은 언어를 예로들면, 우리는 소스코드의 의존성이 경계를 가로지르는 적당한 지점에서 제어의 흐름에 반대되도록 인터페이스와 상관 관계를 정렬합니다.

 

예를 들면, 어떤 유즈케이스가 Presenter를 콜해야 한다고 생각해봅시다. 그러나 이 콜은 의존성 규칙(외부 원에 있는 이름은 내부 원에 의해 언급될 수 없다.)을 위반하기 때문에 직접적이 되어서는 안됩니다. 그래서 우리는 내부 원 안의 인터페이스(여기서 Use Case Output Port로 보여지는)를 호출하는 유스케이스가 있고, 이 인터페이스를 구현하는 외부 원 안에 있는 Presenter를 가집니다.

 

같은 테크닉이 아키텍처 안의 모든 경계에 사용됩니다. 우리는 동적 다형성의 장점을 활용하여 제어의 흐름에 반대되는 소스 코드 의존성을 생성합니다. 그럼으로써 우리는 제어의 흐름의 방향과 관계없이 의존성 규칙을 따를 수 있습니다.

 

어떠한 데이터가 경계를 넘는가

전형적으로 경계를 넘는 데이터는 간단한 데이터 구조 입니다. 당신이 원한다면 기본적인 구조체 혹은 간단한 DTO(Data Transfer Object)를 사용할 수 있습니다. 혹은 그 데이터는 간단히 함수 호출 인수가 될 수도 있습니다. 혹은 당신이 이것을 해쉬맵으로 감싸도 되고, 혹은 객체로 작성해도 됩니다. 중요한 것은 격리되어있고 단순한 데이터 구조들은 경계를 넘어 전달 된다는 것입니다. 우리는 치트를 쓰거나 엔티티나 데이터베이스의 row를 넘기고 싶지 않습니다. 우리는 데이터 구조가 어떠한 의존성 규칙을 위배한 의존성도 갖게하고 싶지 않습니다.

 

예를들면, 많은 데이터베이스 프레임워크들은 쿼리에 대한 응답으로 편리한 데이터 포멧을 반환합니다. 우리는 이것을 행구조(RowStructure)라고 부를 수 있습니다. 우리는 그 행 구조를 경계를 넘어 안쪽으로 전달하고 싶지 않습니다. 이것은 내부 원이 외부 원에 대해 무언가 알도록 강제 할 수 있기 때문에 의존성 원칙을 위반할 수 있습니다.

 

그래서 우리가 경계를 넘어 데이터를 전달할때는 내부 원을 위한 가장 편리한 형태안에 있어야 합니다.

 

결론

이러한 간단한 규칙을 따르는 것은 어렵지 않고 당신의 앞으로의 많은 두통을 줄여줄 것입니다. 소프트웨어를 레이어로 분리하는 것과 의존성 규칙을 따르는 것을 통해 당신은 본질적으로 테스트 가능하고 의미하는 모든 이점을 가진 시스템을 만들게 될 것입니다. 데이터베이스나 웹 프레임워크 같이 어떠한 외부적 시스템 요소가 노후화 되었을때, 당신은 최소의 노력으로 이러한 것들을 교체할 수 있습니다.




Layer 구분

https://velog.io/@sery270/Android%EC%9D%98-Clean-Architecture%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-n9ihbaj4

 

 

 

Clean Architecture for Android with Kotlin

https://vrgsoft.net/blog/clean-architecture-for-android/

 

 

반응형
Comments