ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Clean Architecture란 무엇인가?(Domain Layer)(2)
    Architecture 2024. 7. 19. 23:50

     

    안녕하세요. 1000JI 입니다 :)

    최근에 인수인계와 프로젝트 작업 마무리하느라 글을 못 올렸습니다..!

    1편에 이어서 클린아키텍처에서 계층화한 Layer에는 어떤 것들이 있고 어떤 목적으로 나눈 것인지를 보도록 하겠습니다.

     

    먼저 클린아키텍처 다이어그램을 보고 시작하시죠!

    출처: https://sunny-maneg.tistory.com/entry/iOS-%EC%84%A4%EA%B3%84%EC%97%90%EC%84%9C%EC%9D%98-Clean-Architecture

    안쪽에 있는 "Domain Layer" 부터 살펴보려고 합니다 :)

     

     

     

    Domain Layer

    Entity

    이하 엔티티라고 부르겠습니다. 엔티티는 "Enterprise wide business rules"을 캡슐화 합니다.

    저 영문장을 ChatGPT한테 번역해달라고 했더니 "기업 전체에 걸쳐 적용되는 규칙이나 지침을 의미"한다고 하네요!

    회사 규칙 같은 경우는 사실 변경하기가 매우 까다롭잖아요? 내부 인원의 동의도 필요하고.. 논의도 필요하고...

    그렇기 때문에 변화 가능성이 거의 없는 친구들 즉, 가장 높은 수준의 규칙을 말합니다.

     

    어떤 것들이 있을까요?

    바로 데이터구조를 예를 들 수 있을 수 있습니다 :)

     

    struct Station {
      let name: String
      let line: Int
      let lat: Double
      let lng: Double
    }

     

    역 정보를 가지고 있는 데이터구조 라고 했을 때 역 이름이 수시로 바뀌거나

    위경도 같은 경우도 역이 발이 달려서 다른 위치로 이동하지 않는 이상 정보가 바뀔 경우는 크게 없습니다.

    Station이라는 데이터 구조는 가장 높은 수준의 규칙이라고 말할 수 있겠습니다 :)

     

    특성을 다시 정리하면 가장 위 또는 안쪽에 있는 Entities 레이어는 모든 레이어에서 의존성을 가지고 있으니

    변화가 가장 적어야하며, 바깥에 있는 레이어에 대해서 아무것도 몰라야 합니다.

     

    Usecase

    이하 유스케이스라고 부르겠습니다.

    유스케이스는 Use: 쓰다, 용도, 사용 / Case: 경우, 사례 -> 사용 사례(?) 뭔가 말이 이상하죠?

    하지만 뭔가 느낌이 옵니다..! 쓰는 방법? 동작 종류? 비슷비슷한 느낌이니 그대로 가져가봅시다 :)

    모바일 앱 기능 Guide 중 A to C 까지 가이드가 필요하다면 A to C 까지 가는 시나리오를 보여주는 예를 들 수 있습니다.

     

    A, B, C 화면은 하나의 앱 화면이고 이동하기 위해선 버튼 클릭, 제스처 등 여러 동작들이 필요 할 수 있습니다.

    그렇단 얘기는 각 앱 화면, 앱 상의 동작들을 통해 시나리오를 이행 할 수 있다고 볼 수 있겠습니다.

     

    즉, 정리하면 "시스템의 동작을 사용자의 입장에서 표현한 시나리오"라고 볼 수 있습니다.

    이 계층은 엔티티와의 데이터 흐름을 조정하고, 해당 엔티티가 사용자의 목적을 달성 할 수 있도록 비즈니스로직이 포함되어 있습니다.

    좀 더 쉽게 설명하자면 '너 목록 조회 버튼 클릭했어? 그러면 조회 해줄게!' 또는 '검색 화면으로 이동해줘? 알겠어!'처럼

    특정 유저 액션을 통해 엔티티를 활용해서 데이터를 조회하거나 가져오거나 하는 비즈니스 로직을 뜻합니다.

     

     

    단, 여기서 주의해야 할 점은 다이어그램에서 보시다시피 엔티티보다 유스케이스가 더 바깥쪽에 위치하고 있습니다.

    그렇기 때문에 엔티티는 유스케이스를 몰라야합니다. 반드시!!

    그렇지 않으면 의존성 방향이 올바르지 않게 되기 때문에 클린아키텍처를 따른다고 볼 수 없겠습니다.

     

    이만하면 어느정도 이론은 알겠는데 코드는 어떻게 되는지 궁금하시죠?

    보통 블로그에서 설명 할 때 쓰는 코드를 가져와봤습니다.

    final class DefaultSearchMoviesUseCase: SearchMoviesUseCase {
    
        private let moviesRepository: MoviesRepository
        private let moviesQueriesRepository: MoviesQueriesRepository
    
        init(
            moviesRepository: MoviesRepository,
            moviesQueriesRepository: MoviesQueriesRepository
        ) {
            self.moviesRepository = moviesRepository
            self.moviesQueriesRepository = moviesQueriesRepository
        }
    
        func execute(
            requestValue: SearchMoviesUseCaseRequestValue,
            cached: @escaping (MoviesPage) -> Void,
            completion: @escaping (Result<MoviesPage, Error>) -> Void
        ) -> Cancellable? {
            return moviesRepository.fetchMoviesList(
                query: requestValue.query,
                page: requestValue.page,
                cached: cached,
                completion: { result in
                    if case .success = result {
                        self.moviesQueriesRepository.saveRecentQuery(query: requestValue.query) { _ in }
                    }
                    completion(result)
                }
            )
        }
        
    }

     

    코드를 봤을 때 어떤 작업을 하는 코드로 보이시나요? 영화 리스트를 조회하는 코드입니다.

    사용자가 영화 목록 조회하는 어떤 버튼 또는 텍스트를 입력 했을 것이고,

    그 Request 엔티티를 가져와서 영화 목록을 조회하는 것 입니다 :)

     

    위에서 말했던 "특정 유저 액션을 통해 엔티티를 활용해서 데이터를 조회하거나 가져오거나 하는 비즈니스 로직"이라는 것이

    어떤 것인지 감이 오시나요? :)

     

    즉, 영화 목록 조회 엔티티를 활용하여 영화 목록 조회 요청하는 비즈니스 로직을 가지고 있는 것 입니다.

    그런데 코드상 뭔가 이상한 것이 들어가있죠? Repository.....???? 형이 왜...???

     

    분명 다이어그램 상 유스케이스보다 바깥쪽에 있는데 왜...??? 유스케이스가 알고 있지? 라는 생각이 들 수 있습니다!

     

     

    자자자자.... 당황하지 마시고요!(내가 제일 당황...)

    아시겠지만 유스케이스는 유저의 특정 액션을 통해 이루어지는 비즈니스 로직을 갖고 있는 레이어 입니다.

    사용자의 목적을 달성하기 위해서 데이터소스 객체가 필요한 상황입니다..!!

     

    사실 근데 유스케이스는 어찌됐든 사용자의 목적을 달성하기 위한 레포지토리 객체가 있으면 되기 때문에,

    이 레포지토리 객체가 DB에서 가져오든~ API에서 가져오든~ 심지어 Mock 객체에서 가져오든 상관 없습니다.

    하지만 유스케이스에서 DB 또는 API에서 가져오는 특정 레포지토리 객체를 생성했습니다.

     

    만약 Mock 객체로 바꾸고 싶다면 유스케이스에 있는 코드를 수정을 해줘야합니다. 결국 이런 상황을 뭐라고할까요?

     

     

    바로 의존성이 생겼다... 라는 것입니다! 유스케이스가 레포지토리에 의존성이 생긴 거라고 볼 수 있겠죠?

    그럼 해결 방법은 뭐가 있을까요? 바로 DIP(의존성 역전 원칙)이라는 개념이 등장합니다.

    이 개념은 추후 글을 한번 더 정리하겠습니다 :)

     

    만약 유스케이스 계층에서 나 이런저런 레포지토리 객체가 필요해~ 라고 인터페이스를 정의하고,

    바깥쪽인 레포지토리 계층에서 레포지토리 객체만 갈아껴서 주입 할 수 만 있다면?

    그러면 유스케이스에서는 레포지토리 객체 인터페이스만 알고 있으면 되는 상황이 만들어집니다.

     

     

    정리하면, 아래 그림과 같아집니다.

    출처: https://clamp-coding.tistory.com/466

     

    유스케이스에서 원하는 레포지토리 인터페이스(프로토콜)만 정의해두고,

    DB를 사용하는 레포지토리 객체든, API를 사용하는 레포지토리 객체든 원하는 객체로 갈아끼면 됩니다.

    심지어 인터페이스를 준하는 Mock 레포지토리 객체를 넣는다해도 유스케이스는 영향이 없는 것이죠!!

    UseCase -> Repository Interface <- Repository 구현체(RepositoryImpl)

     

    유스케이스는 레포지토리 인터페이스를 의존하고 있고 바깥쪽 계층에서 구현체 인스턴스를 생성하여 주입하는 형태,

    의존성 방향이 역전해서 들어간다해서 우리는 이것을 DIP, 즉 "의존성 역전 원칙" 이라고 합니다.

     

     

     

    자자..! 그래서 Domain Layer는 어떤 것들이 있다?

    • Entity(가장 안쪽)
    • UseCase, Repository Interface(Protocol)

    위 내용으로 정리 할 수 있겠습니다 :)

     

     

     

     

    다음 블로그 글은 그러면 구현체는 어떤식으로 구성되어 있는지 Data Repository Layer를 살펴보겠습니다.

     

    읽어주셔서 감사하고, 궁금한 부분이나 틀린 점이 있다면 댓글 남겨주세요!!

    감사합니다 :)

     


    참조 블로그

    https://sunny-maneg.tistory.com/entry/iOS-%EC%84%A4%EA%B3%84%EC%97%90%EC%84%9C%EC%9D%98-Clean-Architecture

     

    iOS 설계에서의 클린아키텍쳐 (Clean Architecture for iOS)

    안녕하세요 써니쿠키입니다 🍪 지난 포스팅에서 Uncle Bob의 클린아키텍쳐를 정리해보았는데요. >> [ 여기! ] 오늘은 이어서 이 클린아키텍쳐를 모바일 설계에는 어떻게 적용할 수 있는지와, 예시

    sunny-maneg.tistory.com

    https://clamp-coding.tistory.com/466

     

    [Clean Architecture] Repository Pattern in IOS

    Repository Pattern Repository Pattern의 필요성 Repository Pattern을 사용하지 않은 앱은 VC or MV이 직접 데이터들을 가지게 된다. 이에 따라오는 문제가 무엇일까? 바로 유지보수이다. 앱이 커지고 화면이 많

    clamp-coding.tistory.com

     

Designed by Tistory.