Flutter

Flutter Clean Architecture

kahnco 2022. 12. 28. 16:19

이 글은 Flutter Library 중에서, flutter_clean_architecture에 대해서 설명하는 글입니다.

 


소개

Uncle Bob의 책과 블로그를 기반으로 한 아키텍처입니다. Onion 아키텍처와 다른 아키텍처에서 가져온 개념의 조합입니다. 아키텍처의 주요 초점은 관심사의 분리와 확장성입니다. App, Domain, Data, Device 이렇게 네 가지 주요 모듈로 구성됩니다.


종속성 규칙

소스 코드 종속성은 그 방향이 내부로만 향합니다. 즉 내부 모듈은 외부 모듈을 인식하지도 종속되지도 않습니다. 그러나 외부 모듈은 내부 모듈을 인식하고 의존합니다. 외부 모듈은 비즈니스 규칙 및 정책(내부 모듈)이 작동하는 메커니즘을 나타냅니다. 더 많이 안으로 들어갈수록 더 많은 추상화가 나타납니다. 바깥쪽으로 이동할수록 더 구체적인(Concrete) 구현이 나타납니다. 내부 모듈은 외부 모듈에 있는 클래스, 함수, 이름, 라이브러리 등을 인식하지 못합니다. 그들은 단순히 규칙을 나타내며 구현과 완전히 독립적입니다.

 


레이어

 

DOMAIN

이 모듈은 애플리케이션의 Domain 비즈니스 로직을 정의합니다. 이것은 개발 플랫폼과 독립적인 모듈입니다. 즉, 순수하게 프로그래밍 언어(Dart)로 작성되고 플랫폼의 요소(Material, Cupertino 등등)를 포함하지 않습니다. 그 이유는 구현 세부 사항이 아니라 애플리케이션의 비즈니스 로직에만 관련되어야 하기 때문입니다. 또한 문제가 발생할 경우 플랫폼 간에 쉽게 마이그레이션 할 수 있습니다.

 

Domain 세부 내용

Domain 레이어는 가장 안쪽 레이어를 나타냅니다. 따라서 아키텍처에서 가장 추상적인 레이어입니다.

 

 

  1. Entity
    1. Enterprise Wide 비즈니스 규칙
    2. 메서드를 포함할 수 있는 클래스로 구성
    3. 애플리케이션의 비즈니스 객체
    4. 응용 프로그램 전체에서 사용됨
    5. 응용 프로그램의 내용이 변경될 때, 변경 가능성이 가장 낮음
  2. Usecase
    1. 애플리케이션별 비즈니스 규칙
    2. 애플리케이션의 모든 사용 사례 캡슐화
    3. 앱 전체에서 데이터 흐름 조정
    4. 어떠한 UI 변경에도 영향을 받지 않아야 합니다
    5. 애플리케이션의 기능 및 흐름이 변경되면 변경될 수 있습니다
  3. Repository
    1. 외부 레이어의 예상 기능을 정의하는 추상 클래스
    2. 외부 레이어를 인식하지 못하고 예상되는 기능만 정의 (ex. Login Usecase는 Repository에게 Login 기능을 기대합니다.)
    3. Usecase 외부 레이어에서 전달

 


APP

App은 Domain 바깥에 있는 레이어입니다. App은 Domain 레이어와 통신하기 위해서 레이어의 경계를 넘습니다. 그러나, 종속성 규칙은 위반되지 않습니다. polymorphism(다형성)을 사용해서, App은 상속 클래스 (Domain Layer의 Repository를 implement / extend한 클래스)를 활용해서 Domain 레이어와 통신합니다. polymorphism(다형성)를 사용했기 때문에, Domain에 전달된 리포지토리는 도메인에 관하여 추상적이기 때문에 여전히 종속성 규칙을 준수합니다. 실행은 다형성에 의해 숨겨져 있습니다.

 

App 세부 내용

App은 애플리케이션의 presentation 레이어이기 때문에, UI, UI 이벤트 핸들러 등등을 포함하고 있는 가장 프레임워크 의존적인 레이어입니다. 애플리케이션에 있는 모든 페이지를 위해서, App은 최소 3개의 클래스를 선언합니다: Controller, Presenter, View

 

  1. View
    1. 페이지의 UI를 대표합니다. View는 이벤트에 대한 제어를 Controller에게 의존하면서 페이지의 UI를 빌드하고 꾸밉니다. View는 Controller를 가집니다(has-a).
    2. View는 2개의 클래스로 구성됩니다.
      • View를 나타내는 Root Widget인 View를 Extend한 클래스
      • 다른 클래스와 해당 Controller의 템플렛 전문화를 사용하여 ViewState를 Extend한 클래스
    3. ViewState에는 기술적으로 UI 구현의 영역인 View Getter가 포함되어 있습니다
    4. StatefulWidget은 Flutter에 따른 State가 포함되어 있습니다.
    5. StatefulWidget은 제목 등의 인수를 다른 페이지에서 State로 전달하는 역할만 합니다.
    6. StatefulWidget은 ViewState만 인스턴스화하고, 필요한 Controller와 함께 Consumer를 통해 제공합니다.
    7. StatefulWidget은 Controller를 포함한 State Object(ViewState)를 가지고 있습니다. (has-a)
    8. 종합적으로, StatefulWidget과 State는 페이지의 View와 ViewState로 대변됩니다.
    9. ViewState 클래스는 해당 Scaffold에서 Key로써 사용할 수 있는 GlobalKey를 유지관리합니다.
    10. 만약 해당 GlobalKey가 사용된다면, Controller는 그 Key에 쉽게 접근할 수 있고 그것을 통해서 SnackBar 및 다른 Dialog를 표시할 수 있습니다. 이것은 유용하지만 선택 사항입니다.
  2. Controller
    1. 모든 ViewSate는 Controller를 가집니다(has-a). Controller는 ViewState의 필요한 멤버 데이터, 즉 동적 데이터를 제공합니다. 또한, Controller는 ViewState Widget의 이벤트 핸들러를 구현하지만 Widget 자체에 액세스할 수 없습니다. ViewState는 Controller를 사용하지만 Controller는 ViewState를 사용할 수 없습니다. ViewState가 Controller에서 핸들러를 호출하면 RefreshUI()를 호출하여 보기를 업데이트할 수 있습니다.
    2. 모든 Controller는 WidgetsBindingObserver를 implement하는, 라이브러리 기본 추상 Controller를 extend 합니다. 모든 Controller 클래스는 View의 수명 주기 이벤트를 처리하며 다음을 Override할 수 있습니다.
      • onInActive()
      • onPaused()
      • onResumed()
      • onDetached()
      • onDisposed()
      • onReassembled()
      • onDidChangeDependencies()
      • onInitState()
    3. 또한, 모든 Controller는 Consistency를 위해 Presenter의 Listener를 초기화하는 initListeners()를 구현해야 합니다.
    4. Controller에는 Presenter가 있습니다(has-a). Controller는 Repository를 Presenter에게 전달하고 나중에 Usecase와 통신합니다. 
    5. Controller는 앞서 언급한 대로 모든 성공 및 오류 이벤트에 대해 Presenter가 호출해야 하는 Listener를 지정합니다. 오직 Controller만 가장 바깥쪽 계층의 Data 또는 Device 모듈에서 Repository 인스턴스를 가져올 수 있습니다.
    6. Controller는 ViewState에 액세스할 수 있으며, RefreshUI()를 통해 ControlledWidget을 새로 고칠 수 있습니다.
  3. Presenter
    1. 모든 Controller에는 Presenter가 있습니다(has-a). Presenter는 App 레이어의 시작 부분에서 언급한 바와 같이 Usecase와 통신합니다. Presenter는 Controller에 의해 선택적으로 설정된 기능인 member를 가질 것이며, 데이터 전송 / 완료 / 오류 등을 Usecase에 설정할 경우 호출됩니다.
    2. Presenter는 두 개의 클래스로 구성되어 있습니다.
      • Presenter (LoginPresenter)
        • Controller에 의해 설정된 이벤트 핸들러를 포함합니다.
        • 사용할 Usecase가 포함되어 있습니다.
        • Observer 클래스와 적절한 인자로 usecase를 초기화 및 실행합니다. (ex. LoginPresenter with username and password)
      • Observer를 Implementation한 클래스
        • Presenter 클래스에 대한 참조가 있습니다. 이상적으로 이것은 inner class여야 하지만 아직 dart에서는 지원되지 않습니다.
        • 3개의 함수를 implement합니다.
          • onNext(T)
          • onComplete()
          • onError()
        • 이 3개의 함수들은 Usecase Output의 모든 케이스를 나타냅니다.
          • Usecase가 Object를 반환하면, onNext(T)로 처리될 것입니다.
          • error가 있다면 onError(e)로 처리될 것입니다.
          • 한번 완료되고 나서는 onComplete를 호출할 것입니다.
        • 그런 다음, 이 메소드들은 Controller가 설정한 해당 Presenter method를 호출합니다. 이렇게 하면 이벤트가 Controller로 전달되어 Controller가 데이터를 조작하고 ViewState를 업데이트할 수 있습니다.
  4. Extra
    1. Utility
    2. Constants
    3. Navigator

 

Data

애플리케이션의 Data 레이어를 나타냅니다. 가장 바깥쪽 계층의 일부인 Data 모듈은 데이터 검색을 담당합니다. 이것은 서버, 로컬 데이터베이스 또는 둘 다에 대한 API 호출의 형태일 수 있습니다.

 

Data 세부 내용

  1. Repository
    1. 모든 Repository는 Domain 레이어의 Repository를 implement 해야합니다.
    2. 다형성을 사용하면 Data 레이어의 이러한 Repository(Concrete)를 레이어 경계를 넘어 View에서 Controller 및 Presenter를 통해 Usecase로 전달할 수 있습니다.
    3. 데이터베이스에서 데이터를 검색하거나 다른 기능들을 합니다.
    4. 어떠한 API Call과 데이터 조작에 대한 책임을 가지고 있습니다.
  2. Model
    1. 플랫폼에 종속될 수 있는 Extra Member를 추가하는 Entity의 Extension 입니다.
    2. 예를 들어, 로컬 데이터베이스의 경우에서 Model은 isDeleted혹은 IsDirty 항목으로 나타날 수 있습니다.
    3. 도메인이 Implementation을 인식하지 않아야 하므로, 종속성 규칙을 위반할 수 있는 항목은 엔티티에 존재할 수 없습니다.
  3. Mapper
    1. Entity object를 Model 혹은 다른 부차적인 것에 매핑시킵니다.
    2. Entity 혹은 Model을 받아서 다른 클래스를 반환하는 static method가 있는 static class입니다.
    3. 오직 Model이 있어야지 필요한 개념입니다.
 

Device

가장 바깥에 있는 레이어의 파트로써, Device는 Android, iOS와 같은 플랫폼과 직접 통신합니다. Device는 GPS와 같은 네이티브 기능과 파일 시스템과 같은 플랫폼 자체에 존재하는 기타 기능을 담당합니다. Device는 모든 Native API를 호출합니다.

 

Contents of Data

  1. Device
    1. Data 레이어의 Repository와 마찬가지로 Device는 플랫폼의 특정 기능과 통신하는 클래스입니다.
    2. App 레이어와 Domain 레이어 사이에서 다형성을 사용하여 Repository가 계층의 경계를 통과하는 것과 동일한 방식으로 레이어를 통과합니다. 즉, Controller는 이를 Presenter에게 전달하고 Presenter는 Usecase에 다형성적으로 전달하여 추상 클래스로 수신합니다.
  2. Extra
    1. 필요하다면 Utility 클래스를 추가합니다.
    2. 필요하다면 Constants 클래스를 추가합니다.
반응형

'Flutter' 카테고리의 다른 글

In Flutter, which Empty Widget is most fastest?  (0) 2023.01.29
Flutter with ChannelTalk  (0) 2023.01.06
Flutter Build Flavor with Firebase (1)  (0) 2023.01.04
CheckBox Inner Color Not Changed  (0) 2022.12.28