안녕하세요 오늘은 Clean Architecture에 대해 정리해보려고 합니다.
애플리케이션 같은 경우에는 수많은 기능들이 있기에 복잡도가 굉장히 높습니다. 복잡도가 높은 애플리케이션을 개발할 때 어떻게 하면 유지 보수하기 쉽고 고품질의 코드를 작성할 수 있을까요? 애플리케이션은 새로운 기능이 추가된다거나 내부 로직이 변경되어야 하는 일이 생겼을 때 유연하게 대처할 수 있도록 구조화해야 합니다. 클린 아키텍처의 목표는 계층을 분리하여 관심사를 분리하는 것입니다. 관심사를 분리하는 것이 무슨 의미가 있을까요?
예시를 한번 들어보겠습니다. 안드로이드 로컬 DB를 기존에 Room을 사용하고 있었습니다. Room으로 수많은 기능을 구현하고 이미 제품까지 나와 있는 상황입니다. 그런데 갑자기 로컬 DB를 Realm으로 교체해야 한다고 합니다. 이미 프로젝트 복잡도가 높아져서 Room에서 Realm으로 바꾸기엔 쉽지 않을 것 같습니다. Room 인스턴스를 전부 Realm 인스턴스로 변경한 뒤, 그에 맞게 또 대처를 해줘야 합니다. 클린 아키텍처로 프로젝트 구조를 잡는다면 변화에 유연하게 코드를 작성할 수 있습니다.
클린 아키텍처는 총 4가지의 계층으로 이루어져 있습니다.
엔티티는 비즈니스 규칙을 캡슐화합니다. 엔티티는 메서드를 갖는 객체일 수도 있지만 데이터 구조와 함수의 집합일 수도 있습니다. 가장 일반적이면서 고수준의 규칙을 캡슐화하게 됩니다. 외부가 변경되더라도 이러한 규칙이 변경될 가능성이 적습니다.
유스케이스는 애플리케이션의 고유 규칙을 캡슐화하며 엔티티로부터의 데이터 흐름을 조합합니다. 유스케이스 계층의 변경이 엔티티에 영향을 줘서는 안 되며 데이터베이스, 공통 프레임워크 및 UI에 대한 변경으로부터 격리됩니다.
인터페이스 어댑터는 Entity 및 UseCase의 편리한 형식에서 데이터베이스 및 웹에 적용할 수 있는 형식으로 변환합니다. 이 계층에는 MVP 패턴의 Presenter, MVVM 패턴의 ViewModel가 포함됩니다. 즉, 순수한 비즈니스 로직만을 담당하는 역할을 합니다.
프레임워크와 드라이버는 상세한 정보들을 두게 됩니다. 웹 프레임워크, 데이터베이스, UI, HTTP client 등으로 구성된 가장 바깥쪽 계층입니다.
클린 아키텍처가 동작하기 위해서는 의존성 규칙을 지켜야 합니다. 각각의 클래스는 한 가지 역할만 수행하고, 서로 의존 관계를 어떻게 할지 규칙이 정해져 있고 이를 지켜야 합니다.
의존성 규칙은 반드시 외부에서 내부로, 저수준 정책에서 고수준 정책으로 향해야 합니다. 위 그림에서는 내부로 갈수록 의존성이 낮아집니다. 예를 들면, 안드로이드에서 비즈니스 로직을 담당하는 ViewModel은 로컬 DB나 Web과 같은 세부적인 사항에 의존하지 않아야 합니다. 이를 통해 비즈니스 로직(고수준 정책)은 세부 사항(저수준 정책)의 변경에 영향받지 않도록 할 수 있습니다.
이렇게 관심사를 나누면 다음과 같은 이점을 얻을 수 있습니다.
클린 아키텍처를 안드로이드에 접목시킬 때는 일반적으로 Presentation, Domain, Data 총 3개의 계층으로 나눠지게 됩니다. Presentation -> Domain, Data -> Domain 방향으로 의존성을 갖고 있습니다.
화면과 입력에 대한 처리 등 UI와 관련된 부분을 담당합니다. Activity, Fragment, View, Presenter 및 ViewModel을 포함합니다. Presentation 계층은 Domain 계층에 대한 의존성을 가지고 있습니다.
애플리케이션의 비즈니스 로직에서 필요한 UseCase와 Model을 포함하고 있습니다. UseCase는 각 개별 기능 또는 비즈니스 논리 단위이며, Presentation, Data 계층에 대한 의존성을 가지지 않고 독립적으로 분리되어 있습니다. 안드로이드의 의존성을 갖지 않고 java 및 kotlin 코드로만 구성하며 다른 애플리케이션에서도 사용할 수 있습니다. Repository 인터페이스도 포함되어 있습니다.
Domain 계층에 의존성을 가지고 있습니다. Domain 계층의 Repository 구현체를 포함하고 있으며, 데이터베이스, 서버와의 통신도 Data 계층에서 이루어집니다. 또한 mapper 클래스를 통해 Data 계층의 모델을 Domain 계층의 모델로 변환해주는 역할도 하게 됩니다.