Android App architecture: Domain layer

안드로이드 App architecture: Domain layer를 알아보겠습니다.



출처:
https://developer.android.com/topic/architecture/domain-layer


domain(도메인) layer(계층)는 optional(선택 사항) layer입니다. 이 layer는 UI layer와 data layer 사이에 존재합니다.




domain layer는 복잡한 business(사업) logic(논리)을 캡슐화하는 역할을 가집니다. 이 layer가 optional인 이유는 모든 앱에서 필요하지 않기 때문입니다. 당신은 domain layer가 필요할 때만 사용해야 합니다. 예를 들어, 복잡한 것을 다루고 싶거나 재사용성이 필요할 때 사용합니다.



domain layer는 아래의 이점을 제공해 줍니다.

• 코드의 중복을 피한다.

• domain layer에 사용된 classes(클래스)의 가독성을 높인다.

• 앱의 테스트를 쉽게 할 수 있게 해준다.

• 큰 덩치의 classes를 응답을 잘하게 잘게 나누어 준다.



domain layer에 있는 classes들은 간단하고 가볍게 유지해야 합니다. 각 각의 use case(유즈 케이스)는 하나의 기능만 수행해야 합니다. 그리고 mutable(변경 가능한) data(데이터)를 포함해서는 안 됩니다. mutable data는 UI나 data layers에서 다뤄야 합니다.





알림: 여기서 제안하는 추천과 방식은 앱을 여러 방면으로 확장 가능하게 만들고, 품질과 활동성을 높여줍니다. 그리고 테스트를 쉽게 할 수 있게 해줍니다. 그러나, 당신은 이것을 필요에 따라 지침으로써 적용해야 합니다.





⊙ Naming conventions in this guid; 이 지침에서의 작명 규칙:

이 지침에서는 use case를 그들이 관리하는 단위 역할 뒤에 붙입니다. 규칙은 다음과 같습니다.



verb in present tense + noun/what(optional) + UseCase.



예를 들면: FormatDateUseCase, LogOutUserUseCase, getLastestNewsWithAuthorsUseCase 또는 MakeLoginRequestUseCase.





⊙ Dependencies; 의존성:

보통의 앱 architecture(구조)에서 use case는 UI layer의 ViewModels(뷰 모델)와 data layer의 repositories(저장소) 사이에 존재합니다. 이 뜻은 use case classes가 repository classes에 의존한다는 것을 뜻합니다. 그리고 domain layer는 UI layer와 repositories가 통신하는 방법으로 UI layer와 통신합니다. Java(자바)의 callbacks(콜백)를 사용하거나 Kotlin(코틀린)의 coroutines(코루틴)을 사용합니다. 자세한 내용은 data layer를 참고하세요.



예를 들면, 당신의 앱에서 당신은 새로운 기사와 작성자 정보를 가져오고 싶을 수 있습니다 그리고 두 정보를 합치고 싶어 합니다.







use cases는 재사용 가능한 logic를 가지기 때문에 다른 use cases에서 사용될 수 있습니다. domain layer에서 다중 계층을 가지는 것은 흔한 일입니다. 예를 들면, 아래의 예시는 FormatDateUseCase를 사용하는 use case입니다. UI layer에서 time zone(시간대)를 표시해야 하는 여러 classes에서 사용됩니다.









⊙ Call use cases in Kotlin; Kotlin에서 use case 사용하기:

Kotlin에서 use case classes instances(인스턴스)를 callable(부를 수 있게) 만들어야 합니다. operator(오퍼레이터)라는 modifier(모디파이어)를 사용하여 operator fun invoke()로 만듭니다. 아래의 예를 보시죠.







위의 예시 FormatDateUseCase의 invoke() 함수는 class를 함수처럼 call 할 수 있게 해줍니다. invoke() 함수는 어떠한 제약 사항이 없습니다. 숫자를 parameters(파라미터)로 받거나 return(반환)을 아무 type(타입)으로 할 수 있습니다. 또한, invoke()를 override(오버라이드) 해서 class에서 여러 방법으로 사용할 수 있습니다. 위의 Use case를 아래에서 call 하는 예시입니다.









invoke() operator를 배우고 싶다면 Kotlin 문서를 보시기 바랍니다.





⊙ Lifecycle; 생애 주기:

Use cases는 자신의 lifecycle(생애 주기)이 없습니다. 대신에 그들을 사용하는 class에 scoped(스코프드) 됩니다. 이 뜻은 Use case는 UI layer에서 services(서비스), Application(애플리케이션) class에 이르기까지 사용될 수 있습니다. 왜냐하면 use cases는 mutable data가 포함되어 있지 않고, 사용될 때마다 새로운 instance를 생성하기 때문입니다.





⊙ Threading; 쓰레딩:

Domain layer의 Use cases는 반드시 main-safe(메인 세이프) 해야 합니다. 다시 말하자면, main thread에서 작동해도 안전해야 합니다. use case classes가 오랜 시간이 필요한 작업을 한다면, 적절한 thread로 옮겨야 합니다. 그러나 그전에 할 것은 이러한 작업이 use case가 아닌 다른 layer에서 하는 것이 적합한지 확인하는 것이 필요합니다. 고려할 사항은, data layer의 복잡한 작업을 재사용해야 하거나 caching(캐싱) 해야 하는 작업이 있습니다. 예를 들면, 큰 배열을 다루는 자원이 많이 사용되는 작업이고, 이 작업의 결과가 여러 화면에서 사용되어야 하거나 cached 되어야 한다면 domain layer보다는 data layer에 있는 것이 좋습니다.



아래의 예시는 use case가 backgroud(백그라운드)에서 작업하는 내용입니다.







⊙ Common tasks; 일반적인 작업:

여기서는 일반적인 domain layer의 작업을 설명합니다.



• Reusable simple business logic; 재사용 가능한 간단한 business logic:

당신은 UI layer에 있는 반복 가능한 business logic을 use case class에 캡슐화할 수 있습니다. 이것은 logic이 사용되는 모든 곳의 변경을 쉽게 해줍니다. 그리고 logic을 따로 격리시켜 테스트할 수 있게 해줍니다.



앞에서 언급한 FormatDateUseCase를 예를 들면, data 형식을 변경하게 된다면 해당 data를 통제하는 UseCase에서 변경해 주면 끝입니다.



- 메모: 몇몇의 경우 use cases에 있는 logic은 Util의 static method에 존재할 수 있습니다. 그러나, Util에 존재하는 것은 장려하지 않습니다. 왜냐하면 Util classes는 찾기가 힘들고 그들의 기능을 한눈에 알아보기가 힘듭니다. 게다가 use cases는 기본 classes의 threading과 error handling(오류 핸들링)을 공유할 수 있습니다. 이것은 큰 규모의 팀에 이점을 줍니다.







• Combine repositories; Repository 결합:

뉴스 앱에서 당신은 뉴스와 작성자 data를 관리하는 NewsRepository와 AuthorsRepository classes를 가지고 있을 겁니다. Article class는 NewsRepository에서 작성자의 이름만 필요합니다. 하지만, 당신이 작성자에 대한 많은 정보를 화면에 표시하고 싶다면, 작성자 정보를 AuthorsRepository에서 가져올 수 있을 겁니다.









Logic이 복잡해짐에 따라, 당신은 GetLastestNewsWithAuthorsUseCase를 작성할 수 있을 겁니다. 이것은 ViewModel의 logic을 가져와 좀 더 읽기 수월한 코드를 만듭니다. 또한, logic을 테스트하기 쉽게 격리 시킵니다. 그리고 앱의 다른 부분에서 재사용 가능하게 만듭니다.








이 logic은 news 목록의 모든 항목들을 다룹니다. 따라서, data layer가 main-safe이더라도 logic의 news를 다루는 일은 main thread를 막아선 안됩니다. 왜냐하면 news의 목록 크기가 엄청나게 클 수도 있기 때문입니다. 이것이 바로 default dispatcher를 사용하여 background thread로 보내는 이유입니다.



- 메모: Room 라이브러리는 relationships(관계)를 이용하여 다른 entities(엔티티)에 query(쿼리)를 보낼 수 있습니다. 만약 database가 source of truth라면 모든 query를 만들 수 있습니다. 이러한 경우 usecase를 만들기 보다 NewsWithAuthorsRepository를 만드는 게 더 낫습니다.







⊙ Other consumers; 다른 소비자들:

UI layer와는 다르게 domain layer는 services나 Applicaition class에서 재사용 될 수 있습니다. 더 나아가, TV나 Wear의 경우 mobile app과 code를 공유할 수 있습니다.  TV와 Wear의 UI layer의 경우 앞에서 언급한 이점을 위해 use cases를 재사용 할 수 있습니다.





⊙ Data layer access restriction; Data layer 접근 제어:

Domain layer를 적용할 때 고려해야 할 다른 사항은 UI layer에서 data layer로 접근을 허용할 것인지, 모든 것을 domain layer를 통해서 할 것인지를 결정하는 것일 겁니다.





UI layer에서 Data layer로 직접적으로 가는 것을 막는 것은 이러한 이점이 있습니다. 예를 들면, data layer에 접근할 때 log를 남기고 분석할 수 있게 해줍니다.



그러나, 잠재적 불이익은 data에 접근하는 간단한 함수도 use case를 쓰도록 만든다는 것입니다. 이것은 작은 이점을 위해 복잡함이 증가하는 것을 의미합니다.



좋은 접근법은 use cases를 필요할 때만 추가하는 것입니다. UI layer에서 거의 use case로 data에 접근한다면 오직 use case로 data에 접근하는 것이 좋아 보입니다.



끝으로, 접근 제어는 당신의 코드에 따라 결정해야 할 사항입니다. 딱딱한 규칙을 적용하거나 유연한 규칙을 적용하거나 선호하는 방법으로 말이죠.





⊙ Testing: 테스트;

일반적인 test 지침을 domain layer에 적용합니다. 보통의 UI tests는 fake(가짜) repository를 사용합니다. domain layer를 test 할 때에도 fake repository를 쓰는 것이 좋습니다.





끝.



카테고리: Android


댓글

이 블로그의 인기 게시물

Python urllib.parse.quote()

Python OpenCV 빈 화면 만들기

tensorflow tf.random.uniform()

Android Notification with Full Screen

KiCad 시작하기 2 (PCB 만들기)

Android Minimum touch target size

Python bs4.SoupStrainer()

KiCad 시작하기 4 (기존 회로도 수정 및 추가)

음악 총보(Score), 파트보(Part)

tensorflow tf.expand_dims()