Clean Architecture : Part 2 — The Clean Architecture

JustWrite
21 min readMay 15, 2018

--

이 문서는 “Clean Architecture : Part 2 — The Clean Architecture” 를번역한 문서입니다. 저자의 동의를 얻어 올립니다. 의역이 심할 수 있으니 주의를 …

클린 아키텍처는 밥 아저씨 (a.k.a 엉클 밥) 이 제안한 용어로, 아키텍처를 만들 때 사용하는 원칙과 설계 방식을 나타내요. 추상적인 방법으로 정의를 해서 많은 질문과 논쟁을 야기시켰죠.

이 글에서는 클린 아키텍처의 가장 중요한 개념을 설명할 거에요. 불행하게도 다행이도 이 글은 단계별 가이드는 아니에요. 이런 가이드는 각 단계에 대해서 깊게 생각할 필요가 없는 기술적인 질문의 경우에만 의미가 있다가 생각해요. 모든 아키텍처적인 결정 사항들은 심사숙고해야 해요. 이 글에 기술된 개념 중 일부는 처음 보면 터무니 없어 보일 수 있지만 프로젝트에 적용한 후에는 이치에 맞아 보일 거에요.

Introduction

클린 아키텍처의 주요 아이디어는 파트 1에 기술된 아키텍처 (Hexagonal, Onion) 과 개념과 매우 유사해요. 심지어 모든 것들이 같다고도 얘기할 수 있을 것 같아요. 일반적으로 그것들은 앞선 아키텍처들로부터 전달된 매우 강력하고 중요한 아이디어의 집합일 뿐이에요. 그러니 클린 아키텍처가 모든 것의 해답이라고 생각하지는 말아요. 어떤 아이디어를 얻었다고 해서 이걸 모든 곳에 적용할 수 있다는 것은 아니에요. 그리고, 적용한다고 해서 극적인 코드 개선과 프로젝트의 성공을 보장하는 것은 아니에요. 왜 클린 아키텍처가 필요한지에 대한 견고한 이해 없이 단지 이 것이 보편적이고 모든 사람이 시도할 만한 아키텍처라는 이유때문에 클린 아키텍처를 사용한다면 원칙을 적용하지 않을 때보다 더 나쁜 결과를 초래할 수 있어요.

How should a good architecture look like?

클린 아키텍처를 살펴보기 전에 왜 이 아키텍처가 필요한지 그리고 좋은 아키텍처는 어떤 요소를 가져야하는지 살펴볼게요. 아키텍처를 통해 완성되어야 할 요구 사항은 무엇이며 일반적으로 이해관계자와 비지니스가 SW 시스템에서 무엇을 기대할까요?

  1. 비지니스 변경사항에 최대한 빠르게 대응하면서 시스템 경쟁력을 높일 수 있는 능력
  2. 시스템은 적절한 경계로 모듈로 잘 나누어져야 해요. 그리고 모든 모듈, 세부 사항, 전달 메커니즘은 시스템 중단 없이 교체 가능해야해요.
  3. 시스템은 유지하기 쉽고, 이해하기 쉽고, 확장과 배치가 쉬워야 해요. 이 부분은 개발자의 생산성을 극적으로 올려줄 거에요.
  4. 시스템은 확장 가능 (scalable) 해야해요. 높은 부하나 많은 데이터를 처리할 수 있도록 수평적으로 자라날 수 있어야 한다는 거에요.
  5. SW 시스템은 구현 세부 사항, 전달 메커니즘, 라이브러리 등에 대한 일부 결정을 미룰 수 있는 강력한 기초를 가져야해요.
  6. 아키텍처는 시스템의 의도 및 사용 사례 (Use Case) 를 지원하고 잘 드러낼 수 있어야 해요. (스크리밍 아키텍처 참고)

Inversion of Control. The Dependency Rule

모든 프레임워크에 사용되는 매우 중요하고 보편적인 개념들 중 하나는 제어의 역전 (Inversion of Control) 이라는 개념이에요. 아마도 이 원칙에 대해서는 이미 들어 봤을 것 같아요. 예를 들면 Java EE 세상에서 IoC 컨테이너들 말이죠. (Enterprise Edition 에 관심이 없어서 뭔지 모름 하하)

클린 아키텍처를 이해하는데 있어 가장 중요한 아이디어 중 하나는 의존성 규칙이에요. 이 규칙은

소스 코드의 종속성은 오직 내부를 가리켜야 해요. 내부 영역 (inner circle) 은 외부 영역 (outer circle) 의 그 어떤 것도 알아서는 안되요. 특히 외부 영역에서 선언된 것들은 내부 영역에서 코드 형태로 언급하면 안되요. 그것은 함수, 클래스, 변수 또는 SW 엔티티로서 역할을 하는 것들이 모두 포함되요.

종속성 규칙 (Depdency Rule) 은 직접적이고 밀접하게 제어의 역전의 개념과는 관련되어 있지는 않지만, 종속성 규칙을 적용하면 IoC 를 적용할 수 밖에 없어요. 그리고 이 지점이 중요한 포인트에요. 그럼 어떻게 클린 아키텍처에 이 부분이 적용되었는지 제어 흐름을 살펴보면서 알아봐요. 다음 다이어그램은 초기 아키텍처 다이어그램의 구석에서 발견할 수 있어요 (이 문장은 아키텍처 문서 구석탱이에서 이 다이어그램을 찾을 수 있을 것 이라는 저자의 농담인가? 의도를 모르겠음. 하하)

이게 전부 뭘까요? 쉬운 이해를 위해서 다이어그램을 살짝 변형해 볼게요.

이 흐름을 단계적으로 따라가 보죠. 버튼 클릭과 같은 액션이 만들어 졌다고 생각해보세요. 이 글의 나중에 설명할 다양한 개념에 대해서는 그렇게 많은 주의를 쏟지는 마세요.

  1. Controller 에서 버튼 클릭 이벤트를 처리해요. Controller 는 UseCase Input Port 인터페이스를 구현하는 객체의 메소드를 하나 호출하죠.
  2. 우린 막 두 레이어 (Presentation 레이어와 Use Case 레이어) 경계를 넘었고 지금은 Use Case 레이어에 있어요.
  3. Use Case 구현 (Use Case Interactor) 는 엔티티 또는 다른 도메인의 핵심 객체를 조정해서 전달된 요청을 처리해요.
  4. Use Case 구현 (Use Case Interactor) 는 처리 결과를 받은 후 UseCase Output Port 인터페이스를 구현하는 객체의 메소드를 하나 호출하죠.
  5. Presenter 는 Use Case 구현으로 부터 결과를 받고 (Presenter 가 구현하는 UseCase Output Port 인터페이스를 통해 전달이 되겠죠?), 그리고, Presenter 는 받은 결과를 적당한 형태로 변형해서 View 레이어로 전달해요.

다이어그램에 보이는 다양한 화살표가 궁금할 수도 있겠내요. 간단하게 각 화살표는 다음과 같아요.

  1. ━▷ 화살표는 화살표가 가리키는 인터페이스를 구현한다는 의미에요.
  2. ━▼ 화살표는 인터페이스 구현, 합성, has-a 관계를 나타내요 (다른 녀석을 참조한다는 의미).
  3. ┉▷ 파선 (dashed) 화살표는 실제 실행 흐름을 보여줘요. 프로그래밍 관점에서 보면 이건 함수 호출의 스택 또는 프로그램 카운터의 움직임으로 표현될 수 있어요.

최종적으로 이 흐름을 이해하기 위해 이 개념을 구현하는 있을법한 코드를 살펴보려고 해요. 먼저 AddProductToCartUseCase 라는 UseCase 를 정의해 볼게요.

위 코드는 AddProductToCartOutputPort 를 execute 메소드에 전달해요. 다르게 구현할 수도 있어요. 예를 들면 AddProductToCartOutputPort 가 AddProdctToCardUseCase 의 멤버로서 주입될 수도 있어요. 이건 프로젝트, 언어, 접근 방법등에 따라 달라질 수 있어요.

다음으로 AddProductToCartInputPort 와 AddProductToCartOutputPort 인터페이스를 정의해볼게요. 모든 인터페이스가 정의된 패키지 이름을 주의 깊게 봐주세요.

Presenter 와 Controller 는 다음과 같이 보이겠내요.

여기서는 AddProductToCartInputPort 와 AddProductToCartOutputPort 가 Controller 클래스에 주입이 되었지만 전에 말했던 것 처럼 다르게 구현될 수도 있어요 (CartConroller 클래스를 사용하는 곳에서 이 두녀석의 Concrete Class 를 생성해서 생성자를 통해 주입할 수 있겠죠). 더욱이 Controller 와 Presenter 가 이상하게 보일 수있지만 이 부분은 계속 언급할 거라 넘어하고 여기서는 주된 목표인 전체 흐름을 이해하는 것에 집중해요.

위에서 설명한 주요 아이디어는 상위 수준의 정책이 세부사항, 프레임워크, UI 등에 의존해서는 안되다는 것이에요. 이미 알고 있겠지만 AddProductToCartUseCase 는 UseCase 자체와 동일한 수준으로 정의된 인터페이스(AddProductToCartInputPort 와 AddProductToCartOutputPort)를 통해서만 동작합니다. 여기에서 제어의 역전 (Inversion of Control), 종속성 주입 (Dependency Injection), 종속성 역전 (Dependency Inversion) 으로 인한 종속성 규칙의 힘이 나와요. 이를 통해서 정의된 인터페이스를 만족하는 구현을 언제든 교체할 수 있어요. 그래서 고수준의 규칙과 정책이 지속적인 변경으로 부터 격리되어 어플리케이션의 코어가 안정적이 되도록 하죠.

Layers and Circles

이제는 이 다이어그램을 이해할 시간이에요. 대부분의 아키텍처는 보통 위 (Top) 에서 아래 (Bottom) 으로 정의되기 때문에 이 다이어그램을 처음 보면이상하게 보일 수 있어요. 이 다이어그램을 이해해 보죠. 그런데 살펴보기 전에 다이어그램에 실행 흐름을 표시해 볼게요.

위에 보이는 곡선 라인이 뭘하는지 그리고 왜 여러 경계를 왔다 갔다하는지 궁금할 수 있겠내요. 이는 어떤 작업을 처리하기 위해 복수의 세부 사항이 필요한 UseCase 가 있는 경우 발생할 수 있는 흐름이에요. 이 문맥에서 세부 사항은 가장 바깥 레이어에 존재하는 모든 툴 (라이브러리, DB, 전달 메커니즘, 프레임워크, 외부 장치 등) 을 의미해요.

예를 들면 주문이 확인되는 경우 송장 생성, 히스토리 추가, 관리자에 통지, 제품이 가용한지 검사하는 등의 많은 단계가 필요해요. 따라서 코드 실행 흐름은 곡선으로 표시된 선처럼 보여요. 이 선은 종속성 관계를 나타내는 것은 아니라는 점을 명심해야해요. 종속성은 오직 내부를 향해야 해요.

마지막으로 최초의 다이어그램을 좀더 이해하기 쉬운 형태로 변형해 볼게요.

이 흐름은 거의 모든 어플리케이션에서 볼 수 있어요.

  1. 어떤 외부 동작이 발생해요. 예를 들면 사용자가 버튼을 눌렀다거나 하는 거요.
  2. 이 이벤트가 View 레이어에서 Presenter 레이어로 전달이 되요. Presenter 클래는 전달된 데이터를 Presentation 레이어에 맞게 변환하고 UseCase 클래스에 전달해요.
  3. UseCase 레이어의 주요 역할은 어플리케이션 스펙에 따라 엔티티와 다른 도메인 객체를 제어하거나 조정하는 거에요. 그러나 이런 작업을 하기 전에 데이터베이스나 다른 곳에 저장된 데이터를 기반으로 도메인 객체가 생성되어 있어야 하죠. 이건 보통 게이트웨이를 통해 완료가 되고, 이 경우 Data Store 인터페이스를 통하죠.
  4. UseCase 레이어에서 엔티티를 가져온 후 UseCase 구현은 어플리케이션 스펙에 따라서 엔티티와 도메인 객체를 조정해요.
  5. 다이어그램에 있는 Aggregate Root 는 도메인 코어 레이어의 진입점이에요. 이 용어 (Aggregate Root) 는 도메인 주도 설계 원칙에 정의가 되어었이요.
  6. 비지니스 룰에 따라 요청 사항이 처리되면 그 결과는 필요하다면 UI 레이어에 전달되요.

지금까지 살펴본 다이어그램이 클린 아키텍처를 더 잘 이해하는데 도움이 되면 좋겠내요. 이젠 각 레이어를 각각 살펴볼게요. 그럼 가장 바깥에 존재하는 레이어 부터 코어 레이어 까지 차근 차근 살펴볼게요.

Details. Databases. Delivery Mechanisms. UI. Web

이 다이어그램은 가장 바깥 레이어를 표시한 거예요. 아마 어떤 시스템이든 이 레이어가 가장 크지 않을까 해요. 왜냐하면 다양한 장치, 라이브러리, 프레임워크가 존재하기 때문이죠. 이 것들은 지속적으로 변경되기 때문에 높은 수준의 정책을 세부 사항으로 부터 추상화해야 해요. 그중 몇 개를 한 번 살펴볼게요.

  1. 네트워크. HTTP, API, 웹 소켓, 소켓. 기본적으로 OSI 모델 또는 다른 대안은 단지 전달 메커니즘이에요.
  2. UI. Web UI, Native Window UI, Command Line, API, ViewController, Activity, Fragment. 이 것들은 모두 단지 UI 메커니즘이고 따라서 세부사항이죠.
  3. 데이터베이스. SQL, NoSQL, In-Memory, 파일 기반 데이터베이스. 데이터베이스는 단지 지속성 (persistence) 또는 캐싱 도구일 뿐이에요.
  4. 라이브러리와 프레임워크. 이 것의 예는 정말 많아요. 우리는 항상 라이브러리로 부터 어플리케이션의 코어 레이어를 추상화하려고 노력해야 해요. 그런데 몇몇 라이브러리는 크로스 커팅하기 때문에 이런 추상화가 언제나 가능하지는 않아요.
  5. I/O 장치. 입력 장치 (터치 스크린, 키보드), 지속성 장치 (NAND, HDD), 출력 장치 (스크린, 스피커). C로 작성된 리눅스 조차 입출력 장치를 위한 파일 추상화를 사용해서 커널을 정말 강력하고 이식성있게 만들어요.

이 정도면 충분한 예인 것 같아요. 주요 아이디어는 이 레이어에 있는 것들이 매우 빈번하게 변경되는 것들이라 추상화와 제어의 역전을 통해 우리 스스로를 변경으로부터 안전할 수 있게 만들어야 한다는 거예요. 이런 안정성은 개발 프로세스를 훨씬 쉽게 만들 뿐만아니라 가능한 문제점(bug)을 격리시켜요. 더욱이 이 레이어는 보통 변경이 자연스럽고 불안정성이 있기 때문에 거의 테스트 할 수 없어요.

때문에 추상화를 통해 코어 레이어가 이 레이어와 직접적인 종속성을 가지지 않게 하는 것이 중요하죠. 그래야 전체적인 테스트가 쉬워지니까요.

Presenters. Controllers. Gateways. Interface Adapters

이 레이어는 엉클 밥이 설명한 클린 아키텍처에서는 인터페이스 어댑터라는 이름으로 불렸어요. 이 것은 바깥 또는 안 레이어로 전달되는 모든 데이터가 데이터를 전달 받는 레이어에 맞게 변환이 일어나야 하는 것을 의미하죠. 예를 들면 View 레이어로 전달되는 데이터는 오직 문자열 필드만 가져야해요. 그래야 View 가 어떤 추가적인 작업 없이 display 를 할 수 있거든요.

MVP, MVC, MVVM 과 같은 디자인 패턴을 이미 잘 알고 있어야해요. 각각의 패턴을 여기에서 설명하지는 않아요. 뷰와 관련된 설계 세부적인 방법은 프로젝트의 요구 사항, 프로젝트의 유형 그리고 기타 요소들에 따라 선택돼요. 이 레이어는 단지 맨 바깥 레이어의 세부사항과 어플리케이션 레이어 이 경우에는 UseCase 레이어를 연결하는 역할을 해요. 대략적으로 말하면 문자 M (Model) 은 클린 아키텍처에서 UseCase 레이어에 의해 구현되지만 엔티티나 코어 도메인 객체에 의해서 구현되지는 않아요. 또는 이 Model 은 단지 UseCase 레이어와 인터페이스 어댑터 레이어 간에 주고 받는 데이터에요.

인터페이스 어댑터 레이어의 뷰

엉클 밥은 이 레이어에서 View 를 고려했지만 개인적으로는 이런 부분이 이해가 되지 않아요. View 는 가장 바깥 레이어에 두는게 나을 것 같다는 생각이에요. 하지만 만약 우리가 View 인터페이스에 대해서 얘기하고 있다면 인터페이스 어댑터에 정의될 수 있겠죠.

엉클 밥은 인터페이스 어댑터에 Presenter, View, Controller 가 모두 포함된다고 The Clean Architecture 책에도 명시를 했다. 클린 아키텍처 레이어 구조를 보면 인터페이스 어댑터의 밖에 UI 레이어가 존재하게 그려져있다. 나름 해석해 보자면 여기에 보여지는 UI 레이어는 프레임워크이고, 인터페이스 어댑터는 이 프레임워크를 기반으로 직접 만든 코드(Presenter, View, Contgroller 모두 직접 만든 것들)에 해당하기 때문에 이 레이어에 두는 것이 아닐까? 실제 UI 프레임워와 도메인 영역을 연결하는 역할을 한다는 의미로. 맞는지 아닌지 모르겠지만 … 난 그냥 이렇게 이해할래요. ㅎ

Use Cases. Interactors

이제 뭔가 흥미로워 지기 시작해요. Use Case 레이어는 이미 논의한 레이어들과는 달리 이해가 쉽지 않을 수 있어요. 그래서 이 레이어의 목적에 대해서 많은 오해가 있지요.

Use Case 는 목표를 완료하는 데 필요한 역할과 자동화 시스템 간의 작업 및 통신 단계 목록이에요. 이러한 정의는 살펴봐야할 중요한 몇 가지 포인트를 포함하죠. 사용자는 Use Case 꾸러미를 통해 SW 시스템의 기능적인 측면을 봐요.

대부분의 사람들이 Use Case 다이어그램을 알거라고 생각해요. 이 다이어그램은 SW 시스템을 설계하는 첫 번째 단계에서 사용되거든요. 가능한 시스템 동작을 설명하는 UseCase 가 더 많이 정의될 수록 알맞는 아키텍처와 개발 프로세스 전략과 방법론을 선택하는 것이 더 쉬워져요.

우리가 가장 흥미로워하는 기술적인 관점에서 Use Case 를 보면 Use Case 는 주로 여러 엔티티를 사용하는 것 뿐이에요. 엔티티는 나중에 논의하겠지만 여기에서 엔티티는 핵심 비지니스 룰을 포함하고 비지니스 룰에 따라 다른 Use Case 와의 조화를 이루어야 한다는 거에요. 여기에 Interactor 라고 불리는 용어가 있어요. Use Case 와 Interactor 는 서로 호환 가능한 용어에요. 제 생각에는 Interactor 객체가 시스템의 Use Case 를 구현한다고 말하는 것이 더 나은 것 같아요.

이 레이어의 SW 는 어플리케이션 관련 비지니스 룰을 가지고 있어요. 그리고 시스템의 모든 Use Case 를 캡슐화하고 구현하죠.

여기에서 어플리케이션 특정 (application specific) 이라는 것은 무슨 의미일까요? 어플리케이션 관련 규칙은 어플리케이션의 요구 사항이 변경됨에 따라 바뀔 수 있어요. 이 경우 어플리케이션은 자동화 시스템이에요. 예를 들어 은행을 코어 비니지스 규칙의 집합으로 간주해 봐요. ATM 어플리케이션은 자체 규칙을 가지고, 개인 계좌 관리를 위한 웹 시스템도 자체 규칙을 가져요. 그리고 모바일 어플리케이션도 자체 규칙을 가지겠죠. 그래서 이런 규칙들이 어플리케이션 특정이라고 하는 이유에요.

Use Case 에 대한 아주 좋은 글을 찾았는데 ATM 에서 돈을 뽑는 Use Case 에 관한 거에요.

어플리케이션 특정 규칙과 핵심 비지니즈 규칙을 구분하는 아주 좋은 예라고 생각해요. 엔티티 레이어에 속하는 핵심 비지니스 규칙은 위 UseCase 시나리오에서 두꺼운 글씨로 표시되어 있어요. 개인적으로 더 많은 단계를 비지니스 규칙에 추가하고 싶지만 아이디어는 명확해야 하니 그렇게 하지는 않겠어요!

보통 UseCase 는 입력 데이터와 UseCase 가 완료되었을 때 전달되는 출력 데이터가 있어요. 그 결과는 데이터를 직접적으로 반환하는 것 뿐만이아니라 콜백 함수 또는 단지 성공 여부만을 전달하는 것도 포함해요. 그럼 UseCase 를 살짝 변경해서 입력 데이터와 출력 데이터를 표시해 볼게요.

보시다시피 UseCase 명세에 입력과 출력 데이터를 추가했어요. 사용자 관점에서 보다 구체화 되었지만, (예를 들면) Interactor 구현 클래스의 실행 메소드의 인자나 리턴 값은 아니에요.

보통 UseCase 는 개별적인 작업으로 원자성을 보여요. 때때로 개발자들이 복수의 연관된 UseCase 를 하나의 Interactor 또는 UseCase 클래스에 섞기도 하지만요 (저도 여기서 그랬어요). 이건 Boilerplaate 코드를 없애기 위해서 그랬어요. 때문에 이 코드는 단일 책임의 원칙과 관심의 분리 원칙을 위배했죠. 개인적으로 UseCase 를 개별적으로 유지하는 것을 선호해요.

개발 중인 시스템의 UseCase 다이어그램을 만드는 것이 왜 중요할까요? 새로운 개발자가 참여해서 숙련된 프로젝트 멤버에게 어떤 비지니스 규칙과 UseCase 가 있는지 질문하는 걸 본적있었나요? 숙련된 멤버 조차 간단하지 않은 UseCase 와 정책으로 인해 버그를 수정하거나 코드를 변경할 때 망설일 수 있어요.

이 모든 문제점들을 고려하면 사용자가 예상하는 모든 UseCase 를 문서화하는 것을 제안해요. 먼저 가장 중요한 것은 고객이 이해하고, 논의하고 변경할 수 있는 비지니스 규칙을 수집하는 거에요. 그다음은 모든 가능한 시스템 UseCase 와 비지니스 규칙을 포함하는 단일 데이터 저장소를 만들어 새로운 멤버 (개발자와 이해관계자) 가 다른 멤버로부터 정보를 얻는 것보다 적은 시간을 사용해 이를 파악할 수 있게 해야 해요. 이렇게 하면 코드에 뛰어들지 않고 시스템의 향후 개발을 설계하는 것이 훨씬 쉬워질 거에요.

UseCase 는 다이그램, 시나리오 (위 예시 처럼), 단계와 조건, 프로젝트, 팀과 이해 관계자에 편리한 모든 형태로 문서화할 수 있어요.

Entities. Domain.

아키텍처 관점에서 마지막이자 가장 중요한 레이어는 엔티티 또는 도메인 레이어에요. 엔티티는 핵심 비니지스 규칙을 캡슐화해요. 핵심 비지니스 규칙은 비지니스의 존재에 필수지만, SW 시스템에 필수는 아니에요.

아키텍처 관점에서 마지막이자 가장 중요한 레이어는 엔티티 또는 도메인 레이어에요. 엔티티는 핵심 비니지스 규칙을 캡슐화해요. 핵심 비지니스 규칙은 비지니스의 존재에 필수지만, SW 시스템에 필수는 아니에요.

외부 변경은 이 레이어의 동작에 그 어떤 영향을 주면 안되요. SW 시스템 또는 어플리케이션이 없어도 타당한 비지니스 규칙을 구현하는 엔티티를 만들 수 있어요.

기술적인 관점에서 엔티티는 단지 데이터 만이 아니라 메소드와 로직을 포함하는 객체에요. 엔티티는 DTO (Data Transfer Object) 와 DAO (Data Access Object) 가 아니에요.

엔티티 레이어를 설계하는 것은 쉽지 않아요. 엔터프라이즈 어플리케이션을 개발하는 경우 이 레이어를 설계하는 데 많은 시간을 사용하고도 정말 심각한 결과를 초래하는 오류가 발생할 수 있어요. 도메인 주도 설계라는 매우 가치있는 규칙이 있어요. 이 개념들은 복잡한 비지니스 규칙을 가지는 규모가 큰 엔터프라이즈 어플리케이션 개발을 용이하게 하는 걸 목표로 해요. 엔터프라이즈 어플리케이션을 개발하지 않는 경우에도 이런 개념은 여전히 주목할 가치가 있어요. 그러니 도메인 주도 설계 방법을 공부하는 것을 강력히 추천해요.

엔터프라이즈 어플리케이션에 포함되지 않는 아주 간단한 어플리케이션의 경우는 어떻게할까요? 엔티티 레이어는 상위 레이어의 변경에 영향을 받지 않아야하는 가장 일반적이고 고수준의 규칙을 포함해야 해요.

엔티티는 자신의 상태를 데이터베이스에 저장하지 않아야해요. 많은 ORM 라이브러리와 프레임워크가 엔티티를 데이터베이스에 바로 매핑하는데 이런 접근은 종속성 규칙을 위반하는 거에요. 예를 들어 기본 엔티티 클래스가 어떤 데이터베이스 모델 클래스를 확장(상속)하면 데이터베이스 및 저수준 세부 사항과 강한 결합을 갖게 되는거에요. 일부 프레임워크는 이 문제를 해결하기 위해 IoC (Inversion of Control) 를 사용하거나 바이트코드 위빙(weaving) 을 사용해요. 일반적으로 말하면 엔티티는 데이터베이스의 존재 조차 몰라야 해요.

Entities vs Use Cases

한 가지 흥미로운 질문은 Use Case 와 엔티티 레이어의 차이에요. 언제 어떤 걸 사용해야 하는 거죠? 아래는 제가 이해하는 수준에서 두 레이어를 비교한 내용이에요.

  1. Use Case 는 어플리케이션 특정 규칙을 가져요. 반면에 엔티티는 비지니스 규칙을 가지죠.
  2. Use Case 는 보통 어플리케이션 없이는 존재할 수가 없어요. 반면에 엔티티는 규칙과 정책으로써 어떤 자동화 시스템 없이도 존재할 수 있어요.
  3. 엔티티는 거의 변경되지 않고 외부 레이어의 변경에 의한 영향을 받지 않는 규칙을 포함해요.
  4. Use Case 는 엔티티의 흐름과 동작을 조율해요. 엔티티나 도메인 레이어에 필요한 모든 데이터는 대개 Use Case 나 Gateway 를 통해 전달되요.
  5. 어플리케이션의 요구 사항이 변경되면 도메인 레이어 (엔티티) 의 어떤 변경도 있어서는 안되는 것이 최선이지만 대부분의 변경은 Use Case 나 세부 사항과 관련된 레이어에서 발생해야해요. 하지만 일부 코어 비지니스 규칙이 변경된다면 이 변경 사항은 엔티티 레이어에 반영이 되어야 해요.

Conclusion

이 글을 통해 클린 아키텍처 원칙의 이해와 적용에 대한 경험을 전달하려고 했어요. 기술된 모든 규칙과 개념을 정확하게 사용한다면 정말로 가치가 있을 거에요. 그렇지 않으면 이전보다 더한 스파게티와 질흙덩이 같은 코드를 만들 수 있어요. 따라서 초보 개발자라면 이 규칙을 적용하기 전에 충분한 경험을 하는 것이 중요해요.

이 규칙을 무조건적으로 따라야 하는 것은 아니에요. 클린 아키텍처는 단지 SW의 모양에 맞는 결정 사항과 설계 단계에 대한 아이디어를 제공하는 실천법이라고 생각하면 될 것 같아요. 모든 프로젝트는 많은 요소를 바탕으로 아키텍트를 통해 만들어지는 고유의 접근 방법이 필요해요.

이 글과 관련된 질문이 있으면 언제든지 연락하거나 아래 코멘트를 달아 주세요. 토론은 언제나 합의에 이르는 가장 좋은 방법이거든요.

--

--

JustWrite
JustWrite

Written by JustWrite

Just write and see and make it more readable.

Responses (2)