Bob 아저씨의 깔끔한 아키텍처-각 계층의 엔티티 / 모델 클래스?


44

배경 :

내 안드로이드 앱에서 Bob 아저씨의 깨끗한 아키텍처를 사용하려고합니다. 올바른 방법을 보여주기 위해 노력하는 많은 오픈 소스 프로젝트를 연구했으며 RxAndroid를 기반으로 흥미로운 구현을 발견 했습니다 .

내가 알았던 것 :

모든 계층 (프레젠테이션, 도메인 및 데이터)에는 동일한 엔터티 (토킹 UML)에 대한 모델 클래스가 있습니다. 또한 데이터가 경계를 넘을 때마다 (계층에서 다른 계층으로) 객체의 변환을 처리하는 매퍼 클래스가 있습니다.

질문 :

모든 CRUD 작업이 필요한 경우 모두 동일한 속성으로 끝날 것임을 알 때 모든 계층에 모델 클래스가 있어야합니까? 아니면 깨끗한 아키텍처를 사용할 때 규칙이나 모범 사례입니까?

답변:


52

제 생각에 그것은 그것이 의미하는 방식이 아닙니다. 그리고 그것은 DRY의 위반입니다.

아이디어는 중간에있는 엔티티 / 도메인 객체가 도메인을 최대한 좋고 편리하게 나타내도록 모델링된다는 것입니다. 그것은 도메인 자체가 대부분의 시간을 변경하지 않기 때문에 모든 것의 중심에 있으며 모든 것이 그것에 의존 할 수 있습니다.

외부의 데이터베이스가 해당 객체를 직접 저장할 수 있다면 레이어 분리를 위해 다른 형식으로 매핑하는 것은 무의미한 것이 아니라 모델의 복제본을 만드는 것이며 의도가 아닙니다.

우선 클린 아키텍처는 다른 일반적인 환경 / 시나리오를 염두에두고 만들어졌습니다. 고유 한 유형의 특수 객체가 필요한 외부 계층이있는 비즈니스 서버 응용 프로그램. 예를 들어 SQLRow객체 를 생성 SQLTransactions하고 업데이트 항목을 반환 해야하는 데이터베이스 중심에있는 것을 사용하는 경우 코어가 데이터베이스에 의존하기 때문에 종속성 방향을 위반해야합니다.

그렇지 않은 엔터티 개체를로드하고 저장하는 간단한 ORM 내부 SQLRow도메인과 도메인 간의 매핑을 수행 합니다. @EntitiyORM 주석을 도메인 객체에 추가해야하더라도 이것이 외부 레이어의 "멘션"을 설정하지는 않는다고 주장합니다. 주석은 메타 데이터 일 뿐이므로 특수하게 찾지 않은 코드는 해당 주석을 볼 수 없습니다. 더 중요한 것은 제거하거나 다른 데이터베이스 주석으로 바꾸면 변경할 필요가 없다는 것입니다.

반대로, 도메인을 변경하고 모든 매퍼를 만든 경우 많이 변경해야합니다.


수정 : 위의 내용은 약간 단순화되었으며 잘못되었을 수도 있습니다. 클린 아키텍처에는 레이어 당 표현을 만들려는 부분이 있기 때문입니다. 그러나 그것은 응용 프로그램의 맥락에서 볼 수 있어야합니다.

즉 다음은 https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html입니다.

중요한 것은 격리되고 간단한 데이터 구조가 경계를 넘어 전달된다는 것입니다. 엔터 티나 데이터베이스 행 을 속이고 통과하고 싶지 않습니다 . 데이터 구조가 종속성 규칙을 위반하는 모든 종류의 종속성을 갖기를 원하지 않습니다.

중심에서 외부 층으로 엔티티를 전달하는 것은 종속성 규칙을 위반하지 않지만 언급됩니다. 그러나 이것은 구상 된 응용의 맥락에서 이유가있다. 엔터티를 전달하면 응용 프로그램 논리가 외부로 이동합니다. 외부 레이어는 내부 객체를 해석하는 방법을 알아야하며 "사용 사례"레이어와 같은 내부 레이어가 수행해야하는 작업을 효과적으로 수행해야합니다.

또한 코어를 변경할 때 반드시 외부 레이어를 변경할 필요가 없도록 레이어를 분리합니다 (SteveCallender의 설명 참조). 이러한 맥락에서 객체가 사용 목적을 구체적으로 나타내는 방법을 쉽게 알 수 있습니다. 또한이 통신의 목적을 위해 특별히 만들어진 객체의 관점에서 레이어는 서로 대화해야합니다. 이는 각 계층마다 1 개, 계층 간 전송에 대해 1 개씩 3 개의 표현이 있음을 의미 할 수도 있습니다.

그리고 위에서 언급 한 https://blog.8thlight.com/uncle-bob/2011/11/22/Clean-Architecture.html 이 있습니다 :

다른 사람들은 내 조언의 최종 결과가 많은 중복 코드와 시스템의 여러 계층에 걸쳐 하나의 데이터 구조에서 다른 데이터 구조로 데이터를 많이 복사하는 것에 대해 걱정했습니다. 확실히 나는 이것도 원하지 않는다. 필자가 제안한 것은 필연적으로 데이터 구조의 반복과 과도한 필드 복사로 이어질 수 없습니다.

IMO는 실제로 적절한 레이어 및 / 또는 추상화를 사용하지 않기 때문에 객체의 일반적인 1 : 1 복사가 아키텍처에서 냄새가 난다는 것을 의미합니다.

그는 나중에 모든 "복사"를 상상하는 방법을 설명합니다

둘 사이에 간단한 데이터 구조를 전달하여 UI를 비즈니스 규칙과 분리합니다. 비즈니스 규칙에 대해 컨트롤러에게 알리지 않습니다. 대신, 컨트롤러는 HttpRequest 오브젝트를 간단한 바닐라 데이터 구조로 압축 해제 한 후 해당 데이터 구조를 비즈니스 오브젝트를 호출하여 유스 케이스를 구현하는 상호 작용 오브젝트로 전달합니다. 그런 다음 인터랙 터는 응답 데이터를 다른 바닐라 데이터 구조로 수집하여 UI로 다시 전달합니다. 뷰는 비즈니스 오브젝트에 대해 알지 못합니다. 그들은 단지 그 데이터 구조를보고 응답을 제시합니다.

이 응용 프로그램에서는 표현간에 큰 차이가 있습니다. 흐르는 데이터는 단순한 엔터티가 아닙니다. 그리고 이것은 다른 계급을 보증하고 요구합니다.

그러나 Photo엔터티에 약 0 개의 비즈니스 규칙이 있고이를 처리하는 "사용 사례"가 거의 존재하지 않으며 실제로 캐싱 및 다운로드에 더 관심이 있는 사진 뷰어와 같은 간단한 Android 애플리케이션에 적용됩니다 (그 프로세스는 사진을 더 명확하게 표현하려는 시점이 사라지기 시작합니다. 실제 비즈니스 로직 코어 레이어가 누락 된 동안 사진 자체가 데이터 전송 객체라는 느낌을 받았습니다.

" 둘 사이에 간단한 데이터 구조를 전달하여 비즈니스 규칙과 UI를 분리""가는 도중에 사진 이름을 3 번 바꾸려면" 사이에는 차이가 있습니다.

게다가, 데모 애플리케이션이 깔끔한 아키텍처를 표현하지 못하는 점은 레이어를 분리하기 위해 레이어를 분리하는 데 큰 강조점을 두지 만 애플리케이션이하는 일을 효과적으로 숨기고 있다는 것입니다. 그것은 https://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Architecture.html 에서 말한 것과 대조적 입니다.

소프트웨어 응용 프로그램의 아키텍처가 응용 프로그램의 사용 사례에 대해 비명을 지른다.

깨끗한 아키텍처에서 레이어를 분리하는 것을 강조하지는 않습니다. 의존성 방향에 관한 것이고 외부에 대한 의존성이없는 이상적인 평범한 자바에서 응용 프로그램의 핵심-엔티티 및 유스 케이스를 나타내는 데 중점을 둡니다. 그 핵심에 대한 의존성에 대해서는별로 중요하지 않습니다.

따라서 응용 프로그램에 실제로 비즈니스 규칙 및 사용 사례를 나타내는 핵심이 있거나 다른 사람들이 다른 계층에서 작업하는 경우 의도 한 방식으로 분리하십시오. 반면에 간단한 앱을 직접 작성하면 과용하지 마십시오. 유창한 경계를 갖는 2 개의 층으로 충분할 수 있습니다. 나중에 레이어를 추가 할 수도 있습니다.


1
@RamiJemli 이상적으로는 엔터티가 모든 응용 프로그램에서 동일합니다. 그것은 "기업 전반의 비즈니스 규칙"과 "응용 프로그램 비즈니스 규칙"(때로는 비즈니스와 응용 프로그램 논리)의 차이입니다. 핵심은 엔터티를 매우 추상적으로 표현한 것으로 어디에서나 사용할 수있을 정도로 일반적입니다. 하나는 고객 지원, 하나는 현금 인출기, 하나는 고객 자체를위한 웹 UI와 같은 많은 응용 프로그램이있는 은행을 상상해보십시오. 모든 사용자는 동일 BankAccount하지만 해당 계정으로 수행 할 수있는 작업 별 규칙을 사용할 수 있습니다.

4
깨끗한 아키텍처에서 중요한 점은 인터페이스 어댑터 계층을 사용하여 다른 계층의 엔티티 표현 사이를 변환 (또는 맵이라고 함)하여 해당 엔티티에 대한 종속성을 줄이는 것입니다. 유스 케이스 또는 엔티티 계층에 변경 사항이있는 경우 (바람직하게는 아니지만 요구 사항이 변경 될 때마다 변경 사항이 있음) 변경 사항의 영향은 어댑터 계층에 포함됩니다. 아키텍처 전체에서 동일한 엔터티 표현을 사용하기로 선택한 경우이 변경의 영향이 훨씬 커집니다.
SteveCallender

1
@RamiJemli 인생을 더 간단하게 만드는 프레임 워크를 사용하는 것이 좋습니다. 요점은 아키텍처가 의존 할 때주의해야하며 모든 것을 중심에두기 시작한다는 것입니다. RxJava blog.8thlight.com/uncle-bob/2015/08/06/let-the-magic-die.html 에 대한 기사도 있습니다. 사용해서는 안된다는 말이 아닙니다. 더 비슷합니다. 나는 이것을 보았습니다 .1 년 안에 다를 수 있으며 응용 프로그램이 여전히 주위에있을 때 그것에 붙어 있습니다. 평범한 옛 SOLID 원칙을 적용하면서 평범한 옛 자바에서 세부 사항을 만들고 가장 중요한 일을하십시오.
zapl

1
@zapl 당신은 웹 서비스 계층에 대해 같은 느낌이 있습니까? 다시 말해, @SerializedName도메인 모델에 Gson 주석을 추가 하시겠습니까? 아니면 웹 응답을 도메인 모델에 매핑하는 새로운 객체를 생성 하시겠습니까?
tir38

2
@ tir38 분리 자체는 이점을 제공하지 않으므로 향후 변경 비용이 발생합니다. => 응용 프로그램에 따라 다릅니다. 1) 다른 표현간에 변형되는 추가 단계를 만들고 유지 관리하는 데 시간이 걸립니다. 예를 들어, 도메인에 필드를 추가하고 다른 곳에 추가하는 것을 잊어 버린 적이 있습니다. 간단한 접근으로는 일어날 수 없습니다. 2) 나중에 필요한 경우 더 복잡한 설정으로 전환하는 데 비용이 듭니다. 레이어를 추가하는 것은 쉽지 않습니다. 따라서 대규모 응용 프로그램에서는 즉시 필요하지 않은 더 많은 레이어를 정당화하는 것이 더 쉽습니다.
zapl

7

당신은 실제로 그것을 얻었다. SRP를 수락하므로 DRY 위반이 없습니다.

예를 들어 : Business-Method createX (String name)이 있고 business-Method 내에서 호출되는 DAO-Layer에 createX (String name) 메소드가있을 수 있습니다. 그들은 서명이 같을 수도 있고 위임이있을 수도 있지만 목적이 다릅니다. UseCase에 createX (String name)를 가질 수도 있습니다. 그럼에도 불구하고 중복되지 않습니다. 내가 의미하는 바는 다음과 같습니다. 동일한 서명이 동일한 의미를 의미하지는 않습니다. 의미를 명확하게하려면 다른 이름을 선택하십시오. 명명 자체는 SRP에 전혀 영향을 미치지 않습니다.

UseCase는 응용 프로그램 특정 논리를 담당하고 비즈니스 오브젝트는 응용 프로그램 독립 논리를 담당하며 DAO는 저장을 담당합니다.

시맨틱이 다르기 때문에 모든 계층에는 고유 한 표현 및 통신 모델이있을 수 있습니다. 종종 "엔터티"를 "비즈니스 오브젝트"로보고 종종 분리 할 필요가없는 경우가 있습니다. 그러나 "거대한"프로젝트에서는 레이어를 올바르게 분리하기위한 노력이 필요합니다. 프로젝트가 클수록 다른 계층과 클래스로 표현 된 다른 의미론이 필요할 가능성이 높아집니다.

동일한 의미의 다른 측면을 생각할 수 있습니다. 사용자 개체는 화면에 표시되어야하고 내부 일관성 규칙이 있으며 어딘가에 저장해야합니다. 각 측면은 다른 클래스 (SRP)로 표시되어야합니다. 매퍼를 만드는 것은 엉덩이에 고통을 줄 수 있으므로 이러한 측면에서 작업 한 대부분의 프로젝트에서 하나의 클래스로 녹습니다. 이것은 분명히 SRP 위반이지만 아무도 신경 쓰지 않습니다.

저는 깨끗한 건축과 SOLID를 "사회적으로 받아 들일 수없는"응용 프로그램이라고 부릅니다. 내가 허락한다면 나는 그것을 사용할 것입니다. 현재는 할 수 없습니다. SOLID를 진지하게 고려할 때까지 기다려야합니다.


데이터 레이어의 어떤 메소드도 도메인 레이어의 어떤 메소드와 동일한 서명을 가져서는 안된다고 생각합니다. 도메인 계층에서는 가입 또는 로그인과 같은 비즈니스 관련 명명 규칙을 사용하고 데이터 계층에서는 저장 (DAO 패턴 인 경우) 또는 add (이 패턴이 콜렉션을 은유로 사용하므로 저장소 인 경우)를 사용합니다. 마지막으로 엔터티 (데이터)와 모델 (도메인)에 대해 이야기하지 않고 UserModel과 그 Mapper (프레젠테이션 레이어)의 쓸모없는 부분을 강조하고 있습니다. 프리젠 테이션 내에서 도메인의 User 클래스를 호출 할 수 있으며 이는 종속성 규칙을 위반하지 않습니다.
Rami Jemli

Rami와 동의합니다. 매퍼가 필요하지 않습니다. 상호 작용기 구현에서 직접 매핑을 수행 할 수 있기 때문입니다.
크리스토퍼 페리

5

아니요, 모든 계층에서 모델 클래스를 만들 필요는 없습니다.

엔티티 ( DATA_LAYER)-데이터베이스 오브젝트의 전체 또는 부분 표현입니다.DATA_LAYER

매퍼 ( DOMAIN_LAYER)-실제로는 Entity를 ModelClass로 변환하는 클래스입니다.DOMAIN_LAYER

살펴보기 : https://github.com/lifedemons/photoviewer


1
물론 데이터 계층의 엔터티에 반대하지는 않지만 프레젠테이션 계층의 PhotoModel 클래스에는 도메인 계층의 Photo 클래스와 동일한 특성이 있습니다. 기술적으로 같은 클래스입니다. 그게 필요한가요?

나는 무언가가 당신의 맵퍼는 IMO, 그 반대의 반대를해야 데이터 계층의 개체에 따라 당신의 예에서와 같이 다른 레이어에 의존해서는 안 도메인 레이어로 예에서 꺼져 생각
navid_gh
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.