클린 아키텍처 첫번째 포스팅에서 Domain 계층이 외부에 의존성을 가지지 않도록 하는 의존성 규칙(DIP)을 적용하기 위해 추상화된 인터페이스를 사용했다.
하지만 클린 아키텍처는 비교적 러닝커브가 가파른 개념이라 세부사항들에 대한 설명이 중요할 것 같아서
DIP를 사용하는 이유와 적용했을때/적용하지 않았을 때의 구조적 차이점에 대해 알아보고자 한다.
이번 포스팅은 Project level(Module Level이라고 해야하나? 몰?루)에서의 계층간 의존도와 Source code level에서의 계층간 의존도 이렇게 크게 두 파트로 나뉘어진다.
프바프라서 모든 프로젝트에 해당되지 않을 수도 있지만, 내가 진행중인 프로젝트에서는
Data 계층의 Mapper에서 해당 계층의 데이터 클래스를 Domain 계층의 Model 클래스로 변환하기 위해 Domain 계층에 대한 의존성을 가져야 해서
직접적으로 Source code level에서의 의존성은 없지만, Project level에서의 의존성은 존재하기 때문에 두 파트로 나누었다.
✔ Project level
우선 Project level에서의 계층간 의존성 구조에 대해 알아보자.
위 그림은 DIP를 적용하지 않았을 경우의 계층 간 의존성 구조도이다.
Data 계층과 Domain 계층이 상호 의존성을 가지게 되어 고수준에서 저수준으로 흘러야 하는 클린 아키텍처 설계 원칙을 위반하게 된다.
핵심 비즈니스 로직 덩어리인 Domain은 "정책"이기 때문에 그 구현부(메커니즘)인 Data 계층과 Presentation 계층의 변경에 영향을 받아서는 안된다. 변경의 전파는 정책으로부터 시작되는 것이지, 메커니즘에서 시작되어서는 안되기 때문이다.
여기서는 양방향 의존성을 가지지만, Source code Level에서는 양방향 의존성을 가지지 않기 때문에 그 점을 유의해야 한다.
위 그림은 DIP를 적용했을 경우의 의존성 구조도이다.
Data 계층과 Presentation 계층은 Domain 계층에만 의존하며, Domain 계층은 외부에 의존성을 가지지 않는다.
이로써 Domain 계층은 외부의 변경에 영향을 받지 않을 수 있게 되었다.
✔ Source code Level
Data 계층에 데이터 작업 수행을 요청하거나 그 결과값을 반환받기 위해 Domain 계층이 Data 계층에 의존성을 가진다.
이 경우에는 Project level에서의 의존성 규칙과 다르게 Domain 계층만 Data 계층에 의존성을 가지는데,
이 경우에는 Mapper 클래스를 Domain 계층에 가지게 하거나,
Data 계층 또한 Domain 계층에 대한 의존성을 가지게 하여 상호 의존성을 가지게 한 뒤 Data 계층에 Mapper 클래스를 두는 방법도 있다.
Q.
단방향 의존성을 가지기 때문에 전체적으로 보면 괜찮은 의존성 구조 아닌가요?
아니다. 메커니즘의 변경보다 정책의 변경이 소프트웨어 전체의 관점에서 훨씬 큰 영향을 미친다.
게임을 예로 들면, 정책의 변경은 근거리 전투를 하는 캐릭터를 원거리 전투를 하는 캐릭터로 바꾸는 격이다. 그로 인해 해당 캐릭터의 모든 스킬과 다른 캐릭터와의 상성, 추천 아이템 등등 그 캐릭터를 운용하는 메커니즘이 전부 바뀐다는 것을 생각하면 된다.
- 정책 -> 물리 피해, 물리 방어력이 높음, 근거리 공격, 5가지 스킬(액티브 3개, 패시브 2개)
- 메커니즘 -> 마법 피해 캐릭터에 약함, 스킬은 a -> b -> c -> d -> e 순서로 마스터할 것, 추천 아이템은 xx, xx, xx
때문에 정책을 포함하는 Domain 계층이 가장 안쪽에서 외부에 영향을 받지 않도록 설계해야 한다.
이것이 우리가 클린 아키텍처를 설계할 때 의존성 역전 원칙(DIP)를 사용하는 이유이다.
* 위 그림에서 Presentation 계층의 ViewModel이 Repository 인터페이스를 참조한다고 되어있는데, 잘못 연결한 것이라 UseCase를 참조하는것이 맞다. UseCase가 Repository의 메서드들을 호출하기 때문이다.
-
Data 계층과 Presentation 계층이 Domain 계층에 의존성을 가진다.
위 그림에서는 DIP를 적용하기 위해 추상화된 인터페이스를 사용하였다.
Data 계층은 여러 컴포넌트를 사용하여 RepositoryImpl 클래스를 구현하고, Presentation은 Domain 계층의 Repository 인터페이스를 참조하여 데이터 작업을 요청하고 결과값을 반환받는다.
이를 통해서 Domain 계층은 Data 계층에 대한 의존성 없이도 Data 계층과의 상호작용이 가능해진다.
View -> ViewModel -> UseCase -> Repository <- RepositoryImpl <- Mapper <- DataSource <- DataSourceImpl <- Data(local, remote)
Data 계층은 Repository 인터페이스를 참조하거나Entity/Dto를 Domain 계층의 Model로 매핑하는 경우에만 참조하기 때문에 의존성을 가지지만 결합도가 높지는 않다.
클린 아키텍처 설계 시에 의존성 역전 원칙을 적용하는 것의 중요성에 대해 알아보았으며,
3편에서는 멀티 모듈을 적용한 안드로이드 클린 아키텍처 예제를 알아보도록 하자.
'개발 > Android' 카테고리의 다른 글
[안드로이드] EXTRA_PICK_IMAGES_MAX 사용 시 주의할 점 (0) | 2023.09.14 |
---|---|
[안드로이드] 클린 아키텍처 - (3) 멀티 모듈 패키지 구조 (0) | 2023.05.23 |
[안드로이드] 클린 아키텍처 - (1) 개념 (0) | 2023.05.16 |
[안드로이드] 오픈소스 라이선스 고지 (0) | 2023.05.10 |
[안드로이드] ViewModel에서 context를 사용하는 방법 (1) | 2023.05.10 |