# 만들면서 배우는 클린 아키텍쳐

## 계층형 아키텍처의 문제점

* 웹 → 도메인 → 영속성 으로 구성된 전통적인 웹 애플리케이션 구조
  * 웹 계층에서 요청을 받아 도메인 or 비지니스 계층의 서비스로 요청을 보내면
  * 서비스에서 필요한 비지니스 로직을 수행하고
  * 도메인 엔티티의 현재 상태를 조회하거나, 변경하기 위해 영속성 계층의 컴포넌트를 호출한다
* **계층형 아키텍쳐의 단점**
  1. **계층형 아키텍쳐는 데이터베이스 주도 설계를 유도하게 된다.**
     * 점점 하위 레이어에 의존하기 때문에 결국 영속성 계층인 데이터베이스의 상태에 의존하게 된다.
     * 만약 도메인 계층과 영속성 계층 사이에 강한 결합이 생기면, 서비스가 영속성 모델을 비지니스 모델 처럼 사용하게 되고, 이로 인해 영속성 관련 작업들(eager, lazy loading, 트랜잭션, 캐시 flush…)을 서비스에서 해야된다.
  2. **개발에 편리하다는 이유로 특정 컴포넌트들이(헬퍼, 유틸리티 등) 영속성 계층에 쌓이는 경우가 생긴다**
  3. **웹 계층에 도메인 계층을 건너뛰어서 영속성계층에 바로 접근하는 코드가 생긴다면 도메인 로직이 점점 흩어진다.**
     * 이 경우 웹 계층 테스트를 하는데 영속성 계층도 모킹해서 테스트해야되서, 유닛 테스트의 복잡도가 올라간다. 이렇게 되면 종속성을 이해하고 mocking을 하는데 많은 시간이 소요되서 테스트 자체를 회피하게 된다.
  4. **유스케이스를 숨기게 된다.**
     * 모든 계층에 유스케이스가 흩어져서 기능 추가나 변경을 위한 코드를 탐색하기 힘들어진다.
     * 하나의 서비스가 여러 개의 유스케이스를 담당하는 등 아주 큰 서비스가 만들어진다.

       * 넓은 서비스는 영속성 계층에 많은 의존성을 가지게 되고, 웹 계층의 많은 컴포넌트가 이 서비스에 의존하게 된다. 그렇다는 것은 서비스를 테스트하기 힘들어지고, 특정 기능을 리팩토링할 때 그 기능에 해당하는 코드를 찾기 힘들어진다.

       ⇒ UserService 에서 사용자 등록 유스케이스를 찾는 대신 RegisterUserService 가 있다면 그 서비스클래스만 바로 열어서 작업할 수 있다.
  5. **동시 작업이 어려워진다.**
     * 레이어드 아키텍쳐 상에서는 모든 것이 영속성 계층 위에 만들어지기 때문에 계층별로 순차적으로 개발을 해야되서 하나의 기능을 한 명이 개발자만 작업할 수 있다.
       * 인터페이스를 미리 정의해 놓는다고 해도, 데이터베이스 주도 설계는 영속성 로직이 도메인 로직과 너무 뒤섞여서 각각을 개별적으로 작업하기가 힘들다.
     * 코드에 넓은 범위의 서비스 클래스가 있다면 서로 다른 기능을 동시에 작업하기가 어렵다.
* 애플리케이션은 비지니스를 관장하는 규칙, 정책에 기반한 모델(도메인 로직)을 만들어야 편리하게 사용할 수 있다. state가 아닌 behavior를 중심으로 모델링하는 것이 더 유연할 수 있다.
  * 이 경우 도메인 로직을 먼저 만들고, 이 로직이 맞는 것을 확인한 뒤에 영속성 계층과 웹 계층을 만들어야한다.

## 의존성 역전하기

객체지향의 SOLID 원칙 중 S, D를 적극적으로 지향

* **SRP - 단일 책임 원칙**
  * ~~하나의 컴포넌트는 한 가지 일만 해야한다~~
  * **컴포넌트를 변경하는 이유는 오직 하나 뿐이여야만 한다**
    * 만약 컴포넌트를 변경하는 이유가 단 한 가지 라면 우리가 다른 이유로 소프트웨어를 변경할 때 이 컴퓨넌트에 대해서는 신경쓸 필요가 없어진다.
    * 현실적으로 변경할 이유는 컴포넌트 간 의존성을 통해 쉽게 전파된다.
    * 단일 책임 원칙을 위반한 컴포넌트는 다른 컴포넌트에 의존하기 떄문에 다른 컴포넌트 변경 시 같이 변경해야되서 비용이 증가한다.
* 부수효과
  * 잘 설계된 컴포넌트는 기능을 새로 구현하거나 변경시에 부수효과가 없어진다.
* DIP - 의존성 역전 원칙
  * 레이어드 아키텍쳐에서 계층 간 의존성은 항상 다음 계층인 아래 방향을 가리킨다.
    * 이 경우 고수준의 컴포넌트에 SRP를 적용하면 저수준의 컴포넌트에 비해 변경할 이유가 더 많아진다.
    * **영속성 계층에 대한 도메인 계층의 의존성 때문에 영속성 계층을 변경할 때마다 도메인 계층도 변경해야한다.**
  * **코드상의 어떠한 의존성이든 그 방향을 바꿀 수 있다(역전시킬 수 있다)**
    * 양측 컴포넌트를 모두 제어할 수 있을 때만 의존성을 역전시킬 수 있다.
    * 만약 서드파티 라이브러리에 의존성이 있다면, 해당 라이브러리를 제어할 수 없기 때문에 이 의존성은 역전시킬 수 없다
  * **영속성 계층의 레포지토리가 도메인 계층에 있는 엔티티에 의존하게 만들 수 있다.**
    * 도메인코드와 영속성 코드 간의 의존성을 역전시켜 영속성 코드가 도메인 코드에 의존하고, 도메인 코드의 변경할 이유를 줄여보자
    * 영속성 계층의 리포지토리가 도메인 계층에 있는 엔티티에 의존하도록 만든 뒤에 도메인 계층에 리포지토리에 대한 인터페이스를 만들고, 실제 리포지토리는 영속성 계층에서 구현하게 하면 된다.
    * ㅑ

      <figure><img src="https://584269691-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIa46FQu86l5O8OVNei87%2Fuploads%2F63aojDF0IragIzkEarNc%2FScreenshot%202023-03-17%20at%203.15.46%20PM.png?alt=media&#x26;token=fbc983bf-4a57-4c3e-a6e3-7a873901393d" alt=""><figcaption></figcaption></figure>
*
* 클린 아키텍쳐
  \*

  ```
  * 애플리케이션이 다른 시스템이나 어댑터에 연결되는 4개 이상의 면(port)을 가질 수 있음을 보여주려고 육각형이라고 이름 붙였다고 함
  * 육각형 안에는 도메인 엔티티와 상호작용하는 유스케이스가 있음. 모든 의존성은 코어를 향한다
  * 어댑터 - 포트 - 유즈케이스 - 엔티티

  <figure><img src="https://584269691-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIa46FQu86l5O8OVNei87%2Fuploads%2FAhZqthrVqsSqj4xqDUuy%2Fthe-clean-architecture%20(1).png?alt=media&#x26;token=0c739248-030c-4761-a4fb-6f01ef809dcf" alt=""><figcaption></figcaption></figure>
  ```

  * 클린 아키텍쳐에서 모든 의존성은 도메인 로직을 향해 안으로 향한다.
    * 코어는 주변 유스케이스에서 접근하는 도메인 엔티티
    * 유스케이스(단일 책임으로 잘게 분리된 서비스)
    * 비지니스 규칙을 지원하는 나머지 컴포넌트(영속성, UI, 서드파티 컴포넌트에 대한 어댑터…)
  * 클린 아키텍쳐를 사용했을 때의 대가
    * 도메인 계층이 영속성, UI 등의 외부 계층과 철저히 분리해야한다는 것은 **애플리케이션 엔티티에 대한 모델을 각 계층에서 유지보수해야한다.**
* 헥사고날 아키텍쳐

<figure><img src="https://584269691-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIa46FQu86l5O8OVNei87%2Fuploads%2FbyBKY2P1PHgt7zZ5FbEk%2Fhexagonal-architecture_hu6764515d7030d45af6f7f498c79e292b_50897_956x0_resize_box_3.png?alt=media&#x26;token=929988fc-564c-4b0c-bd89-0c43d634e49c" alt=""><figcaption></figcaption></figure>

* 애플리케이션이 다른 시스템이나 어댑터에 연결되는 4개 이상의 면(port)을 가질 수 있음을 보여주려고 육각형이라고 이름 붙였다고 함
* 육각형 안에는 도메인 엔티티와 상호작용하는 유스케이스가 있음. 모든 의존성은 코어를 향한다
* 어댑터 - 포트 - 유즈케이스 - 엔티티
