본문 바로가기

클린아키텍쳐

[클린아티텍쳐] BestPractice로 공부하는 클린아키텍쳐 1편: Domain

 

안녕하세요 주니오스입니다~! ✋

 

이번글 은 클린아키텍쳐와 관련된 포스팅입니다.

 

클린아키텍쳐는 아래 보이는 그림처럼 원형으로 이루어집니다.

 

클린아키텍의 핵심은 아래그림에서 볼 수 있듯이 경계의 분할(동심원)과 

 

의존성의 방향(화살표방향)이 항상 안쪽으로 향하는 단방향 의존성입니다.

 

출처: https://github.com/kudoleh/iOS-Clean-Architecture-MVVM?tab=readme-ov-file

 

위 두 제약은 SOLID개발 원칙이 기저에 깔려있으며 소프트웨어를 안정적으로 개발할 수 있게합니다.

 

동심원은 크게 3가지로 구성됩니다.

 

Domain, Presentation 그리고 Data 입니다.

 

해당 레이어가 하나의 모듈을 의미하는 것이아닌 논리적인 개념으로 생각하시면됩니다.

 

예를들어 Presentation영역은 화면에 컨텐츠를 표시하는 요소들이 속하게 됩니다.

 

이번 글은 3가지 레이어중 Best Practice의 Domain영역에 대해 분석하는 시간을 가져 보려고 합니다.

 

Best Practice 

 

디렉토리 구조

해당 프로젝트의 폴더구조는 다음과 같습니다.

 

Entities, UseCases, Interfaces

 

먼저 Entities 부터 살펴보겠습니다.

 

Entities

엔티티는 앱내 비즈니스로직을 캡슐화하여 보관하는 객체입니다. 따라서 다층구조에서 변동성이 가장 낮은 Domain에 존재합니다.

 

모바일 엔지니어링의 특징상 실제로 비즈니스 로직(연산로직)을 엔티티가 보유하는 경우는 거의 업지만,

 

어플리케이션의 Essential한 객체라고 생각하시면 됩니다.

 

Entities

 

서버로 부터 받아온 DTO(Data Transfer Object)을 앱내 니즈에 맞게 변형한 형태를 엔티티에 담을 수 있습니다.

니즈에 맞게 변형했다는 것자체가 앱내 비즈니스를 보유한다고 볼 수 있습니다.

 

Entities/Movies

 

Interfaces

인터페이스가 사용되는 이유는 추상화를 통해 의존성을 역전시키기 위해서입니다.

 

우선 해당 디렉토리(Domain내)에는 레포지토리 인터페이스들이 존재합니다.

Interfaces/Repositories

 

레퍼지토리 인터페이스 프롵토콜

 

레포지토리는 앱내에서 사용될 데이터를 불러오는 역할을 합니다.

 

위 프로토콜만 봐서는 어떤 곳에서 데이터를 불러오는 예를 들어 네트워크인지, 로컬 저장소인지를 판단할 수 없습니다.

 

즉, 위 프로토콜은 데이터를 불러오는 방법을 추상화합니다. 

 

그렇다면 왜 레포지토리 인터페이스가 도메인 영역에 존재하는지 의문이 드실 수 있습니다. 🤔 

 

앞서 언급드린대로 도메인 영역은 다른 두 레이어에게 의존하면 안됩니다.

 

즉, 데이터를 불러올 곳에 의존해서는 안되며, 불러온 데이터를 어떻게 처리할 지와 같은 비지니스 로직에 집중한 영역입니다.

 

레포지토리 인터페이스가 Domain영역에 존재하게 되면,

 

Domain은 외부 의존성을 가지지 않게되고, 오히려 레포지토리 구현체가 제약을 받게됩니다. 

따라서, 변동가능한 부분을 추상화하여 앱내 핵심인 Domain영역의 변동성을 최소화하게 됩니다.

 

※ DIP와 관련된 부분으로 제 포스팅중 클린아키텍쳐 주요원칙을 안보셨다면 보시길 추천합니다!

 

추가적으로,

 

레포지토리 함수 반환값에 엔티티가 사용되는 것을 확인할 수 있습니다.

 

엔티티는 도메인 레이어와 데이터 레이어의 경계에 위치함으로써, 도메인의 격리성을 높입니다.

 

아래 코드는 Presentation레이어에 존재하는 ViewModel의 생성자 입니다.

 

해당 뷰모델은 Movie엔티티의 데이터를 사용하여 화면에 표시될 데이터를 가져옵니다.

 

여기서 엔티티는 화면을 구성하는 비즈니스에 쓰이고 있으며 해당 종류의 엔티티를 VO(View Object)라고 합니다.

 

엔티티 -> 뷰모델

 

 

더보기

코드의 @discadableResult 속성은 무엇인가?

 

매서드의 return값이 Void가 아닌 값을 사용하는 경우 해당 값을 이용하지 않으면 warning이 표시됩니다.

 

위 속성을 매서드에 표기할 경우 해당 warning을 표시하지 않습니다.

 

UseCases

UseCase는 시스템의 애플리케이션 레벨에서 특정 작업이나 유저와의 상호작용을 정의하는 객체입니다.

Domain/UseCases

 

SearchMovieUseCase는 말그대로 영화를 검색하는 동작(유저 상호작용혹은 작업)입니다.

 

UseCase는 개발자 뿐만아니라 특정 프로덕트의 모든 이해관계자가 이해할 수 있는 동작을 다뤄야 합니다.

 

BestPractice의 경우도 이름부터 매우 직관적임을 알 수 있습니다.

 

(영화검색.실행, 정말 깔끔한 네이밍이라고 생각합니다. 🤗)

 

UseCases/SearchMovieUseCase

 

파일에 들어가 보시면 인터페이스가 함께 포함되어 있는 것을 확인할 수 있습니다.

 

UseCase도 결국에 특정 작업임으로 변동될 수 있습니다, 그러한 확장을 열어놓기 위해 인터페이스를 가지고 있습니다.

 

아래코드는 프로젝트의 DIContainer내부 코드인데요. ViewModel에 UseCase구체타입이 주입되는 것을 확인할 수 있습니다.

 

Application/MoviesSceneDIContainer

 

아래코드는 UseCase를 주입받는 ViewModel로 프로퍼티타입이 프로토콜입니다.

 

Presentation내부 ViewModel디렉토리

 

정리

Best practice 프로젝트의 Domain영역을 살펴보았습니다.

 

알 수 있었던 포인트는 다음과 같았습니다.

 

1. Domain영역은 앱네 비지니스 로직을 보관하는 엔티티를 보유한다.
2. 도메인 레이어는 레포지토리 인터페이스를 보유하며 인터페이스는 후에 의존성 주입을 가능하게 한다.

 

궁금증

아직 완전한 분석이 이루어지지 않았음으로 아래 궁금증들을 함께 해결해 나갔으면 좋겠습니다!

1. UseCase추상화를 통해서 얻을 수 있는 실질적인 이점은 무엇일까?
2. 타입의 추상화는 확인하였지만 구체적인 의존성 주입은 어떻게 이루어지는가?

 

 

위 질문에 대한 답은 제가 성장해가며 포스팅 하도록 하겠습니다! 포스팅 시점은 (24.4.16)이내요.

 

궁금증1 해결(24.4.16 추가): UseCase추상화를 통해서 얻을 수 있는 실질적인 이점은 무엇일까?

 

저는 이부분에 이점이 없다고 생각한 것이 유스케이스는 변동성이 적다고 생각해서였습니다.

 

일단 현업에서도 유스케이스가 변동되는 일은 잦다고 합니다.

 

그리고 더욱 중요한 관점이 있는데요, 바로 유스케이스가 같은 레포지토리를 사용하더라도 내부로직이 달라야하는 경우가 있기 때문입니다.

 

해당 경우를 레포지토리내부코드에 분기문을 작성하는 것은 레포지토리마다 모두 제각각 설정이 필요함으로 부적절합니다.

 

UseCaseImpl을 여러개 만들고 해당 객체 내부에 코드를 위치시킴으로써 문제를 해결할 수 있습니다.

 

이게 바로 UseCase를 추상화해야하는 이유인데요.

 

조건에 따라 원하는 UseCase를 주입하여야하기 때문입니다.

 

DI를 통한 분기

 

 

주입을 통한 분기를 이루기 위해서요! 🔥

 

 

다음 포스팅에서 뵙겠습니다.

 

감사합니다~! 😆